import { codeFrameColumns } from "next/dist/compiled/babel/code-frame";
import { constants as FS, promises as fs } from "fs";
import path from "path";
import { SourceMapConsumer } from "next/dist/compiled/source-map08";
import url from "url";
import { getRawSourceMap } from "../internal/helpers/getRawSourceMap";
import { launchEditor } from "../internal/helpers/launchEditor";
export { getServerError } from "../internal/helpers/nodeStackFrames";
export { parseStack } from "../internal/helpers/parseStack";
function getModuleId(compilation, module) {
    return compilation.chunkGraph.getModuleId(module);
}
function getModuleById(id, compilation) {
    return [
        ...compilation.modules
    ].find((searchModule)=>{
        const moduleId = getModuleId(compilation, searchModule);
        return moduleId === id;
    });
}
function findModuleNotFoundFromError(errorMessage) {
    const match = errorMessage == null ? void 0 : errorMessage.match(/'([^']+)' module/);
    return match && match[1];
}
function getModuleSource(compilation, module) {
    var _compilation_codeGenerationResults_get;
    var _ref;
    return (_ref = module && ((_compilation_codeGenerationResults_get = compilation.codeGenerationResults.get(module)) == null ? void 0 : _compilation_codeGenerationResults_get.sources.get("javascript"))) != null ? _ref : null;
}
function getSourcePath(source) {
    // Webpack prefixes certain source paths with this path
    if (source.startsWith("webpack:///")) {
        return source.substring(11);
    }
    // Make sure library name is filtered out as well
    if (source.startsWith("webpack://_N_E/")) {
        return source.substring(15);
    }
    if (source.startsWith("webpack://")) {
        return source.substring(10);
    }
    return source;
}
async function findOriginalSourcePositionAndContent(webpackSource, position) {
    const consumer = await new SourceMapConsumer(webpackSource.map());
    try {
        var _position_column;
        const sourcePosition = consumer.originalPositionFor({
            line: position.line,
            column: (_position_column = position.column) != null ? _position_column : 0
        });
        if (!sourcePosition.source) {
            return null;
        }
        var _consumer_sourceContentFor;
        const sourceContent = (_consumer_sourceContentFor = consumer.sourceContentFor(sourcePosition.source, /* returnNullOnMissing */ true)) != null ? _consumer_sourceContentFor : null;
        return {
            sourcePosition,
            sourceContent
        };
    } finally{
        consumer.destroy();
    }
}
function findOriginalSourcePositionAndContentFromCompilation(moduleId, importedModule, compilation) {
    var _module_buildInfo_importLocByPath, _module_buildInfo;
    const module = getModuleById(moduleId, compilation);
    var _module_buildInfo_importLocByPath_get;
    return (_module_buildInfo_importLocByPath_get = module == null ? void 0 : (_module_buildInfo = module.buildInfo) == null ? void 0 : (_module_buildInfo_importLocByPath = _module_buildInfo.importLocByPath) == null ? void 0 : _module_buildInfo_importLocByPath.get(importedModule)) != null ? _module_buildInfo_importLocByPath_get : null;
}
function findCallStackFramePackage(id, compilation) {
    var _module_resourceResolveData_descriptionFileData, _module_resourceResolveData;
    if (!compilation) {
        return undefined;
    }
    const module = getModuleById(id, compilation);
    return module == null ? void 0 : (_module_resourceResolveData = module.resourceResolveData) == null ? void 0 : (_module_resourceResolveData_descriptionFileData = _module_resourceResolveData.descriptionFileData) == null ? void 0 : _module_resourceResolveData_descriptionFileData.name;
}
export async function createOriginalStackFrame(param) {
    let { line, column, source, sourcePackage, moduleId, modulePath, rootDirectory, frame, errorMessage, clientCompilation, serverCompilation, edgeCompilation } = param;
    var // default is not a valid identifier in JS so webpack uses a custom variable when it's an unnamed default export
    // Resolve it back to `default` for the method name if the source position didn't have the method.
    _frame_methodName_replace, _frame_methodName, _originalFrame_file;
    const moduleNotFound = findModuleNotFoundFromError(errorMessage);
    const result = await (async ()=>{
        if (moduleNotFound) {
            let moduleNotFoundResult = null;
            if (clientCompilation) {
                moduleNotFoundResult = findOriginalSourcePositionAndContentFromCompilation(moduleId, moduleNotFound, clientCompilation);
            }
            if (moduleNotFoundResult === null && serverCompilation) {
                moduleNotFoundResult = findOriginalSourcePositionAndContentFromCompilation(moduleId, moduleNotFound, serverCompilation);
            }
            if (moduleNotFoundResult === null && edgeCompilation) {
                moduleNotFoundResult = findOriginalSourcePositionAndContentFromCompilation(moduleId, moduleNotFound, edgeCompilation);
            }
            return moduleNotFoundResult;
        }
        // This returns 1-based lines and 0-based columns
        return await findOriginalSourcePositionAndContent(source, {
            line,
            column
        });
    })();
    if (result === null) {
        return null;
    }
    const { sourcePosition, sourceContent } = result;
    if (!sourcePosition.source) {
        return null;
    }
    const filePath = path.resolve(rootDirectory, getSourcePath(// When sourcePosition.source is the loader path the modulePath is generally better.
    (sourcePosition.source.includes("|") ? modulePath : sourcePosition.source) || modulePath));
    var _sourcePosition_column;
    const originalFrame = {
        file: sourceContent ? path.relative(rootDirectory, filePath) : sourcePosition.source,
        lineNumber: sourcePosition.line,
        column: ((_sourcePosition_column = sourcePosition.column) != null ? _sourcePosition_column : 0) + 1,
        methodName: sourcePosition.name || ((_frame_methodName = frame.methodName) == null ? void 0 : (_frame_methodName_replace = _frame_methodName.replace("__WEBPACK_DEFAULT_EXPORT__", "default")) == null ? void 0 : _frame_methodName_replace.replace("__webpack_exports__.", "")),
        arguments: []
    };
    var _originalFrame_file_includes, _sourcePosition_column1;
    const originalCodeFrame = !((_originalFrame_file_includes = (_originalFrame_file = originalFrame.file) == null ? void 0 : _originalFrame_file.includes("node_modules")) != null ? _originalFrame_file_includes : true) && sourceContent && sourcePosition.line ? codeFrameColumns(sourceContent, {
        start: {
            line: sourcePosition.line,
            column: ((_sourcePosition_column1 = sourcePosition.column) != null ? _sourcePosition_column1 : 0) + 1
        }
    }, {
        forceColor: true
    }) : null;
    return {
        originalStackFrame: originalFrame,
        originalCodeFrame,
        sourcePackage
    };
}
export async function getSourceById(isFile, id, compilation) {
    if (isFile) {
        const fileContent = await fs.readFile(id, "utf-8").catch(()=>null);
        if (fileContent == null) {
            return null;
        }
        const map = getRawSourceMap(fileContent);
        if (map == null) {
            return null;
        }
        return {
            map () {
                return map;
            }
        };
    }
    try {
        if (!compilation) {
            return null;
        }
        const module = getModuleById(id, compilation);
        const moduleSource = getModuleSource(compilation, module);
        return moduleSource;
    } catch (err) {
        console.error('Failed to lookup module by ID ("' + id + '"):', err);
        return null;
    }
}
function getOverlayMiddleware(options) {
    return async function(req, res, next) {
        const { pathname, query } = url.parse(req.url, true);
        if (pathname === "/__nextjs_original-stack-frame") {
            var _frame_file, _frame_file1, _frame_file2, _frame_lineNumber, _options_stats, _options_serverStats, _options_edgeServerStats, _frame_lineNumber1, _frame_column;
            const frame = query;
            const isAppDirectory = frame.isAppDirectory === "true";
            const isServerError = frame.isServer === "true";
            const isEdgeServerError = frame.isEdgeServer === "true";
            const isClientError = !isServerError && !isEdgeServerError;
            var _frame_lineNumber_toString;
            if (!((((_frame_file = frame.file) == null ? void 0 : _frame_file.startsWith("webpack-internal:///")) || ((_frame_file1 = frame.file) == null ? void 0 : _frame_file1.startsWith("file://")) || ((_frame_file2 = frame.file) == null ? void 0 : _frame_file2.startsWith("webpack://"))) && Boolean(parseInt((_frame_lineNumber_toString = (_frame_lineNumber = frame.lineNumber) == null ? void 0 : _frame_lineNumber.toString()) != null ? _frame_lineNumber_toString : "", 10)))) {
                res.statusCode = 400;
                res.write("Bad Request");
                return res.end();
            }
            const moduleId = frame.file.replace(/^(webpack-internal:\/\/\/|file:\/\/|webpack:\/\/(_N_E\/)?)/, "");
            const modulePath = frame.file.replace(/^(webpack-internal:\/\/\/|file:\/\/|webpack:\/\/(_N_E\/)?)(\(.*\)\/?)/, "");
            let source = null;
            let sourcePackage = undefined;
            const clientCompilation = (_options_stats = options.stats()) == null ? void 0 : _options_stats.compilation;
            const serverCompilation = (_options_serverStats = options.serverStats()) == null ? void 0 : _options_serverStats.compilation;
            const edgeCompilation = (_options_edgeServerStats = options.edgeServerStats()) == null ? void 0 : _options_edgeServerStats.compilation;
            const isFile = frame.file.startsWith("file:");
            try {
                if (isClientError || isAppDirectory) {
                    // Try Client Compilation first
                    // In `pages` we leverage `isClientError` to check
                    // In `app` it depends on if it's a server / client component and when the code throws. E.g. during HTML rendering it's the server/edge compilation.
                    source = await getSourceById(isFile, moduleId, clientCompilation);
                    sourcePackage = findCallStackFramePackage(moduleId, clientCompilation);
                }
                // Try Server Compilation
                // In `pages` this could be something imported in getServerSideProps/getStaticProps as the code for those is tree-shaken.
                // In `app` this finds server components and code that was imported from a server component. It also covers when client component code throws during HTML rendering.
                if ((isServerError || isAppDirectory) && source === null) {
                    source = await getSourceById(isFile, moduleId, serverCompilation);
                    sourcePackage = findCallStackFramePackage(moduleId, serverCompilation);
                }
                // Try Edge Server Compilation
                // Both cases are the same as Server Compilation, main difference is that it covers `runtime: 'edge'` pages/app routes.
                if ((isEdgeServerError || isAppDirectory) && source === null) {
                    source = await getSourceById(isFile, moduleId, edgeCompilation);
                    sourcePackage = findCallStackFramePackage(moduleId, edgeCompilation);
                }
            } catch (err) {
                console.log("Failed to get source map:", err);
                res.statusCode = 500;
                res.write("Internal Server Error");
                return res.end();
            }
            if (source == null) {
                res.statusCode = 204;
                res.write("No Content");
                return res.end();
            }
            var _frame_lineNumber_toString1;
            const frameLine = parseInt((_frame_lineNumber_toString1 = (_frame_lineNumber1 = frame.lineNumber) == null ? void 0 : _frame_lineNumber1.toString()) != null ? _frame_lineNumber_toString1 : "", 10);
            var _frame_column_toString;
            let frameColumn = parseInt((_frame_column_toString = (_frame_column = frame.column) == null ? void 0 : _frame_column.toString()) != null ? _frame_column_toString : "", 10);
            if (!frameColumn) {
                frameColumn = null;
            }
            try {
                const originalStackFrameResponse = await createOriginalStackFrame({
                    line: frameLine,
                    column: frameColumn,
                    source,
                    sourcePackage,
                    frame,
                    moduleId,
                    modulePath,
                    rootDirectory: options.rootDirectory,
                    errorMessage: frame.errorMessage,
                    clientCompilation: isClientError ? clientCompilation : undefined,
                    serverCompilation: isServerError ? serverCompilation : undefined,
                    edgeCompilation: isEdgeServerError ? edgeCompilation : undefined
                });
                if (originalStackFrameResponse === null) {
                    res.statusCode = 204;
                    res.write("No Content");
                    return res.end();
                }
                res.statusCode = 200;
                res.setHeader("Content-Type", "application/json");
                res.write(Buffer.from(JSON.stringify(originalStackFrameResponse)));
                return res.end();
            } catch (err) {
                console.log("Failed to parse source map:", err);
                res.statusCode = 500;
                res.write("Internal Server Error");
                return res.end();
            }
        } else if (pathname === "/__nextjs_launch-editor") {
            var _frame_file3, _frame_lineNumber2, _frame_column1;
            const frame = query;
            const frameFile = ((_frame_file3 = frame.file) == null ? void 0 : _frame_file3.toString()) || null;
            if (frameFile == null) {
                res.statusCode = 400;
                res.write("Bad Request");
                return res.end();
            }
            // frame files may start with their webpack layer, like (middleware)/middleware.js
            const filePath = path.resolve(options.rootDirectory, frameFile.replace(/^\([^)]+\)\//, ""));
            const fileExists = await fs.access(filePath, FS.F_OK).then(()=>true, ()=>false);
            if (!fileExists) {
                res.statusCode = 204;
                res.write("No Content");
                return res.end();
            }
            var _frame_lineNumber_toString2;
            const frameLine = parseInt((_frame_lineNumber_toString2 = (_frame_lineNumber2 = frame.lineNumber) == null ? void 0 : _frame_lineNumber2.toString()) != null ? _frame_lineNumber_toString2 : "", 10) || 1;
            var _frame_column_toString1;
            const frameColumn = parseInt((_frame_column_toString1 = (_frame_column1 = frame.column) == null ? void 0 : _frame_column1.toString()) != null ? _frame_column_toString1 : "", 10) || 1;
            try {
                await launchEditor(filePath, frameLine, frameColumn);
            } catch (err) {
                console.log("Failed to launch editor:", err);
                res.statusCode = 500;
                res.write("Internal Server Error");
                return res.end();
            }
            res.statusCode = 204;
            return res.end();
        }
        return next();
    };
}
export { getOverlayMiddleware };

//# sourceMappingURL=middleware.js.map