import { webpack, StringXor } from "next/dist/compiled/webpack/webpack";
import { getOverlayMiddleware } from "next/dist/compiled/@next/react-dev-overlay/dist/middleware";
import { WebpackHotMiddleware } from "./hot-middleware";
import { join, relative, isAbsolute, posix } from "path";
import { createEntrypoints, createPagesMapping, finalizeEntrypoint, getClientEntry, getEdgeServerEntry, getAppEntry, runDependingOnPageType, getStaticInfoIncludingLayouts } from "../../build/entries";
import { watchCompilers } from "../../build/output";
import * as Log from "../../build/output/log";
import getBaseWebpackConfig, { loadProjectInfo } from "../../build/webpack-config";
import { APP_DIR_ALIAS, WEBPACK_LAYERS } from "../../lib/constants";
import { recursiveDelete } from "../../lib/recursive-delete";
import { BLOCKED_PAGES, CLIENT_STATIC_FILES_RUNTIME_AMP, CLIENT_STATIC_FILES_RUNTIME_MAIN, CLIENT_STATIC_FILES_RUNTIME_MAIN_APP, CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH, COMPILER_NAMES, RSC_MODULE_TYPES } from "../../shared/lib/constants";
import { getPathMatch } from "../../shared/lib/router/utils/path-match";
import { findPageFile } from "../lib/find-page-file";
import { BUILDING, getEntries, EntryTypes, getInvalidator, onDemandEntryHandler } from "./on-demand-entry-handler";
import { denormalizePagePath } from "../../shared/lib/page-path/denormalize-page-path";
import { normalizePathSep } from "../../shared/lib/page-path/normalize-path-sep";
import getRouteFromEntrypoint from "../get-route-from-entrypoint";
import { difference, isInstrumentationHookFile, isMiddlewareFile, isMiddlewareFilename } from "../../build/utils";
import { DecodeError } from "../../shared/lib/utils";
import { Span, trace } from "../../trace";
import { getProperError } from "../../lib/is-error";
import ws from "next/dist/compiled/ws";
import { existsSync, promises as fs } from "fs";
import { getRegistry } from "../../lib/helpers/get-registry";
import { parseVersionInfo } from "./parse-version-info";
import { isAPIRoute } from "../../lib/is-api-route";
import { getRouteLoaderEntry } from "../../build/webpack/loaders/next-route-loader";
import { isInternalComponent, isNonRoutePagesPage } from "../../lib/is-internal-component";
import { RouteKind } from "../future/route-kind";
import { HMR_ACTIONS_SENT_TO_BROWSER } from "./hot-reloader-types";
import { PAGE_TYPES } from "../../lib/page-types";
const MILLISECONDS_IN_NANOSECOND = 1000000;
function diff(a, b) {
    return new Set([
        ...a
    ].filter((v)=>!b.has(v)));
}
const wsServer = new ws.Server({
    noServer: true
});
export async function renderScriptError(res, error, { verbose = true } = {}) {
    // Asks CDNs and others to not to cache the errored page
    res.setHeader("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate");
    if (error.code === "ENOENT") {
        return {
            finished: undefined
        };
    }
    if (verbose) {
        console.error(error.stack);
    }
    res.statusCode = 500;
    res.end("500 - Internal Error");
    return {
        finished: true
    };
}
function addCorsSupport(req, res) {
    // Only rewrite CORS handling when URL matches a hot-reloader middleware
    if (!req.url.startsWith("/__next")) {
        return {
            preflight: false
        };
    }
    if (!req.headers.origin) {
        return {
            preflight: false
        };
    }
    res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
    res.setHeader("Access-Control-Allow-Methods", "OPTIONS, GET");
    // Based on https://github.com/primus/access-control/blob/4cf1bc0e54b086c91e6aa44fb14966fa5ef7549c/index.js#L158
    if (req.headers["access-control-request-headers"]) {
        res.setHeader("Access-Control-Allow-Headers", req.headers["access-control-request-headers"]);
    }
    if (req.method === "OPTIONS") {
        res.writeHead(200);
        res.end();
        return {
            preflight: true
        };
    }
    return {
        preflight: false
    };
}
export const matchNextPageBundleRequest = getPathMatch("/_next/static/chunks/pages/:path*.js(\\.map|)");
// Iteratively look up the issuer till it ends up at the root
function findEntryModule(module, compilation) {
    for(;;){
        const issuer = compilation.moduleGraph.getIssuer(module);
        if (!issuer) return module;
        module = issuer;
    }
}
function erroredPages(compilation) {
    const failedPages = {};
    for (const error of compilation.errors){
        if (!error.module) {
            continue;
        }
        const entryModule = findEntryModule(error.module, compilation);
        const { name } = entryModule;
        if (!name) {
            continue;
        }
        // Only pages have to be reloaded
        const enhancedName = getRouteFromEntrypoint(name);
        if (!enhancedName) {
            continue;
        }
        if (!failedPages[enhancedName]) {
            failedPages[enhancedName] = [];
        }
        failedPages[enhancedName].push(error);
    }
    return failedPages;
}
export default class HotReloaderWebpack {
    constructor(dir, { config, pagesDir, distDir, buildId, previewProps, rewrites, appDir, telemetry }){
        this.clientError = null;
        this.serverError = null;
        this.hmrServerError = null;
        this.pagesMapping = {};
        this.versionInfo = {
            staleness: "unknown",
            installed: "0.0.0"
        };
        this.reloadAfterInvalidation = false;
        this.hasAmpEntrypoints = false;
        this.hasAppRouterEntrypoints = false;
        this.hasPagesRouterEntrypoints = false;
        this.buildId = buildId;
        this.dir = dir;
        this.interceptors = [];
        this.pagesDir = pagesDir;
        this.appDir = appDir;
        this.distDir = distDir;
        this.clientStats = null;
        this.serverStats = null;
        this.edgeServerStats = null;
        this.serverPrevDocumentHash = null;
        this.telemetry = telemetry;
        this.config = config;
        this.previewProps = previewProps;
        this.rewrites = rewrites;
        this.hotReloaderSpan = trace("hot-reloader", undefined, {
            version: "14.1.0"
        });
        // Ensure the hotReloaderSpan is flushed immediately as it's the parentSpan for all processing
        // of the current `next dev` invocation.
        this.hotReloaderSpan.stop();
    }
    async run(req, res, parsedUrl) {
        // Usually CORS support is not needed for the hot-reloader (this is dev only feature)
        // With when the app runs for multi-zones support behind a proxy,
        // the current page is trying to access this URL via assetPrefix.
        // That's when the CORS support is needed.
        const { preflight } = addCorsSupport(req, res);
        if (preflight) {
            return {};
        }
        // When a request comes in that is a page bundle, e.g. /_next/static/<buildid>/pages/index.js
        // we have to compile the page using on-demand-entries, this middleware will handle doing that
        // by adding the page to on-demand-entries, waiting till it's done
        // and then the bundle will be served like usual by the actual route in server/index.js
        const handlePageBundleRequest = async (pageBundleRes, parsedPageBundleUrl)=>{
            const { pathname } = parsedPageBundleUrl;
            const params = matchNextPageBundleRequest(pathname);
            if (!params) {
                return {};
            }
            let decodedPagePath;
            try {
                decodedPagePath = `/${params.path.map((param)=>decodeURIComponent(param)).join("/")}`;
            } catch (_) {
                throw new DecodeError("failed to decode param");
            }
            const page = denormalizePagePath(decodedPagePath);
            if (page === "/_error" || BLOCKED_PAGES.indexOf(page) === -1) {
                try {
                    await this.ensurePage({
                        page,
                        clientOnly: true,
                        url: req.url
                    });
                } catch (error) {
                    return await renderScriptError(pageBundleRes, getProperError(error));
                }
                const errors = await this.getCompilationErrors(page);
                if (errors.length > 0) {
                    return await renderScriptError(pageBundleRes, errors[0], {
                        verbose: false
                    });
                }
            }
            return {};
        };
        const { finished } = await handlePageBundleRequest(res, parsedUrl);
        for (const fn of this.interceptors){
            await new Promise((resolve, reject)=>{
                fn(req, res, (err)=>{
                    if (err) return reject(err);
                    resolve();
                });
            });
        }
        return {
            finished
        };
    }
    setHmrServerError(error) {
        this.hmrServerError = error;
    }
    clearHmrServerError() {
        if (this.hmrServerError) {
            this.setHmrServerError(null);
            this.send({
                action: HMR_ACTIONS_SENT_TO_BROWSER.RELOAD_PAGE
            });
        }
    }
    async refreshServerComponents() {
        this.send({
            action: HMR_ACTIONS_SENT_TO_BROWSER.SERVER_COMPONENT_CHANGES
        });
    }
    onHMR(req, _socket, head) {
        wsServer.handleUpgrade(req, req.socket, head, (client)=>{
            var _this_webpackHotMiddleware, _this_onDemandEntries;
            (_this_webpackHotMiddleware = this.webpackHotMiddleware) == null ? void 0 : _this_webpackHotMiddleware.onHMR(client);
            (_this_onDemandEntries = this.onDemandEntries) == null ? void 0 : _this_onDemandEntries.onHMR(client, ()=>this.hmrServerError);
            client.addEventListener("message", ({ data })=>{
                data = typeof data !== "string" ? data.toString() : data;
                try {
                    const payload = JSON.parse(data);
                    let traceChild;
                    switch(payload.event){
                        case "span-end":
                            {
                                new Span({
                                    name: payload.spanName,
                                    startTime: BigInt(Math.floor(payload.startTime)) * BigInt(MILLISECONDS_IN_NANOSECOND),
                                    attrs: payload.attributes
                                }).stop(BigInt(Math.floor(payload.endTime)) * BigInt(MILLISECONDS_IN_NANOSECOND));
                                break;
                            }
                        case "client-hmr-latency":
                            {
                                traceChild = {
                                    name: payload.event,
                                    startTime: BigInt(payload.startTime) * BigInt(MILLISECONDS_IN_NANOSECOND),
                                    endTime: BigInt(payload.endTime) * BigInt(MILLISECONDS_IN_NANOSECOND),
                                    attrs: {
                                        updatedModules: payload.updatedModules.map((m)=>m.replace(/^\.\//, "[project]/")),
                                        page: payload.page,
                                        isPageHidden: payload.isPageHidden
                                    }
                                };
                                break;
                            }
                        case "client-reload-page":
                        case "client-success":
                            {
                                traceChild = {
                                    name: payload.event
                                };
                                break;
                            }
                        case "client-error":
                            {
                                traceChild = {
                                    name: payload.event,
                                    attrs: {
                                        errorCount: payload.errorCount
                                    }
                                };
                                break;
                            }
                        case "client-warning":
                            {
                                traceChild = {
                                    name: payload.event,
                                    attrs: {
                                        warningCount: payload.warningCount
                                    }
                                };
                                break;
                            }
                        case "client-removed-page":
                        case "client-added-page":
                            {
                                traceChild = {
                                    name: payload.event,
                                    attrs: {
                                        page: payload.page || ""
                                    }
                                };
                                break;
                            }
                        case "client-full-reload":
                            {
                                const { event, stackTrace, hadRuntimeError } = payload;
                                traceChild = {
                                    name: event,
                                    attrs: {
                                        stackTrace: stackTrace ?? ""
                                    }
                                };
                                if (hadRuntimeError) {
                                    Log.warn(`Fast Refresh had to perform a full reload due to a runtime error.`);
                                    break;
                                }
                                let fileMessage = "";
                                if (stackTrace) {
                                    var _exec;
                                    const file = (_exec = /Aborted because (.+) is not accepted/.exec(stackTrace)) == null ? void 0 : _exec[1];
                                    if (file) {
                                        // `file` is filepath in `pages/` but it can be weird long webpack url in `app/`.
                                        // If it's a webpack loader URL, it will start with '(app-pages)/./'
                                        if (file.startsWith(`(${WEBPACK_LAYERS.appPagesBrowser})/./`)) {
                                            const fileUrl = new URL(file, "file://");
                                            const cwd = process.cwd();
                                            const modules = fileUrl.searchParams.getAll("modules").map((filepath)=>filepath.slice(cwd.length + 1)).filter((filepath)=>!filepath.startsWith("node_modules"));
                                            if (modules.length > 0) {
                                                fileMessage = ` when ${modules.join(", ")} changed`;
                                            }
                                        } else {
                                            fileMessage = ` when ${file} changed`;
                                        }
                                    }
                                }
                                Log.warn(`Fast Refresh had to perform a full reload${fileMessage}. Read more: https://nextjs.org/docs/messages/fast-refresh-reload`);
                                break;
                            }
                        default:
                            {
                                break;
                            }
                    }
                    if (traceChild) {
                        this.hotReloaderSpan.manualTraceChild(traceChild.name, traceChild.startTime || process.hrtime.bigint(), traceChild.endTime || process.hrtime.bigint(), {
                            ...traceChild.attrs,
                            clientId: payload.id
                        });
                    }
                } catch (_) {
                // invalid WebSocket message
                }
            });
        });
    }
    async clean(span) {
        return span.traceChild("clean").traceAsyncFn(()=>recursiveDelete(join(this.dir, this.config.distDir), /^cache/));
    }
    async getVersionInfo(span, enabled) {
        const versionInfoSpan = span.traceChild("get-version-info");
        return versionInfoSpan.traceAsyncFn(async ()=>{
            let installed = "0.0.0";
            if (!enabled) {
                return {
                    installed,
                    staleness: "unknown"
                };
            }
            try {
                installed = require("next/package.json").version;
                const registry = getRegistry();
                const res = await fetch(`${registry}-/package/next/dist-tags`);
                if (!res.ok) return {
                    installed,
                    staleness: "unknown"
                };
                const tags = await res.json();
                return parseVersionInfo({
                    installed,
                    latest: tags.latest,
                    canary: tags.canary
                });
            } catch  {
                return {
                    installed,
                    staleness: "unknown"
                };
            }
        });
    }
    async getWebpackConfig(span) {
        const webpackConfigSpan = span.traceChild("get-webpack-config");
        const pageExtensions = this.config.pageExtensions;
        return webpackConfigSpan.traceAsyncFn(async ()=>{
            const pagePaths = !this.pagesDir ? [] : await webpackConfigSpan.traceChild("get-page-paths").traceAsyncFn(()=>Promise.all([
                    findPageFile(this.pagesDir, "/_app", pageExtensions, false),
                    findPageFile(this.pagesDir, "/_document", pageExtensions, false)
                ]));
            this.pagesMapping = webpackConfigSpan.traceChild("create-pages-mapping").traceFn(()=>createPagesMapping({
                    isDev: true,
                    pageExtensions: this.config.pageExtensions,
                    pagesType: PAGE_TYPES.PAGES,
                    pagePaths: pagePaths.filter((i)=>typeof i === "string"),
                    pagesDir: this.pagesDir
                }));
            const entrypoints = await webpackConfigSpan.traceChild("create-entrypoints").traceAsyncFn(()=>createEntrypoints({
                    appDir: this.appDir,
                    buildId: this.buildId,
                    config: this.config,
                    envFiles: [],
                    isDev: true,
                    pages: this.pagesMapping,
                    pagesDir: this.pagesDir,
                    previewMode: this.previewProps,
                    rootDir: this.dir,
                    pageExtensions: this.config.pageExtensions
                }));
            const commonWebpackOptions = {
                dev: true,
                buildId: this.buildId,
                config: this.config,
                pagesDir: this.pagesDir,
                rewrites: this.rewrites,
                originalRewrites: this.config._originalRewrites,
                originalRedirects: this.config._originalRedirects,
                runWebpackSpan: this.hotReloaderSpan,
                appDir: this.appDir
            };
            return webpackConfigSpan.traceChild("generate-webpack-config").traceAsyncFn(async ()=>{
                const info = await loadProjectInfo({
                    dir: this.dir,
                    config: commonWebpackOptions.config,
                    dev: true
                });
                return Promise.all([
                    // order is important here
                    getBaseWebpackConfig(this.dir, {
                        ...commonWebpackOptions,
                        compilerType: COMPILER_NAMES.client,
                        entrypoints: entrypoints.client,
                        ...info
                    }),
                    getBaseWebpackConfig(this.dir, {
                        ...commonWebpackOptions,
                        compilerType: COMPILER_NAMES.server,
                        entrypoints: entrypoints.server,
                        ...info
                    }),
                    getBaseWebpackConfig(this.dir, {
                        ...commonWebpackOptions,
                        compilerType: COMPILER_NAMES.edgeServer,
                        entrypoints: entrypoints.edgeServer,
                        ...info
                    })
                ]);
            });
        });
    }
    async buildFallbackError() {
        if (this.fallbackWatcher) return;
        const info = await loadProjectInfo({
            dir: this.dir,
            config: this.config,
            dev: true
        });
        const fallbackConfig = await getBaseWebpackConfig(this.dir, {
            runWebpackSpan: this.hotReloaderSpan,
            dev: true,
            compilerType: COMPILER_NAMES.client,
            config: this.config,
            buildId: this.buildId,
            pagesDir: this.pagesDir,
            rewrites: {
                beforeFiles: [],
                afterFiles: [],
                fallback: []
            },
            originalRewrites: {
                beforeFiles: [],
                afterFiles: [],
                fallback: []
            },
            originalRedirects: [],
            isDevFallback: true,
            entrypoints: (await createEntrypoints({
                appDir: this.appDir,
                buildId: this.buildId,
                config: this.config,
                envFiles: [],
                isDev: true,
                pages: {
                    "/_app": "next/dist/pages/_app",
                    "/_error": "next/dist/pages/_error"
                },
                pagesDir: this.pagesDir,
                previewMode: this.previewProps,
                rootDir: this.dir,
                pageExtensions: this.config.pageExtensions
            })).client,
            ...info
        });
        const fallbackCompiler = webpack(fallbackConfig);
        this.fallbackWatcher = await new Promise((resolve)=>{
            let bootedFallbackCompiler = false;
            fallbackCompiler.watch(// @ts-ignore webpack supports an array of watchOptions when using a multiCompiler
            fallbackConfig.watchOptions, // Errors are handled separately
            (_err)=>{
                if (!bootedFallbackCompiler) {
                    bootedFallbackCompiler = true;
                    resolve(true);
                }
            });
        });
    }
    async start() {
        const startSpan = this.hotReloaderSpan.traceChild("start");
        startSpan.stop() // Stop immediately to create an artificial parent span
        ;
        const testMode = process.env.NEXT_TEST_MODE || process.env.__NEXT_TEST_MODE;
        this.versionInfo = await this.getVersionInfo(startSpan, !!testMode || this.telemetry.isEnabled);
        await this.clean(startSpan);
        // Ensure distDir exists before writing package.json
        await fs.mkdir(this.distDir, {
            recursive: true
        });
        const distPackageJsonPath = join(this.distDir, "package.json");
        // Ensure commonjs handling is used for files in the distDir (generally .next)
        // Files outside of the distDir can be "type": "module"
        await fs.writeFile(distPackageJsonPath, '{"type": "commonjs"}');
        this.activeWebpackConfigs = await this.getWebpackConfig(startSpan);
        for (const config of this.activeWebpackConfigs){
            const defaultEntry = config.entry;
            config.entry = async (...args)=>{
                var _this_multiCompiler;
                const outputPath = ((_this_multiCompiler = this.multiCompiler) == null ? void 0 : _this_multiCompiler.outputPath) || "";
                const entries = getEntries(outputPath);
                // @ts-ignore entry is always a function
                const entrypoints = await defaultEntry(...args);
                const isClientCompilation = config.name === COMPILER_NAMES.client;
                const isNodeServerCompilation = config.name === COMPILER_NAMES.server;
                const isEdgeServerCompilation = config.name === COMPILER_NAMES.edgeServer;
                await Promise.all(Object.keys(entries).map(async (entryKey)=>{
                    const entryData = entries[entryKey];
                    const { bundlePath, dispose } = entryData;
                    const result = /^(client|server|edge-server)@(app|pages|root)@(.*)/g.exec(entryKey);
                    const [, key /* pageType */ , , page] = result// this match should always happen
                    ;
                    if (key === COMPILER_NAMES.client && !isClientCompilation) return;
                    if (key === COMPILER_NAMES.server && !isNodeServerCompilation) return;
                    if (key === COMPILER_NAMES.edgeServer && !isEdgeServerCompilation) return;
                    const isEntry = entryData.type === EntryTypes.ENTRY;
                    const isChildEntry = entryData.type === EntryTypes.CHILD_ENTRY;
                    // Check if the page was removed or disposed and remove it
                    if (isEntry) {
                        const pageExists = !dispose && existsSync(entryData.absolutePagePath);
                        if (!pageExists) {
                            delete entries[entryKey];
                            return;
                        }
                    }
                    // For child entries, if it has an entry file and it's gone, remove it
                    if (isChildEntry) {
                        if (entryData.absoluteEntryFilePath) {
                            const pageExists = !dispose && existsSync(entryData.absoluteEntryFilePath);
                            if (!pageExists) {
                                delete entries[entryKey];
                                return;
                            }
                        }
                    }
                    // Ensure _error is considered a `pages` page.
                    if (page === "/_error") {
                        this.hasPagesRouterEntrypoints = true;
                    }
                    const hasAppDir = !!this.appDir;
                    const isAppPath = hasAppDir && bundlePath.startsWith("app/");
                    const staticInfo = isEntry ? await getStaticInfoIncludingLayouts({
                        isInsideAppDir: isAppPath,
                        pageExtensions: this.config.pageExtensions,
                        pageFilePath: entryData.absolutePagePath,
                        appDir: this.appDir,
                        config: this.config,
                        isDev: true,
                        page
                    }) : {};
                    if (staticInfo.amp === true || staticInfo.amp === "hybrid") {
                        this.hasAmpEntrypoints = true;
                    }
                    const isServerComponent = isAppPath && staticInfo.rsc !== RSC_MODULE_TYPES.client;
                    const pageType = entryData.bundlePath.startsWith("pages/") ? PAGE_TYPES.PAGES : entryData.bundlePath.startsWith("app/") ? PAGE_TYPES.APP : PAGE_TYPES.ROOT;
                    if (pageType === "pages") {
                        this.hasPagesRouterEntrypoints = true;
                    }
                    if (pageType === "app") {
                        this.hasAppRouterEntrypoints = true;
                    }
                    runDependingOnPageType({
                        page,
                        pageRuntime: staticInfo.runtime,
                        pageType,
                        onEdgeServer: ()=>{
                            // TODO-APP: verify if child entry should support.
                            if (!isEdgeServerCompilation || !isEntry) return;
                            const appDirLoader = isAppPath ? getAppEntry({
                                name: bundlePath,
                                page,
                                appPaths: entryData.appPaths,
                                pagePath: posix.join(APP_DIR_ALIAS, relative(this.appDir, entryData.absolutePagePath).replace(/\\/g, "/")),
                                appDir: this.appDir,
                                pageExtensions: this.config.pageExtensions,
                                rootDir: this.dir,
                                isDev: true,
                                tsconfigPath: this.config.typescript.tsconfigPath,
                                basePath: this.config.basePath,
                                assetPrefix: this.config.assetPrefix,
                                nextConfigOutput: this.config.output,
                                preferredRegion: staticInfo.preferredRegion,
                                middlewareConfig: Buffer.from(JSON.stringify(staticInfo.middleware || {})).toString("base64")
                            }).import : undefined;
                            entries[entryKey].status = BUILDING;
                            entrypoints[bundlePath] = finalizeEntrypoint({
                                compilerType: COMPILER_NAMES.edgeServer,
                                name: bundlePath,
                                value: getEdgeServerEntry({
                                    absolutePagePath: entryData.absolutePagePath,
                                    rootDir: this.dir,
                                    buildId: this.buildId,
                                    bundlePath,
                                    config: this.config,
                                    isDev: true,
                                    page,
                                    pages: this.pagesMapping,
                                    isServerComponent,
                                    appDirLoader,
                                    pagesType: isAppPath ? PAGE_TYPES.APP : PAGE_TYPES.PAGES,
                                    preferredRegion: staticInfo.preferredRegion
                                }),
                                hasAppDir
                            });
                        },
                        onClient: ()=>{
                            if (!isClientCompilation) return;
                            if (isChildEntry) {
                                entries[entryKey].status = BUILDING;
                                entrypoints[bundlePath] = finalizeEntrypoint({
                                    name: bundlePath,
                                    compilerType: COMPILER_NAMES.client,
                                    value: entryData.request,
                                    hasAppDir
                                });
                            } else {
                                entries[entryKey].status = BUILDING;
                                entrypoints[bundlePath] = finalizeEntrypoint({
                                    name: bundlePath,
                                    compilerType: COMPILER_NAMES.client,
                                    value: getClientEntry({
                                        absolutePagePath: entryData.absolutePagePath,
                                        page
                                    }),
                                    hasAppDir
                                });
                            }
                        },
                        onServer: ()=>{
                            // TODO-APP: verify if child entry should support.
                            if (!isNodeServerCompilation || !isEntry) return;
                            entries[entryKey].status = BUILDING;
                            let relativeRequest = relative(config.context, entryData.absolutePagePath);
                            if (!isAbsolute(relativeRequest) && !relativeRequest.startsWith("../")) {
                                relativeRequest = `./${relativeRequest}`;
                            }
                            let value;
                            if (isAppPath) {
                                value = getAppEntry({
                                    name: bundlePath,
                                    page,
                                    appPaths: entryData.appPaths,
                                    pagePath: posix.join(APP_DIR_ALIAS, relative(this.appDir, entryData.absolutePagePath).replace(/\\/g, "/")),
                                    appDir: this.appDir,
                                    pageExtensions: this.config.pageExtensions,
                                    rootDir: this.dir,
                                    isDev: true,
                                    tsconfigPath: this.config.typescript.tsconfigPath,
                                    basePath: this.config.basePath,
                                    assetPrefix: this.config.assetPrefix,
                                    nextConfigOutput: this.config.output,
                                    preferredRegion: staticInfo.preferredRegion,
                                    middlewareConfig: Buffer.from(JSON.stringify(staticInfo.middleware || {})).toString("base64")
                                });
                            } else if (isAPIRoute(page)) {
                                value = getRouteLoaderEntry({
                                    kind: RouteKind.PAGES_API,
                                    page,
                                    absolutePagePath: relativeRequest,
                                    preferredRegion: staticInfo.preferredRegion,
                                    middlewareConfig: staticInfo.middleware || {}
                                });
                            } else if (!isMiddlewareFile(page) && !isInternalComponent(relativeRequest) && !isNonRoutePagesPage(page) && !(isInstrumentationHookFile(page) && pageType === "root")) {
                                value = getRouteLoaderEntry({
                                    kind: RouteKind.PAGES,
                                    page,
                                    pages: this.pagesMapping,
                                    absolutePagePath: relativeRequest,
                                    preferredRegion: staticInfo.preferredRegion,
                                    middlewareConfig: staticInfo.middleware ?? {}
                                });
                            } else {
                                value = relativeRequest;
                            }
                            entrypoints[bundlePath] = finalizeEntrypoint({
                                compilerType: COMPILER_NAMES.server,
                                name: bundlePath,
                                isServerComponent,
                                value,
                                hasAppDir
                            });
                        }
                    });
                }));
                if (!this.hasAmpEntrypoints) {
                    delete entrypoints[CLIENT_STATIC_FILES_RUNTIME_AMP];
                }
                if (!this.hasPagesRouterEntrypoints) {
                    delete entrypoints[CLIENT_STATIC_FILES_RUNTIME_MAIN];
                    delete entrypoints["pages/_app"];
                    delete entrypoints["pages/_error"];
                    delete entrypoints["/_error"];
                    delete entrypoints["pages/_document"];
                }
                // Remove React Refresh entrypoint chunk as `app` doesn't require it.
                if (!this.hasAmpEntrypoints && !this.hasPagesRouterEntrypoints) {
                    delete entrypoints[CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH];
                }
                if (!this.hasAppRouterEntrypoints) {
                    delete entrypoints[CLIENT_STATIC_FILES_RUNTIME_MAIN_APP];
                }
                return entrypoints;
            };
        }
        // Enable building of client compilation before server compilation in development
        // @ts-ignore webpack 5
        this.activeWebpackConfigs.parallelism = 1;
        this.multiCompiler = webpack(this.activeWebpackConfigs);
        // Copy over the filesystem so that it is shared between all compilers.
        const inputFileSystem = this.multiCompiler.compilers[0].inputFileSystem;
        for (const compiler of this.multiCompiler.compilers){
            compiler.inputFileSystem = inputFileSystem;
            // This is set for the initial compile. After that Watching class in webpack adds it.
            compiler.fsStartTime = Date.now();
            // Ensure NodeEnvironmentPlugin doesn't purge the inputFileSystem. Purging is handled in `done` below.
            compiler.hooks.beforeRun.intercept({
                register (tapInfo) {
                    if (tapInfo.name === "NodeEnvironmentPlugin") {
                        return null;
                    }
                    return tapInfo;
                }
            });
        }
        this.multiCompiler.hooks.done.tap("NextjsHotReloader", ()=>{
            inputFileSystem.purge();
        });
        watchCompilers(this.multiCompiler.compilers[0], this.multiCompiler.compilers[1], this.multiCompiler.compilers[2]);
        // Watch for changes to client/server page files so we can tell when just
        // the server file changes and trigger a reload for GS(S)P pages
        const changedClientPages = new Set();
        const changedServerPages = new Set();
        const changedEdgeServerPages = new Set();
        const changedServerComponentPages = new Set();
        const changedCSSImportPages = new Set();
        const prevClientPageHashes = new Map();
        const prevServerPageHashes = new Map();
        const prevEdgeServerPageHashes = new Map();
        const prevCSSImportModuleHashes = new Map();
        const pageExtensionRegex = new RegExp(`\\.(?:${this.config.pageExtensions.join("|")})$`);
        const trackPageChanges = (pageHashMap, changedItems, serverComponentChangedItems)=>(stats)=>{
                try {
                    stats.entrypoints.forEach((entry, key)=>{
                        if (key.startsWith("pages/") || key.startsWith("app/") || isMiddlewareFilename(key)) {
                            // TODO this doesn't handle on demand loaded chunks
                            entry.chunks.forEach((chunk)=>{
                                if (chunk.id === key) {
                                    const modsIterable = stats.chunkGraph.getChunkModulesIterable(chunk);
                                    let hasCSSModuleChanges = false;
                                    let chunksHash = new StringXor();
                                    let chunksHashServerLayer = new StringXor();
                                    modsIterable.forEach((mod)=>{
                                        if (mod.resource && mod.resource.replace(/\\/g, "/").includes(key) && // Shouldn't match CSS modules, etc.
                                        pageExtensionRegex.test(mod.resource)) {
                                            var _mod_buildInfo_rsc, _mod_buildInfo;
                                            // use original source to calculate hash since mod.hash
                                            // includes the source map in development which changes
                                            // every time for both server and client so we calculate
                                            // the hash without the source map for the page module
                                            const hash = require("crypto").createHash("sha1").update(mod.originalSource().buffer()).digest().toString("hex");
                                            if (mod.layer === WEBPACK_LAYERS.reactServerComponents && (mod == null ? void 0 : (_mod_buildInfo = mod.buildInfo) == null ? void 0 : (_mod_buildInfo_rsc = _mod_buildInfo.rsc) == null ? void 0 : _mod_buildInfo_rsc.type) !== "client") {
                                                chunksHashServerLayer.add(hash);
                                            }
                                            chunksHash.add(hash);
                                        } else {
                                            var _mod_buildInfo_rsc1, _mod_buildInfo1;
                                            // for non-pages we can use the module hash directly
                                            const hash = stats.chunkGraph.getModuleHash(mod, chunk.runtime);
                                            if (mod.layer === WEBPACK_LAYERS.reactServerComponents && (mod == null ? void 0 : (_mod_buildInfo1 = mod.buildInfo) == null ? void 0 : (_mod_buildInfo_rsc1 = _mod_buildInfo1.rsc) == null ? void 0 : _mod_buildInfo_rsc1.type) !== "client") {
                                                chunksHashServerLayer.add(hash);
                                            }
                                            chunksHash.add(hash);
                                            // Both CSS import changes from server and client
                                            // components are tracked.
                                            if (key.startsWith("app/") && /\.(css|scss|sass)$/.test(mod.resource || "")) {
                                                const resourceKey = mod.layer + ":" + mod.resource;
                                                const prevHash = prevCSSImportModuleHashes.get(resourceKey);
                                                if (prevHash && prevHash !== hash) {
                                                    hasCSSModuleChanges = true;
                                                }
                                                prevCSSImportModuleHashes.set(resourceKey, hash);
                                            }
                                        }
                                    });
                                    const prevHash = pageHashMap.get(key);
                                    const curHash = chunksHash.toString();
                                    if (prevHash && prevHash !== curHash) {
                                        changedItems.add(key);
                                    }
                                    pageHashMap.set(key, curHash);
                                    if (serverComponentChangedItems) {
                                        const serverKey = WEBPACK_LAYERS.reactServerComponents + ":" + key;
                                        const prevServerHash = pageHashMap.get(serverKey);
                                        const curServerHash = chunksHashServerLayer.toString();
                                        if (prevServerHash && prevServerHash !== curServerHash) {
                                            serverComponentChangedItems.add(key);
                                        }
                                        pageHashMap.set(serverKey, curServerHash);
                                    }
                                    if (hasCSSModuleChanges) {
                                        changedCSSImportPages.add(key);
                                    }
                                }
                            });
                        }
                    });
                } catch (err) {
                    console.error(err);
                }
            };
        this.multiCompiler.compilers[0].hooks.emit.tap("NextjsHotReloaderForClient", trackPageChanges(prevClientPageHashes, changedClientPages));
        this.multiCompiler.compilers[1].hooks.emit.tap("NextjsHotReloaderForServer", trackPageChanges(prevServerPageHashes, changedServerPages, changedServerComponentPages));
        this.multiCompiler.compilers[2].hooks.emit.tap("NextjsHotReloaderForServer", trackPageChanges(prevEdgeServerPageHashes, changedEdgeServerPages, changedServerComponentPages));
        // This plugin watches for changes to _document.js and notifies the client side that it should reload the page
        this.multiCompiler.compilers[1].hooks.failed.tap("NextjsHotReloaderForServer", (err)=>{
            this.serverError = err;
            this.serverStats = null;
            this.serverChunkNames = undefined;
        });
        this.multiCompiler.compilers[2].hooks.done.tap("NextjsHotReloaderForServer", (stats)=>{
            this.serverError = null;
            this.edgeServerStats = stats;
        });
        this.multiCompiler.compilers[1].hooks.done.tap("NextjsHotReloaderForServer", (stats)=>{
            this.serverError = null;
            this.serverStats = stats;
            if (!this.pagesDir) {
                return;
            }
            const { compilation } = stats;
            // We only watch `_document` for changes on the server compilation
            // the rest of the files will be triggered by the client compilation
            const documentChunk = compilation.namedChunks.get("pages/_document");
            // If the document chunk can't be found we do nothing
            if (!documentChunk) {
                return;
            }
            // Initial value
            if (this.serverPrevDocumentHash === null) {
                this.serverPrevDocumentHash = documentChunk.hash || null;
                return;
            }
            // If _document.js didn't change we don't trigger a reload.
            if (documentChunk.hash === this.serverPrevDocumentHash) {
                return;
            }
            // As document chunk will change if new app pages are joined,
            // since react bundle is different it will effect the chunk hash.
            // So we diff the chunk changes, if there's only new app page chunk joins,
            // then we don't trigger a reload by checking pages/_document chunk change.
            if (this.appDir) {
                const chunkNames = new Set(compilation.namedChunks.keys());
                const diffChunkNames = difference(this.serverChunkNames || new Set(), chunkNames);
                if (diffChunkNames.length === 0 || diffChunkNames.every((chunkName)=>chunkName.startsWith("app/"))) {
                    return;
                }
                this.serverChunkNames = chunkNames;
            }
            this.serverPrevDocumentHash = documentChunk.hash || null;
            // Notify reload to reload the page, as _document.js was changed (different hash)
            this.send({
                action: HMR_ACTIONS_SENT_TO_BROWSER.RELOAD_PAGE
            });
        });
        this.multiCompiler.hooks.done.tap("NextjsHotReloaderForServer", ()=>{
            const reloadAfterInvalidation = this.reloadAfterInvalidation;
            this.reloadAfterInvalidation = false;
            const serverOnlyChanges = difference(changedServerPages, changedClientPages);
            const edgeServerOnlyChanges = difference(changedEdgeServerPages, changedClientPages);
            const pageChanges = serverOnlyChanges.concat(edgeServerOnlyChanges).filter((key)=>key.startsWith("pages/"));
            const middlewareChanges = Array.from(changedEdgeServerPages).filter((name)=>isMiddlewareFilename(name));
            if (middlewareChanges.length > 0) {
                this.send({
                    event: HMR_ACTIONS_SENT_TO_BROWSER.MIDDLEWARE_CHANGES
                });
            }
            if (pageChanges.length > 0) {
                this.send({
                    event: HMR_ACTIONS_SENT_TO_BROWSER.SERVER_ONLY_CHANGES,
                    pages: serverOnlyChanges.map((pg)=>denormalizePagePath(pg.slice("pages".length)))
                });
            }
            if (changedServerComponentPages.size || changedCSSImportPages.size || reloadAfterInvalidation) {
                this.refreshServerComponents();
            }
            changedClientPages.clear();
            changedServerPages.clear();
            changedEdgeServerPages.clear();
            changedServerComponentPages.clear();
            changedCSSImportPages.clear();
        });
        this.multiCompiler.compilers[0].hooks.failed.tap("NextjsHotReloaderForClient", (err)=>{
            this.clientError = err;
            this.clientStats = null;
        });
        this.multiCompiler.compilers[0].hooks.done.tap("NextjsHotReloaderForClient", (stats)=>{
            this.clientError = null;
            this.clientStats = stats;
            const { compilation } = stats;
            const chunkNames = new Set([
                ...compilation.namedChunks.keys()
            ].filter((name)=>!!getRouteFromEntrypoint(name)));
            if (this.prevChunkNames) {
                // detect chunks which have to be replaced with a new template
                // e.g, pages/index.js <-> pages/_error.js
                const addedPages = diff(chunkNames, this.prevChunkNames);
                const removedPages = diff(this.prevChunkNames, chunkNames);
                if (addedPages.size > 0) {
                    for (const addedPage of addedPages){
                        const page = getRouteFromEntrypoint(addedPage);
                        this.send({
                            action: HMR_ACTIONS_SENT_TO_BROWSER.ADDED_PAGE,
                            data: [
                                page
                            ]
                        });
                    }
                }
                if (removedPages.size > 0) {
                    for (const removedPage of removedPages){
                        const page = getRouteFromEntrypoint(removedPage);
                        this.send({
                            action: HMR_ACTIONS_SENT_TO_BROWSER.REMOVED_PAGE,
                            data: [
                                page
                            ]
                        });
                    }
                }
            }
            this.prevChunkNames = chunkNames;
        });
        this.webpackHotMiddleware = new WebpackHotMiddleware(this.multiCompiler.compilers, this.versionInfo);
        let booted = false;
        this.watcher = await new Promise((resolve)=>{
            var _this_multiCompiler;
            const watcher = (_this_multiCompiler = this.multiCompiler) == null ? void 0 : _this_multiCompiler.watch(// @ts-ignore webpack supports an array of watchOptions when using a multiCompiler
            this.activeWebpackConfigs.map((config)=>config.watchOptions), // Errors are handled separately
            (_err)=>{
                if (!booted) {
                    booted = true;
                    resolve(watcher);
                }
            });
        });
        this.onDemandEntries = onDemandEntryHandler({
            hotReloader: this,
            multiCompiler: this.multiCompiler,
            pagesDir: this.pagesDir,
            appDir: this.appDir,
            rootDir: this.dir,
            nextConfig: this.config,
            ...this.config.onDemandEntries
        });
        this.interceptors = [
            getOverlayMiddleware({
                rootDirectory: this.dir,
                stats: ()=>this.clientStats,
                serverStats: ()=>this.serverStats,
                edgeServerStats: ()=>this.edgeServerStats
            })
        ];
    }
    invalidate({ reloadAfterInvalidation } = {
        reloadAfterInvalidation: false
    }) {
        var _this_multiCompiler, _getInvalidator;
        // Cache the `reloadAfterInvalidation` flag, and use it to reload the page when compilation is done
        this.reloadAfterInvalidation = reloadAfterInvalidation;
        const outputPath = (_this_multiCompiler = this.multiCompiler) == null ? void 0 : _this_multiCompiler.outputPath;
        return outputPath && ((_getInvalidator = getInvalidator(outputPath)) == null ? void 0 : _getInvalidator.invalidate());
    }
    async stop() {
        await new Promise((resolve, reject)=>{
            this.watcher.close((err)=>err ? reject(err) : resolve(true));
        });
        if (this.fallbackWatcher) {
            await new Promise((resolve, reject)=>{
                this.fallbackWatcher.close((err)=>err ? reject(err) : resolve(true));
            });
        }
        this.multiCompiler = undefined;
    }
    async getCompilationErrors(page) {
        var _this_clientStats, _this_serverStats, _this_edgeServerStats;
        const getErrors = ({ compilation })=>{
            var _failedPages_normalizedPage;
            const failedPages = erroredPages(compilation);
            const normalizedPage = normalizePathSep(page);
            // If there is an error related to the requesting page we display it instead of the first error
            return ((_failedPages_normalizedPage = failedPages[normalizedPage]) == null ? void 0 : _failedPages_normalizedPage.length) > 0 ? failedPages[normalizedPage] : compilation.errors;
        };
        if (this.clientError) {
            return [
                this.clientError
            ];
        } else if (this.serverError) {
            return [
                this.serverError
            ];
        } else if ((_this_clientStats = this.clientStats) == null ? void 0 : _this_clientStats.hasErrors()) {
            return getErrors(this.clientStats);
        } else if ((_this_serverStats = this.serverStats) == null ? void 0 : _this_serverStats.hasErrors()) {
            return getErrors(this.serverStats);
        } else if ((_this_edgeServerStats = this.edgeServerStats) == null ? void 0 : _this_edgeServerStats.hasErrors()) {
            return getErrors(this.edgeServerStats);
        } else {
            return [];
        }
    }
    send(action) {
        this.webpackHotMiddleware.publish(action);
    }
    async ensurePage({ page, clientOnly, appPaths, definition, isApp, url }) {
        var _this_onDemandEntries;
        // Make sure we don't re-build or dispose prebuilt pages
        if (page !== "/_error" && BLOCKED_PAGES.indexOf(page) !== -1) {
            return;
        }
        const error = clientOnly ? this.clientError : this.serverError || this.clientError;
        if (error) {
            throw error;
        }
        return (_this_onDemandEntries = this.onDemandEntries) == null ? void 0 : _this_onDemandEntries.ensurePage({
            page,
            appPaths,
            definition,
            isApp,
            url
        });
    }
}

//# sourceMappingURL=hot-reloader-webpack.js.map