"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "exportPages", {
    enumerable: true,
    get: function() {
        return exportPages;
    }
});
require("../server/node-environment");
const _path = require("path");
const _promises = /*#__PURE__*/ _interop_require_default(require("fs/promises"));
const _loadcomponents = require("../server/load-components");
const _isdynamic = require("../shared/lib/router/utils/is-dynamic");
const _normalizepagepath = require("../shared/lib/page-path/normalize-page-path");
const _normalizelocalepath = require("../shared/lib/i18n/normalize-locale-path");
const _trace = require("../trace");
const _setuphttpagentenv = require("../server/setup-http-agent-env");
const _requestmeta = require("../server/request-meta");
const _apppaths = require("../shared/lib/router/utils/app-paths");
const _mockrequest = require("../server/lib/mock-request");
const _isapprouteroute = require("../lib/is-app-route-route");
const _ciinfo = require("../server/ci-info");
const _approute = require("./routes/app-route");
const _apppage = require("./routes/app-page");
const _pages = require("./routes/pages");
const _getparams = require("./helpers/get-params");
const _createincrementalcache = require("./helpers/create-incremental-cache");
const _ispostpone = require("../server/lib/router-utils/is-postpone");
const _isdynamicusageerror = require("./helpers/is-dynamic-usage-error");
const _bailouttocsr = require("../shared/lib/lazy-dynamic/bailout-to-csr");
const _turborepoaccesstrace = require("../build/turborepo-access-trace");
const _fallbackparams = require("../server/request/fallback-params");
const _needsexperimentalreact = require("../lib/needs-experimental-react");
const _staticgenerationbailout = require("../client/components/static-generation-bailout");
const _multifilewriter = require("../lib/multi-file-writer");
const _resumedatacache = require("../server/resume-data-cache/resume-data-cache");
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
process.env.NEXT_IS_EXPORT_WORKER = 'true';
const envConfig = require('../shared/lib/runtime-config.external');
globalThis.__NEXT_DATA__ = {
    nextExport: true
};
class TimeoutError extends Error {
    constructor(...args){
        super(...args), this.code = 'NEXT_EXPORT_TIMEOUT_ERROR';
    }
}
class ExportPageError extends Error {
    constructor(...args){
        super(...args), this.code = 'NEXT_EXPORT_PAGE_ERROR';
    }
}
async function exportPageImpl(input, fileWriter) {
    var _req_url;
    const { exportPath, distDir, pagesDataDir, buildExport = false, serverRuntimeConfig, subFolders = false, optimizeCss, disableOptimizedLoading, debugOutput = false, enableExperimentalReact, ampValidatorPath, trailingSlash, sriEnabled, renderOpts: commonRenderOpts, outDir: commonOutDir, buildId, renderResumeDataCache } = input;
    if (enableExperimentalReact) {
        process.env.__NEXT_EXPERIMENTAL_REACT = 'true';
    }
    const { path, page, // The parameters that are currently unknown.
    _fallbackRouteParams = [], // Check if this is an `app/` page.
    _isAppDir: isAppDir = false, // Check if this should error when dynamic usage is detected.
    _isDynamicError: isDynamicError = false, // If this page supports partial prerendering, then we need to pass that to
    // the renderOpts.
    _isRoutePPREnabled: isRoutePPREnabled, // Configure the rendering of the page to allow that an empty static shell
    // is generated while rendering using PPR and Cache Components.
    _allowEmptyStaticShell: allowEmptyStaticShell = false, // Pull the original query out.
    query: originalQuery = {} } = exportPath;
    const fallbackRouteParams = (0, _fallbackparams.getFallbackRouteParams)(_fallbackRouteParams);
    let query = {
        ...originalQuery
    };
    const pathname = (0, _apppaths.normalizeAppPath)(page);
    const isDynamic = (0, _isdynamic.isDynamicRoute)(page);
    const outDir = isAppDir ? (0, _path.join)(distDir, 'server/app') : commonOutDir;
    const filePath = (0, _normalizepagepath.normalizePagePath)(path);
    const ampPath = `${filePath}.amp`;
    let renderAmpPath = ampPath;
    let updatedPath = exportPath._ssgPath || path;
    let locale = exportPath._locale || commonRenderOpts.locale;
    if (commonRenderOpts.locale) {
        const localePathResult = (0, _normalizelocalepath.normalizeLocalePath)(path, commonRenderOpts.locales);
        if (localePathResult.detectedLocale) {
            updatedPath = localePathResult.pathname;
            locale = localePathResult.detectedLocale;
            if (locale === commonRenderOpts.defaultLocale) {
                renderAmpPath = `${(0, _normalizepagepath.normalizePagePath)(updatedPath)}.amp`;
            }
        }
    }
    // We need to show a warning if they try to provide query values
    // for an auto-exported page since they won't be available
    const hasOrigQueryValues = Object.keys(originalQuery).length > 0;
    // Check if the page is a specified dynamic route
    const { pathname: nonLocalizedPath } = (0, _normalizelocalepath.normalizeLocalePath)(path, commonRenderOpts.locales);
    let params;
    if (isDynamic && page !== nonLocalizedPath) {
        const normalizedPage = isAppDir ? (0, _apppaths.normalizeAppPath)(page) : page;
        params = (0, _getparams.getParams)(normalizedPage, updatedPath);
    }
    const { req, res } = (0, _mockrequest.createRequestResponseMocks)({
        url: updatedPath
    });
    // If this is a status code page, then set the response code.
    for (const statusCode of [
        404,
        500
    ]){
        if ([
            `/${statusCode}`,
            `/${statusCode}.html`,
            `/${statusCode}/index.html`
        ].some((p)=>p === updatedPath || `/${locale}${p}` === updatedPath)) {
            res.statusCode = statusCode;
        }
    }
    // Ensure that the URL has a trailing slash if it's configured.
    if (trailingSlash && !((_req_url = req.url) == null ? void 0 : _req_url.endsWith('/'))) {
        req.url += '/';
    }
    if (locale && buildExport && commonRenderOpts.domainLocales && commonRenderOpts.domainLocales.some((dl)=>{
        var _dl_locales;
        return dl.defaultLocale === locale || ((_dl_locales = dl.locales) == null ? void 0 : _dl_locales.includes(locale || ''));
    })) {
        (0, _requestmeta.addRequestMeta)(req, 'isLocaleDomain', true);
    }
    envConfig.setConfig({
        serverRuntimeConfig,
        publicRuntimeConfig: commonRenderOpts.runtimeConfig
    });
    const getHtmlFilename = (p)=>subFolders ? `${p}${_path.sep}index.html` : `${p}.html`;
    let htmlFilename = getHtmlFilename(filePath);
    // dynamic routes can provide invalid extensions e.g. /blog/[...slug] returns an
    // extension of `.slug]`
    const pageExt = isDynamic || isAppDir ? '' : (0, _path.extname)(page);
    const pathExt = isDynamic || isAppDir ? '' : (0, _path.extname)(path);
    // force output 404.html for backwards compat
    if (path === '/404.html') {
        htmlFilename = path;
    } else if (pageExt !== pathExt && pathExt !== '') {
        const isBuiltinPaths = [
            '/500',
            '/404'
        ].some((p)=>p === path || p === path + '.html');
        // If the ssg path has .html extension, and it's not builtin paths, use it directly
        // Otherwise, use that as the filename instead
        const isHtmlExtPath = !isBuiltinPaths && path.endsWith('.html');
        htmlFilename = isHtmlExtPath ? getHtmlFilename(path) : path;
    } else if (path === '/') {
        // If the path is the root, just use index.html
        htmlFilename = 'index.html';
    }
    const baseDir = (0, _path.join)(outDir, (0, _path.dirname)(htmlFilename));
    let htmlFilepath = (0, _path.join)(outDir, htmlFilename);
    await _promises.default.mkdir(baseDir, {
        recursive: true
    });
    const components = await (0, _loadcomponents.loadComponents)({
        distDir,
        page,
        isAppPath: isAppDir,
        isDev: false,
        sriEnabled
    });
    // Handle App Routes.
    if (isAppDir && (0, _isapprouteroute.isAppRouteRoute)(page)) {
        return (0, _approute.exportAppRoute)(req, res, params, page, components.routeModule, commonRenderOpts.incrementalCache, commonRenderOpts.cacheLifeProfiles, htmlFilepath, fileWriter, commonRenderOpts.experimental, buildId);
    }
    const renderOpts = {
        ...components,
        ...commonRenderOpts,
        ampPath: renderAmpPath,
        params,
        optimizeCss,
        disableOptimizedLoading,
        locale,
        supportsDynamicResponse: false,
        // During the export phase in next build, we always enable the streaming metadata since if there's
        // any dynamic access in metadata we can determine it in the build phase.
        // If it's static, then it won't affect anything.
        // If it's dynamic, then it can be handled when request hits the route.
        serveStreamingMetadata: true,
        allowEmptyStaticShell,
        experimental: {
            ...commonRenderOpts.experimental,
            isRoutePPREnabled
        },
        renderResumeDataCache
    };
    if (_ciinfo.hasNextSupport) {
        renderOpts.isRevalidate = true;
    }
    // Handle App Pages
    if (isAppDir) {
        const sharedContext = {
            buildId
        };
        return (0, _apppage.exportAppPage)(req, res, page, path, pathname, query, fallbackRouteParams, renderOpts, htmlFilepath, debugOutput, isDynamicError, fileWriter, sharedContext);
    }
    const sharedContext = {
        buildId,
        deploymentId: commonRenderOpts.deploymentId,
        customServer: undefined
    };
    const renderContext = {
        isFallback: exportPath._pagesFallback ?? false,
        isDraftMode: false,
        developmentNotFoundSourcePage: undefined
    };
    return (0, _pages.exportPagesPage)(req, res, path, page, query, params, htmlFilepath, htmlFilename, ampPath, subFolders, outDir, ampValidatorPath, pagesDataDir, buildExport, isDynamic, sharedContext, renderContext, hasOrigQueryValues, renderOpts, components, fileWriter);
}
async function exportPages(input) {
    const { exportPaths, dir, distDir, outDir, cacheHandler, cacheMaxMemorySize, fetchCacheKeyPrefix, pagesDataDir, renderOpts, nextConfig, options, renderResumeDataCachesByPage = {} } = input;
    if (nextConfig.experimental.enablePrerenderSourceMaps) {
        try {
            // Same as `next dev`
            // Limiting the stack trace to a useful amount of frames is handled by ignore-listing.
            // TODO: How high can we go without severely impacting CPU/memory?
            Error.stackTraceLimit = 50;
        } catch  {}
    }
    // If the fetch cache was enabled, we need to create an incremental
    // cache instance for this page.
    const incrementalCache = await (0, _createincrementalcache.createIncrementalCache)({
        cacheHandler,
        cacheMaxMemorySize,
        fetchCacheKeyPrefix,
        distDir,
        dir,
        // skip writing to disk in minimal mode for now, pending some
        // changes to better support it
        flushToDisk: !_ciinfo.hasNextSupport,
        cacheHandlers: nextConfig.experimental.cacheHandlers
    });
    renderOpts.incrementalCache = incrementalCache;
    const maxConcurrency = nextConfig.experimental.staticGenerationMaxConcurrency ?? 8;
    const results = [];
    const exportPageWithRetry = async (exportPath, maxAttempts)=>{
        var // Also tests for `inspect-brk`
        _process_env_NODE_OPTIONS;
        const { page, path } = exportPath;
        const pageKey = page !== path ? `${page}: ${path}` : path;
        let attempt = 0;
        let result;
        const hasDebuggerAttached = (_process_env_NODE_OPTIONS = process.env.NODE_OPTIONS) == null ? void 0 : _process_env_NODE_OPTIONS.includes('--inspect');
        const renderResumeDataCache = renderResumeDataCachesByPage[page] ? (0, _resumedatacache.createRenderResumeDataCache)(renderResumeDataCachesByPage[page]) : undefined;
        while(attempt < maxAttempts){
            try {
                var _nextConfig_experimental_amp, _nextConfig_experimental_sri;
                result = await Promise.race([
                    exportPage({
                        exportPath,
                        distDir,
                        outDir,
                        pagesDataDir,
                        renderOpts,
                        ampValidatorPath: ((_nextConfig_experimental_amp = nextConfig.experimental.amp) == null ? void 0 : _nextConfig_experimental_amp.validator) || undefined,
                        trailingSlash: nextConfig.trailingSlash,
                        serverRuntimeConfig: nextConfig.serverRuntimeConfig,
                        subFolders: nextConfig.trailingSlash && !options.buildExport,
                        buildExport: options.buildExport,
                        optimizeCss: nextConfig.experimental.optimizeCss,
                        disableOptimizedLoading: nextConfig.experimental.disableOptimizedLoading,
                        parentSpanId: input.parentSpanId,
                        httpAgentOptions: nextConfig.httpAgentOptions,
                        debugOutput: options.debugOutput,
                        enableExperimentalReact: (0, _needsexperimentalreact.needsExperimentalReact)(nextConfig),
                        sriEnabled: Boolean((_nextConfig_experimental_sri = nextConfig.experimental.sri) == null ? void 0 : _nextConfig_experimental_sri.algorithm),
                        buildId: input.buildId,
                        renderResumeDataCache
                    }),
                    hasDebuggerAttached ? new Promise(()=>{}) : new Promise((_, reject)=>{
                        setTimeout(()=>{
                            reject(new TimeoutError());
                        }, nextConfig.staticPageGenerationTimeout * 1000);
                    })
                ]);
                // If there was an error in the export, throw it immediately. In the catch block, we might retry the export,
                // or immediately fail the build, depending on user configuration. We might also continue on and attempt other pages.
                if (result && 'error' in result) {
                    throw new ExportPageError();
                }
                break;
            } catch (err) {
                // The only error that should be caught here is an ExportError, as `exportPage` doesn't throw and instead returns an object with an `error` property.
                // This is an overly cautious check to ensure that we don't accidentally catch an unexpected error.
                if (!(err instanceof ExportPageError || err instanceof TimeoutError)) {
                    throw err;
                }
                if (err instanceof TimeoutError) {
                    // If the export times out, we will restart the worker up to 3 times.
                    maxAttempts = 3;
                }
                // We've reached the maximum number of attempts
                if (attempt >= maxAttempts - 1) {
                    // Log a message if we've reached the maximum number of attempts.
                    // We only care to do this if maxAttempts was configured.
                    if (maxAttempts > 1) {
                        console.info(`Failed to build ${pageKey} after ${maxAttempts} attempts.`);
                    }
                    // If prerenderEarlyExit is enabled, we'll exit the build immediately.
                    if (nextConfig.experimental.prerenderEarlyExit) {
                        console.error(`Export encountered an error on ${pageKey}, exiting the build.`);
                        process.exit(1);
                    } else {
                    // Otherwise, this is a no-op. The build will continue, and a summary of failed pages will be displayed at the end.
                    }
                } else {
                    // Otherwise, we have more attempts to make. Wait before retrying
                    if (err instanceof TimeoutError) {
                        console.info(`Failed to build ${pageKey} (attempt ${attempt + 1} of ${maxAttempts}) because it took more than ${nextConfig.staticPageGenerationTimeout} seconds. Retrying again shortly.`);
                    } else {
                        console.info(`Failed to build ${pageKey} (attempt ${attempt + 1} of ${maxAttempts}). Retrying again shortly.`);
                    }
                    // Exponential backoff with random jitter to avoid thundering herd on retries
                    const baseDelay = 500 // 500ms
                    ;
                    const maxDelay = 2000 // 2 seconds
                    ;
                    const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
                    const jitter = Math.random() * 0.3 * delay // Add up to 30% random jitter
                    ;
                    await new Promise((r)=>setTimeout(r, delay + jitter));
                }
            }
            attempt++;
        }
        return {
            result,
            path,
            page,
            pageKey
        };
    };
    for(let i = 0; i < exportPaths.length; i += maxConcurrency){
        const subset = exportPaths.slice(i, i + maxConcurrency);
        const subsetResults = await Promise.all(subset.map((exportPath)=>exportPageWithRetry(exportPath, nextConfig.experimental.staticGenerationRetryCount ?? 1)));
        results.push(...subsetResults);
    }
    return results;
}
async function exportPage(input) {
    (0, _trace.trace)('export-page', input.parentSpanId).setAttribute('path', input.exportPath.path);
    // Configure the http agent.
    (0, _setuphttpagentenv.setHttpClientAndAgentOptions)({
        httpAgentOptions: input.httpAgentOptions
    });
    const fileWriter = new _multifilewriter.MultiFileWriter({
        writeFile: (filePath, data)=>_promises.default.writeFile(filePath, data),
        mkdir: (dir)=>_promises.default.mkdir(dir, {
                recursive: true
            })
    });
    const exportPageSpan = (0, _trace.trace)('export-page-worker', input.parentSpanId);
    const start = Date.now();
    const turborepoAccessTraceResult = new _turborepoaccesstrace.TurborepoAccessTraceResult();
    // Export the page.
    let result;
    try {
        result = await exportPageSpan.traceAsyncFn(()=>(0, _turborepoaccesstrace.turborepoTraceAccess)(()=>exportPageImpl(input, fileWriter), turborepoAccessTraceResult));
        // Wait for all the files to flush to disk.
        await fileWriter.wait();
        // If there was no result, then we can exit early.
        if (!result) return;
        // If there was an error, then we can exit early.
        if ('error' in result) {
            return {
                error: result.error,
                duration: Date.now() - start
            };
        }
    } catch (err) {
        console.error(`Error occurred prerendering page "${input.exportPath.path}". Read more: https://nextjs.org/docs/messages/prerender-error`);
        // bailoutToCSRError errors should not leak to the user as they are not actionable; they're
        // a framework signal
        if (!(0, _bailouttocsr.isBailoutToCSRError)(err)) {
            // A static generation bailout error is a framework signal to fail static generation but
            // and will encode a reason in the error message. If there is a message, we'll print it.
            // Otherwise there's nothing to show as we don't want to leak an error internal error stack to the user.
            // TODO: Always log the full error. ignore-listing will take care of hiding internal stacks.
            if ((0, _staticgenerationbailout.isStaticGenBailoutError)(err)) {
                if (err.message) {
                    console.error(`Error: ${err.message}`);
                }
            } else {
                console.error(err);
            }
        }
        return {
            error: true,
            duration: Date.now() - start
        };
    }
    // Notify the parent process that we processed a page (used by the progress activity indicator)
    process.send == null ? void 0 : process.send.call(process, [
        3,
        {
            type: 'activity'
        }
    ]);
    // Otherwise we can return the result.
    return {
        ...result,
        duration: Date.now() - start,
        turborepoAccessTraceResult: turborepoAccessTraceResult.serialize()
    };
}
process.on('unhandledRejection', (err)=>{
    // if it's a postpone error, it'll be handled later
    // when the postponed promise is actually awaited.
    if ((0, _ispostpone.isPostpone)(err)) {
        return;
    }
    // we don't want to log these errors
    if ((0, _isdynamicusageerror.isDynamicUsageError)(err)) {
        return;
    }
    console.error(err);
});
process.on('rejectionHandled', ()=>{
// It is ok to await a Promise late in Next.js as it allows for better
// prefetching patterns to avoid waterfalls. We ignore logging these.
// We should've already errored in anyway unhandledRejection.
});
const FATAL_UNHANDLED_NEXT_API_EXIT_CODE = 78;
process.on('uncaughtException', (err)=>{
    if ((0, _isdynamicusageerror.isDynamicUsageError)(err)) {
        console.error('A Next.js API that uses exceptions to signal framework behavior was uncaught. This suggests improper usage of a Next.js API. The original error is printed below and the build will now exit.');
        console.error(err);
        process.exit(FATAL_UNHANDLED_NEXT_API_EXIT_CODE);
    } else {
        console.error(err);
    }
});

//# sourceMappingURL=worker.js.map