"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "stitchBuilds", {
    enumerable: true,
    get: function() {
        return stitchBuilds;
    }
});
const _fs = /*#__PURE__*/ _interop_require_default(require("fs"));
const _path = /*#__PURE__*/ _interop_require_default(require("path"));
const _entries = require("../entries");
const _asyncsema = require("next/dist/compiled/async-sema");
const _recursivecopy = require("../../lib/recursive-copy");
const _utils = require("../../shared/lib/router/utils");
const _buildmanifestplugin = require("../webpack/plugins/build-manifest-plugin");
const _createclientrouterfilter = require("../../lib/create-client-router-filter");
const _detectchangedentries = require("./detect-changed-entries");
const _constants = require("../../shared/lib/constants");
const _apppaths = require("../../shared/lib/router/utils/app-paths");
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
async function stitchBuilds({ config, distDir, shuttleDir, buildId, rewrites, redirects, allowedErrorRate, encryptionKey, edgePreviewProps }, entries) {
    var _entries_unchanged, _entries_changed, _entries_changed1;
    if (!await (0, _detectchangedentries.hasShuttle)(config, shuttleDir)) {
        // no shuttle directory nothing to stitch
        return {};
    }
    // if a manifest is needed in the rest of the build
    // we return it from here so it can be used without
    // re-reading from disk after changing
    const updatedManifests = {};
    // we need to copy the chunks from the shuttle folder
    // to the distDir (we copy all server split chunks currently)
    await (0, _recursivecopy.recursiveCopy)(_path.default.join(shuttleDir, 'server'), _path.default.join(distDir, 'server'), {
        filter (item) {
            // we copy page chunks separately to not copy stale entries
            return !item.match(/^[/\\](pages|app)[/\\]/);
        },
        overwrite: true
    });
    // copy static chunks (this includes stale chunks but won't be loaded)
    // unless referenced
    await (0, _recursivecopy.recursiveCopy)(_path.default.join(shuttleDir, 'static'), _path.default.join(distDir, 'static'), {
        overwrite: true
    });
    async function copyPageChunk(entry, type) {
        // copy entry chunk and flight manifest stuff
        // TODO: copy .map files?
        const entryFile = _path.default.join('server', type, `${entry}.js`);
        await _fs.default.promises.mkdir(_path.default.join(distDir, _path.default.dirname(entryFile)), {
            recursive: true
        });
        await _fs.default.promises.copyFile(_path.default.join(shuttleDir, entryFile + '.nft.json'), _path.default.join(distDir, entryFile + '.nft.json'));
        if (type === 'app' && !entry.endsWith('/route')) {
            const clientRefManifestFile = _path.default.join('server', type, `${entry}_${_constants.CLIENT_REFERENCE_MANIFEST}.js`);
            await _fs.default.promises.copyFile(_path.default.join(shuttleDir, clientRefManifestFile), _path.default.join(distDir, clientRefManifestFile));
        }
        await _fs.default.promises.copyFile(_path.default.join(shuttleDir, entryFile), _path.default.join(distDir, entryFile));
    }
    const copySema = new _asyncsema.Sema(8);
    // restore unchanged entries avoiding copying stale
    // entries from the shuttle/previous build
    for (const { type, curEntries } of [
        {
            type: 'app',
            curEntries: entries.unchanged.app
        },
        {
            type: 'pages',
            curEntries: entries.unchanged.pages
        }
    ]){
        await Promise.all(curEntries.map(async (entry)=>{
            try {
                await copySema.acquire();
                let normalizedEntry = (0, _entries.getPageFromPath)(entry, entries.pageExtensions);
                if (normalizedEntry === '/') {
                    normalizedEntry = '/index';
                }
                await copyPageChunk(normalizedEntry, type);
            } finally{
                copySema.release();
            }
        }));
    }
    // always attempt copying not-found chunk
    await copyPageChunk('/_not-found/page', 'app').catch(()=>{});
    // merge dynamic/static routes in routes-manifest
    const [restoreRoutesManifest, currentRoutesManifest] = await Promise.all([
        _path.default.join(shuttleDir, 'manifests', _constants.ROUTES_MANIFEST),
        _path.default.join(distDir, _constants.ROUTES_MANIFEST)
    ].map(async (f)=>JSON.parse(await _fs.default.promises.readFile(f, 'utf8'))));
    const dynamicRouteMap = {};
    const combinedDynamicRoutes = [
        ...currentRoutesManifest.dynamicRoutes,
        ...restoreRoutesManifest.dynamicRoutes
    ];
    for (const route of combinedDynamicRoutes){
        dynamicRouteMap[route.page] = route;
    }
    const mergedRoutesManifest = {
        ...currentRoutesManifest,
        dynamicRoutes: (0, _utils.getSortedRoutes)(combinedDynamicRoutes.map((item)=>item.page)).map((page)=>dynamicRouteMap[page]),
        staticRoutes: [
            ...currentRoutesManifest.staticRoutes,
            ...restoreRoutesManifest.staticRoutes
        ]
    };
    await _fs.default.promises.writeFile(_path.default.join(distDir, _constants.ROUTES_MANIFEST), JSON.stringify(mergedRoutesManifest, null, 2));
    // for build-manifest we use latest runtime files
    // and only merge previous page chunk entries
    // middleware-build-manifest.js (needs to be regenerated)
    const [restoreBuildManifest, currentBuildManifest] = await Promise.all([
        _path.default.join(shuttleDir, 'manifests', _constants.BUILD_MANIFEST),
        _path.default.join(distDir, _constants.BUILD_MANIFEST)
    ].map(async (file)=>JSON.parse(await _fs.default.promises.readFile(file, 'utf8'))));
    const mergedBuildManifest = {
        // we want to re-use original runtime
        // chunks so we favor restored version
        // over new
        ...currentBuildManifest,
        pages: {
            ...restoreBuildManifest.pages,
            ...currentBuildManifest.pages
        }
    };
    // _app and _error is unique per runtime
    // so nest under each specific entry in build-manifest
    const internalEntries = [
        '/_error',
        '/_app'
    ];
    for (const entry of Object.keys(restoreBuildManifest.pages)){
        if (currentBuildManifest.pages[entry]) {
            continue;
        }
        for (const internalEntry of internalEntries){
            for (const chunk of restoreBuildManifest.pages[internalEntry]){
                if (!restoreBuildManifest.pages[entry].includes(chunk)) {
                    mergedBuildManifest.pages[entry].unshift(chunk);
                }
            }
        }
    }
    for (const entry of Object.keys(currentBuildManifest.pages)){
        for (const internalEntry of internalEntries){
            for (const chunk of currentBuildManifest.pages[internalEntry]){
                if (!currentBuildManifest.pages[entry].includes(chunk)) {
                    mergedBuildManifest.pages[entry].unshift(chunk);
                }
            }
        }
    }
    for (const key of internalEntries){
        mergedBuildManifest.pages[key] = [];
    }
    for (const entry of entries.unchanged.app || []){
        const normalizedEntry = (0, _entries.getPageFromPath)(entry, entries.pageExtensions);
        mergedBuildManifest.rootMainFilesTree[normalizedEntry] = restoreBuildManifest.rootMainFilesTree[normalizedEntry] || restoreBuildManifest.rootMainFiles;
    }
    await _fs.default.promises.writeFile(_path.default.join(distDir, _constants.BUILD_MANIFEST), JSON.stringify(mergedBuildManifest, null, 2));
    await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', `${_constants.MIDDLEWARE_BUILD_MANIFEST}.js`), `self.__BUILD_MANIFEST=${JSON.stringify(mergedBuildManifest)}`);
    await _fs.default.promises.writeFile(_path.default.join(distDir, 'static', buildId, `_buildManifest.js`), `self.__BUILD_MANIFEST = ${(0, _buildmanifestplugin.generateClientManifest)(mergedBuildManifest, rewrites, (0, _createclientrouterfilter.createClientRouterFilter)([
        ...[
            // client filter always has all app paths
            ...((_entries_unchanged = entries.unchanged) == null ? void 0 : _entries_unchanged.app) || [],
            ...((_entries_changed = entries.changed) == null ? void 0 : _entries_changed.app) || []
        ].map((entry)=>(0, _apppaths.normalizeAppPath)((0, _entries.getPageFromPath)(entry, entries.pageExtensions))),
        ...(entries.unchanged.pages.length ? ((_entries_changed1 = entries.changed) == null ? void 0 : _entries_changed1.pages) || [] : []).map((item)=>(0, _entries.getPageFromPath)(item, entries.pageExtensions))
    ], redirects, allowedErrorRate))};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()`);
    // for react-loadable-manifest we just merge directly
    // prioritizing current manifest over previous,
    // middleware-react-loadable-manifest (needs to be regenerated)
    const [restoreLoadableManifest, currentLoadableManifest] = await Promise.all([
        _path.default.join(shuttleDir, 'manifests', _constants.REACT_LOADABLE_MANIFEST),
        _path.default.join(distDir, _constants.REACT_LOADABLE_MANIFEST)
    ].map(async (file)=>JSON.parse(await _fs.default.promises.readFile(file, 'utf8'))));
    const mergedLoadableManifest = {
        ...restoreLoadableManifest,
        ...currentLoadableManifest
    };
    await _fs.default.promises.writeFile(_path.default.join(distDir, _constants.REACT_LOADABLE_MANIFEST), JSON.stringify(mergedLoadableManifest, null, 2));
    await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', `${_constants.MIDDLEWARE_REACT_LOADABLE_MANIFEST}.js`), `self.__REACT_LOADABLE_MANIFEST=${JSON.stringify(JSON.stringify(mergedLoadableManifest))}`);
    // for server/middleware-manifest we just merge the functions
    // and middleware fields
    const [restoreMiddlewareManifest, currentMiddlewareManifest] = await Promise.all([
        _path.default.join(shuttleDir, 'server', _constants.MIDDLEWARE_MANIFEST),
        _path.default.join(distDir, 'server', _constants.MIDDLEWARE_MANIFEST)
    ].map(async (file)=>JSON.parse(await _fs.default.promises.readFile(file, 'utf8'))));
    const mergedMiddlewareManifest = {
        ...currentMiddlewareManifest,
        functions: {
            ...restoreMiddlewareManifest.functions,
            ...currentMiddlewareManifest.functions
        }
    };
    // update edge function env
    const updatedEdgeEnv = {
        __NEXT_BUILD_ID: buildId,
        NEXT_SERVER_ACTIONS_ENCRYPTION_KEY: encryptionKey,
        ...edgePreviewProps
    };
    if (mergedMiddlewareManifest.middleware['/']) {
        Object.assign(mergedMiddlewareManifest.middleware['/'].env, updatedEdgeEnv);
    }
    for (const key of Object.keys(mergedMiddlewareManifest.functions)){
        Object.assign(mergedMiddlewareManifest.functions[key].env, updatedEdgeEnv);
    }
    await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', _constants.MIDDLEWARE_MANIFEST), JSON.stringify(mergedMiddlewareManifest, null, 2));
    // for server/next-font-manifest we just merge nested
    // page/app fields and regenerate server/next-font-manifest.js
    const [restoreNextFontManifest, currentNextFontManifest] = await Promise.all([
        _path.default.join(shuttleDir, 'server', `${_constants.NEXT_FONT_MANIFEST}.json`),
        _path.default.join(distDir, 'server', `${_constants.NEXT_FONT_MANIFEST}.json`)
    ].map(async (file)=>JSON.parse(await _fs.default.promises.readFile(file, 'utf8'))));
    const mergedNextFontManifest = {
        ...currentNextFontManifest,
        pages: {
            ...restoreNextFontManifest.pages,
            ...currentNextFontManifest.pages
        },
        app: {
            ...restoreNextFontManifest.app,
            ...currentNextFontManifest.app
        }
    };
    await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', `${_constants.NEXT_FONT_MANIFEST}.json`), JSON.stringify(mergedNextFontManifest, null, 2));
    await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', `${_constants.NEXT_FONT_MANIFEST}.js`), `self.__NEXT_FONT_MANIFEST=${JSON.stringify(JSON.stringify(mergedNextFontManifest))}`);
    // for server/font-manifest.json we just merge the arrays
    for (const file of [
        _constants.AUTOMATIC_FONT_OPTIMIZATION_MANIFEST
    ]){
        const [restoreFontManifest, currentFontManifest] = await Promise.all([
            _path.default.join(shuttleDir, 'server', file),
            _path.default.join(distDir, 'server', file)
        ].map(async (f)=>JSON.parse(await _fs.default.promises.readFile(f, 'utf8'))));
        const mergedFontManifest = [
            ...restoreFontManifest,
            ...currentFontManifest
        ];
        await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', file), JSON.stringify(mergedFontManifest, null, 2));
    }
    // for server/functions-config-manifest.json we just merge
    // the functions field
    const [restoreFunctionsConfigManifest, currentFunctionsConfigManifest] = await Promise.all([
        _path.default.join(shuttleDir, 'server', _constants.FUNCTIONS_CONFIG_MANIFEST),
        _path.default.join(distDir, 'server', _constants.FUNCTIONS_CONFIG_MANIFEST)
    ].map(async (file)=>JSON.parse(await _fs.default.promises.readFile(file, 'utf8'))));
    const mergedFunctionsConfigManifest = {
        ...currentFunctionsConfigManifest,
        functions: {
            ...restoreFunctionsConfigManifest.functions,
            ...currentFunctionsConfigManifest.functions
        }
    };
    await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', _constants.FUNCTIONS_CONFIG_MANIFEST), JSON.stringify(mergedFunctionsConfigManifest, null, 2));
    for (const file of [
        _constants.APP_BUILD_MANIFEST,
        _constants.APP_PATH_ROUTES_MANIFEST
    ]){
        const [restorePagesManifest, currentPagesManifest] = await Promise.all([
            _path.default.join(shuttleDir, 'manifests', file),
            _path.default.join(distDir, file)
        ].map(async (f)=>JSON.parse(await _fs.default.promises.readFile(f, 'utf8'))));
        const mergedPagesManifest = {
            ...restorePagesManifest,
            ...currentPagesManifest,
            ...file === _constants.APP_BUILD_MANIFEST ? {
                pages: {
                    ...restorePagesManifest.pages,
                    ...currentPagesManifest.pages
                }
            } : {}
        };
        await _fs.default.promises.writeFile(_path.default.join(distDir, file), JSON.stringify(mergedPagesManifest, null, 2));
    }
    for (const file of [
        _constants.PAGES_MANIFEST,
        _constants.APP_PATHS_MANIFEST
    ]){
        const [restoreAppManifest, currentAppManifest] = await Promise.all([
            _path.default.join(shuttleDir, 'server', file),
            _path.default.join(distDir, 'server', file)
        ].map(async (f)=>JSON.parse(await _fs.default.promises.readFile(f, 'utf8'))));
        const mergedManifest = {
            ...restoreAppManifest,
            ...currentAppManifest
        };
        await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', file), JSON.stringify(mergedManifest, null, 2));
        if (file === _constants.PAGES_MANIFEST) {
            updatedManifests.pagesManifest = mergedManifest;
        }
    }
    // for server/server-reference-manifest.json we merge
    // and regenerate server/server-reference-manifest.js
    const [restoreServerRefManifest, currentServerRefManifest] = await Promise.all([
        _path.default.join(shuttleDir, 'server', `${_constants.SERVER_REFERENCE_MANIFEST}.json`),
        _path.default.join(distDir, 'server', `${_constants.SERVER_REFERENCE_MANIFEST}.json`)
    ].map(async (file)=>JSON.parse(await _fs.default.promises.readFile(file, 'utf8'))));
    const mergedServerRefManifest = {
        ...currentServerRefManifest,
        node: {
            ...restoreServerRefManifest.node,
            ...currentServerRefManifest.node
        },
        edge: {
            ...restoreServerRefManifest.edge,
            ...currentServerRefManifest.edge
        }
    };
    await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', `${_constants.SERVER_REFERENCE_MANIFEST}.json`), JSON.stringify(mergedServerRefManifest, null, 2));
    await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', `${_constants.SERVER_REFERENCE_MANIFEST}.js`), `self.__RSC_SERVER_MANIFEST=${JSON.stringify(JSON.stringify(mergedServerRefManifest))}`);
    // TODO: inline env variables post build by find/replace
    // in all the chunks for NEXT_PUBLIC_?
    return updatedManifests;
}

//# sourceMappingURL=stitch-builds.js.map