import { yellow, green, blue } from '../../../lib/picocolors';
const PLUGIN_NAME = 'SlowModuleDetectionPlugin';
const TreeSymbols = {
    VERTICAL_LINE: '│  ',
    BRANCH: '├─ '
};
const PATH_TRUNCATION_LENGTH = 120;
// Matches node_modules paths, including pnpm-style paths
const NODE_MODULES_PATH_PATTERN = /node_modules(?:\/\.pnpm)?\/(.*)/;
const getModuleIdentifier = (module)=>{
    const debugId = module.debugId;
    return String(debugId);
};
const getModuleDisplayName = (module)=>{
    const resourcePath = 'resource' in module && typeof module.resource === 'string' ? module.resource : undefined;
    if (!resourcePath) {
        return undefined;
    }
    let displayPath = resourcePath.replace(process.cwd(), '.');
    const nodeModulesMatch = displayPath.match(NODE_MODULES_PATH_PATTERN);
    if (nodeModulesMatch) {
        return nodeModulesMatch[1];
    }
    return displayPath;
};
/**
 * Truncates a path to a maximum length. If the path exceeds this length,
 * it will be truncated in the middle and replaced with '...'.
 */ function truncatePath(path, maxLength) {
    // If the path length is within the limit, return it as is
    if (path.length <= maxLength) return path;
    // Calculate the available length for the start and end segments after accounting for '...'
    const availableLength = maxLength - 3;
    const startSegmentLength = Math.ceil(availableLength / 2);
    const endSegmentLength = Math.floor(availableLength / 2);
    // Extract the start and end segments of the path
    const startSegment = path.slice(0, startSegmentLength);
    const endSegment = path.slice(-endSegmentLength);
    // Return the truncated path with '...' in the middle
    return `${startSegment}...${endSegment}`;
}
class ModuleBuildTimeAnalyzer {
    constructor(options){
        this.options = options;
        this.pendingModules = [];
        this.modules = new Map();
        this.moduleParents = new Map();
        this.moduleChildren = new Map();
        this.isFinalized = false;
        this.moduleBuildTimes = new WeakMap();
        this.buildTimeThresholdMs = options.buildTimeThresholdMs;
    }
    recordModuleBuildTime(module, duration) {
        // Webpack guarantees that no more modules will be built after finishModules hook is called,
        // where we generate the report. This check is just a defensive measure.
        if (this.isFinalized) {
            throw Object.defineProperty(new Error(`Invariant (SlowModuleDetectionPlugin): Module is recorded after the report is generated. This is a Next.js internal bug.`), "__NEXT_ERROR_CODE", {
                value: "E630",
                enumerable: false,
                configurable: true
            });
        }
        if (duration < this.buildTimeThresholdMs) {
            return; // Skip fast modules
        }
        this.moduleBuildTimes.set(module, duration);
        this.pendingModules.push(module);
    }
    /**
   * For each slow module, traverses up the dependency chain to find all ancestor modules.
   * Builds a directed graph where:
   * 1. Each slow module and its ancestors become nodes
   * 2. Edges represent "imported by" relationships
   * 3. Root nodes are entry points with no parents
   *
   * The resulting graph allows us to visualize the import chains that led to slow builds.
   */ prepareReport(compilation) {
        for (const module of this.pendingModules){
            const chain = new Set();
            // Walk up the module graph until we hit a root module (no issuer) to populate the chain
            {
                let currentModule = module;
                chain.add(currentModule);
                while(true){
                    const issuerModule = compilation.moduleGraph.getIssuer(currentModule);
                    if (!issuerModule) break;
                    if (chain.has(issuerModule)) {
                        throw Object.defineProperty(new Error(`Invariant (SlowModuleDetectionPlugin): Circular dependency detected in module graph. This is a Next.js internal bug.`), "__NEXT_ERROR_CODE", {
                            value: "E631",
                            enumerable: false,
                            configurable: true
                        });
                    }
                    chain.add(issuerModule);
                    currentModule = issuerModule;
                }
            }
            // Add all visited modules to our graph and create parent-child relationships
            let previousModule = null;
            for (const currentModule of chain){
                const moduleId = getModuleIdentifier(currentModule);
                if (!this.modules.has(moduleId)) {
                    this.modules.set(moduleId, currentModule);
                }
                if (previousModule) {
                    this.moduleParents.set(previousModule, currentModule);
                    let parentChildren = this.moduleChildren.get(currentModule);
                    if (!parentChildren) {
                        parentChildren = new Map();
                        this.moduleChildren.set(currentModule, parentChildren);
                    }
                    parentChildren.set(getModuleIdentifier(previousModule), previousModule);
                }
                previousModule = currentModule;
            }
        }
        this.isFinalized = true;
    }
    generateReport(compilation) {
        if (!this.isFinalized) {
            this.prepareReport(compilation);
        }
        // Find root modules (those with no parents)
        const rootModules = [
            ...this.modules.values()
        ].filter((node)=>!this.moduleParents.has(node));
        const formatModuleNode = (node, depth)=>{
            const moduleName = getModuleDisplayName(node) || '';
            if (!moduleName) {
                return formatChildModules(node, depth);
            }
            const prefix = ' ' + TreeSymbols.VERTICAL_LINE.repeat(depth) + TreeSymbols.BRANCH;
            const moduleText = blue(truncatePath(moduleName, PATH_TRUNCATION_LENGTH - prefix.length));
            const buildTimeMs = this.moduleBuildTimes.get(node);
            const duration = buildTimeMs ? yellow(` (${Math.ceil(buildTimeMs)}ms)`) : '';
            return prefix + moduleText + duration + '\n' + formatChildModules(node, depth + 1);
        };
        const formatChildModules = (node, depth)=>{
            const children = this.moduleChildren.get(node);
            if (!children) return '';
            return [
                ...children
            ].map(([_, child])=>formatModuleNode(child, depth)).join('');
        };
        const report = rootModules.map((root)=>formatModuleNode(root, 0)).join('');
        if (report) {
            console.log(green(`🐌 Detected slow modules while compiling ${this.options.compilerType}:`) + '\n' + report);
        }
    }
}
export default class SlowModuleDetectionPlugin {
    constructor(options){
        this.options = options;
        this.apply = (compiler)=>{
            compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation)=>{
                const analyzer = new ModuleBuildTimeAnalyzer(this.options);
                const moduleBuildStartTimes = new WeakMap();
                compilation.hooks.buildModule.tap(PLUGIN_NAME, (module)=>{
                    moduleBuildStartTimes.set(module, performance.now());
                });
                compilation.hooks.succeedModule.tap(PLUGIN_NAME, (module)=>{
                    const startTime = moduleBuildStartTimes.get(module);
                    if (!startTime) {
                        throw Object.defineProperty(new Error(`Invariant (SlowModuleDetectionPlugin): Unable to find the start time for a module build. This is a Next.js internal bug.`), "__NEXT_ERROR_CODE", {
                            value: "E629",
                            enumerable: false,
                            configurable: true
                        });
                    }
                    analyzer.recordModuleBuildTime(module, performance.now() - startTime);
                });
                compilation.hooks.finishModules.tap(PLUGIN_NAME, ()=>{
                    analyzer.generateReport(compilation);
                });
            });
        };
    }
}

//# sourceMappingURL=slow-module-detection-plugin.js.map