"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_BUILD_ID = exports.ASSETS_DIR = exports.IMAGE_LAMBDA_CODE_DIR = exports.API_LAMBDA_CODE_DIR = exports.DEFAULT_LAMBDA_CODE_DIR = void 0;
const nft_1 = require("@vercel/nft");
const execa_1 = __importDefault(require("execa"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importStar(require("path"));
const getAllFilesInDirectory_1 = __importDefault(require("./lib/getAllFilesInDirectory"));
const sortedRoutes_1 = require("./lib/sortedRoutes");
const isDynamicRoute_1 = require("./lib/isDynamicRoute");
const pathToPosix_1 = __importDefault(require("./lib/pathToPosix"));
const expressifyDynamicRoute_1 = require("./lib/expressifyDynamicRoute");
const pathToRegexStr_1 = __importDefault(require("./lib/pathToRegexStr"));
const normalizeNodeModules_1 = __importDefault(require("./lib/normalizeNodeModules"));
const createServerlessConfig_1 = __importDefault(require("./lib/createServerlessConfig"));
const redirector_1 = require("./routing/redirector");
const readDirectoryFiles_1 = __importDefault(require("./lib/readDirectoryFiles"));
const filterOutDirectories_1 = __importDefault(require("./lib/filterOutDirectories"));
const invalidationUrlGroup_1 = require("./lib/invalidation/invalidationUrlGroup");
const fs_1 = __importDefault(require("fs"));
const lodash_1 = require("lodash");
const permanentStaticPages_1 = require("./lib/permanentStaticPages");
exports.DEFAULT_LAMBDA_CODE_DIR = "default-lambda";
exports.API_LAMBDA_CODE_DIR = "api-lambda";
exports.IMAGE_LAMBDA_CODE_DIR = "image-lambda";
exports.ASSETS_DIR = "assets";
exports.DEFAULT_BUILD_ID = "shared-storage";
const defaultBuildOptions = {
    args: [],
    cwd: process.cwd(),
    env: {},
    cmd: "./node_modules/.bin/next",
    useServerlessTraceTarget: false,
    logLambdaExecutionTimes: false,
    domainRedirects: {},
    minifyHandlers: false,
    enableHTTPCompression: true,
    authentication: undefined,
    resolve: undefined,
    baseDir: process.cwd(),
    distributionId: "",
    urlRewrites: [],
    enableDebugMode: false,
    invalidationUrlGroups: [],
    notFoundPageMark: undefined,
    permanentStaticPages: undefined,
    sentry: undefined,
    abTests: undefined,
    enableRemoteInvalidation: false
};
class Builder {
    constructor(nextConfigDir, outputDir, buildOptions, nextStaticDir) {
        this.buildOptions = defaultBuildOptions;
        this.nextConfigDir = path_1.default.resolve(nextConfigDir);
        this.nextStaticDir = path_1.default.resolve(nextStaticDir !== null && nextStaticDir !== void 0 ? nextStaticDir : nextConfigDir);
        this.dotNextDir = path_1.default.join(this.nextConfigDir, ".next");
        this.serverlessDir = path_1.default.join(this.dotNextDir, "serverless");
        this.outputDir = outputDir;
        if (buildOptions) {
            this.buildOptions = buildOptions;
        }
    }
    readPublicFiles() {
        return __awaiter(this, void 0, void 0, function* () {
            const dirExists = yield fs_extra_1.default.pathExists(path_1.join(this.nextConfigDir, "public"));
            if (dirExists) {
                return getAllFilesInDirectory_1.default(path_1.join(this.nextConfigDir, "public"))
                    .map((e) => e.replace(this.nextConfigDir, ""))
                    .map((e) => e.split(path_1.default.sep).slice(2).join("/"));
            }
            else {
                return [];
            }
        });
    }
    readPagesManifest() {
        return __awaiter(this, void 0, void 0, function* () {
            const path = path_1.join(this.serverlessDir, "pages-manifest.json");
            const hasServerlessPageManifest = yield fs_extra_1.default.pathExists(path);
            if (!hasServerlessPageManifest) {
                return Promise.reject("pages-manifest not found. Check if `next.config.js` target is set to 'serverless'");
            }
            const pagesManifest = yield fs_extra_1.default.readJSON(path);
            const pagesManifestWithoutDynamicRoutes = Object.keys(pagesManifest).reduce((acc, route) => {
                if (isDynamicRoute_1.isDynamicRoute(route)) {
                    return acc;
                }
                acc[route] = pagesManifest[route];
                return acc;
            }, {});
            const dynamicRoutedPages = Object.keys(pagesManifest).filter(isDynamicRoute_1.isDynamicRoute);
            const sortedDynamicRoutedPages = sortedRoutes_1.getSortedRoutes(dynamicRoutedPages);
            const sortedPagesManifest = pagesManifestWithoutDynamicRoutes;
            sortedDynamicRoutedPages.forEach((route) => {
                sortedPagesManifest[route] = pagesManifest[route];
            });
            return sortedPagesManifest;
        });
    }
    copyLambdaHandlerDependencies(fileList, reasons, handlerDirectory, base) {
        return fileList
            .filter((file) => {
            if (file.endsWith(".ts") || file.endsWith(".tsx")) {
                return false;
            }
            const reason = reasons.get(file);
            return ((!reason || reason.type !== "initial") && file !== "package.json");
        })
            .map((filePath) => {
            const resolvedFilePath = path_1.default.resolve(path_1.join(base, filePath));
            const dst = normalizeNodeModules_1.default(path_1.default.relative(this.serverlessDir, resolvedFilePath));
            if (resolvedFilePath !== path_1.join(this.outputDir, handlerDirectory, dst)) {
                return fs_extra_1.default.copy(resolvedFilePath, path_1.join(this.outputDir, handlerDirectory, dst));
            }
            else {
                return Promise.resolve();
            }
        });
    }
    isPrerenderedJSFile(prerenderManifest, relativePageFile) {
        if (path_1.default.extname(relativePageFile) === ".js") {
            let pageRoute = relativePageFile.slice(0, -3);
            pageRoute = pageRoute.startsWith("/") ? pageRoute : `/${pageRoute}`;
            pageRoute = pageRoute === "/index" ? "/" : pageRoute;
            return (!!prerenderManifest.routes && !!prerenderManifest.routes[pageRoute]);
        }
        return false;
    }
    processAndCopyRoutesManifest(source, destination) {
        return __awaiter(this, void 0, void 0, function* () {
            const routesManifest = require(source);
            routesManifest.redirects = routesManifest.redirects.filter((redirect) => {
                return !redirector_1.isTrailingSlashRedirect(redirect, routesManifest.basePath);
            });
            yield fs_extra_1.default.writeFile(destination, JSON.stringify(routesManifest));
        });
    }
    processAndCopyHandler(handlerType, destination, shouldMinify) {
        return __awaiter(this, void 0, void 0, function* () {
            const source = require.resolve(`@getjerry/lambda-at-edge/dist/${handlerType}${shouldMinify ? ".min" : ""}.js`);
            yield fs_extra_1.default.copy(source, destination);
        });
    }
    buildDefaultLambda(buildManifest) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            let copyTraces = [];
            if (this.buildOptions.useServerlessTraceTarget) {
                const ignoreAppAndDocumentPages = (page) => {
                    const basename = path_1.default.basename(page);
                    return basename !== "_app.js" && basename !== "_document.js";
                };
                const allSsrPages = [
                    ...Object.values(buildManifest.pages.ssr.nonDynamic),
                    ...Object.values(buildManifest.pages.ssr.dynamic).map((entry) => entry.file)
                ].filter(ignoreAppAndDocumentPages);
                const ssrPages = Object.values(allSsrPages).map((pageFile) => path_1.default.join(this.serverlessDir, pageFile));
                const base = this.buildOptions.baseDir || process.cwd();
                const { fileList, reasons } = yield nft_1.nodeFileTrace(ssrPages, {
                    base,
                    resolve: this.buildOptions.resolve
                });
                copyTraces = this.copyLambdaHandlerDependencies(Array.from(fileList), reasons, exports.DEFAULT_LAMBDA_CODE_DIR, base);
            }
            const prerenderManifest = require(path_1.join(this.dotNextDir, "prerender-manifest.json"));
            const hasAPIRoutes = yield fs_extra_1.default.pathExists(path_1.join(this.serverlessDir, "pages/api"));
            return Promise.all([
                ...copyTraces,
                this.processAndCopyHandler("default-handler", path_1.join(this.outputDir, exports.DEFAULT_LAMBDA_CODE_DIR, "index.js"), !!this.buildOptions.minifyHandlers),
                ((_a = this.buildOptions) === null || _a === void 0 ? void 0 : _a.handler) ? fs_extra_1.default.copy(path_1.join(this.nextConfigDir, this.buildOptions.handler), path_1.join(this.outputDir, exports.DEFAULT_LAMBDA_CODE_DIR, this.buildOptions.handler))
                    : Promise.resolve(),
                fs_extra_1.default.writeJson(path_1.join(this.outputDir, exports.DEFAULT_LAMBDA_CODE_DIR, "manifest.json"), buildManifest),
                fs_extra_1.default.copy(path_1.join(this.serverlessDir, "pages"), path_1.join(this.outputDir, exports.DEFAULT_LAMBDA_CODE_DIR, "pages"), {
                    filter: (file) => {
                        const isIndexJS = pathToPosix_1.default(file).endsWith("/index.js");
                        if (isIndexJS) {
                            return true;
                        }
                        const isNotPrerenderedHTMLPage = path_1.default.extname(file) !== ".html";
                        const isNotStaticPropsJSONFile = path_1.default.extname(file) !== ".json";
                        const isNotApiPage = pathToPosix_1.default(file).indexOf("pages/api") === -1;
                        const isNotExcludedJSFile = hasAPIRoutes ||
                            !this.isPrerenderedJSFile(prerenderManifest, path_1.default.relative(path_1.join(this.serverlessDir, "pages"), file));
                        return (isNotApiPage &&
                            isNotPrerenderedHTMLPage &&
                            isNotStaticPropsJSONFile &&
                            isNotExcludedJSFile);
                    }
                }),
                this.copyChunks(exports.DEFAULT_LAMBDA_CODE_DIR),
                this.copyJSFiles(exports.DEFAULT_LAMBDA_CODE_DIR),
                fs_extra_1.default.copy(path_1.join(this.dotNextDir, "prerender-manifest.json"), path_1.join(this.outputDir, exports.DEFAULT_LAMBDA_CODE_DIR, "prerender-manifest.json")),
                fs_extra_1.default.copy(path_1.join(this.dotNextDir, "images-manifest.json"), path_1.join(this.outputDir, exports.DEFAULT_LAMBDA_CODE_DIR, "images-manifest.json")),
                this.processAndCopyRoutesManifest(path_1.join(this.dotNextDir, "routes-manifest.json"), path_1.join(this.outputDir, exports.DEFAULT_LAMBDA_CODE_DIR, "routes-manifest.json"))
            ]);
        });
    }
    buildApiLambda(apiBuildManifest) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            let copyTraces = [];
            if (this.buildOptions.useServerlessTraceTarget) {
                const allApiPages = [
                    ...Object.values(apiBuildManifest.apis.nonDynamic),
                    ...Object.values(apiBuildManifest.apis.dynamic).map((entry) => entry.file)
                ];
                const apiPages = Object.values(allApiPages).map((pageFile) => path_1.default.join(this.serverlessDir, pageFile));
                const base = this.buildOptions.baseDir || process.cwd();
                const { fileList, reasons } = yield nft_1.nodeFileTrace(apiPages, {
                    base,
                    resolve: this.buildOptions.resolve
                });
                copyTraces = this.copyLambdaHandlerDependencies(Array.from(fileList), reasons, exports.API_LAMBDA_CODE_DIR, base);
            }
            return Promise.all([
                ...copyTraces,
                this.processAndCopyHandler("api-handler", path_1.join(this.outputDir, exports.API_LAMBDA_CODE_DIR, "index.js"), !!this.buildOptions.minifyHandlers),
                ((_a = this.buildOptions) === null || _a === void 0 ? void 0 : _a.handler) ? fs_extra_1.default.copy(path_1.join(this.nextConfigDir, this.buildOptions.handler), path_1.join(this.outputDir, exports.API_LAMBDA_CODE_DIR, this.buildOptions.handler))
                    : Promise.resolve(),
                fs_extra_1.default.copy(path_1.join(this.serverlessDir, "pages/api"), path_1.join(this.outputDir, exports.API_LAMBDA_CODE_DIR, "pages/api")),
                this.copyChunks(exports.API_LAMBDA_CODE_DIR),
                this.copyJSFiles(exports.API_LAMBDA_CODE_DIR),
                fs_extra_1.default.writeJson(path_1.join(this.outputDir, exports.API_LAMBDA_CODE_DIR, "manifest.json"), apiBuildManifest),
                this.processAndCopyRoutesManifest(path_1.join(this.dotNextDir, "routes-manifest.json"), path_1.join(this.outputDir, exports.API_LAMBDA_CODE_DIR, "routes-manifest.json"))
            ]);
        });
    }
    copyChunks(handlerDir) {
        return !this.buildOptions.useServerlessTraceTarget &&
            fs_extra_1.default.existsSync(path_1.join(this.serverlessDir, "chunks"))
            ? fs_extra_1.default.copy(path_1.join(this.serverlessDir, "chunks"), path_1.join(this.outputDir, handlerDir, "chunks"))
            : Promise.resolve();
    }
    copyJSFiles(handlerDir) {
        return __awaiter(this, void 0, void 0, function* () {
            yield Promise.all([
                (yield fs_extra_1.default.pathExists(path_1.join(this.serverlessDir, "webpack-api-runtime.js")))
                    ? fs_extra_1.default.copy(path_1.join(this.serverlessDir, "webpack-api-runtime.js"), path_1.join(this.outputDir, handlerDir, "webpack-api-runtime.js"))
                    : Promise.resolve(),
                (yield fs_extra_1.default.pathExists(path_1.join(this.serverlessDir, "webpack-runtime.js")))
                    ? fs_extra_1.default.copy(path_1.join(this.serverlessDir, "webpack-runtime.js"), path_1.join(this.outputDir, handlerDir, "webpack-runtime.js"))
                    : Promise.resolve()
            ]);
        });
    }
    buildImageLambda(buildManifest) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            return Promise.all([
                this.processAndCopyHandler("image-handler", path_1.join(this.outputDir, exports.IMAGE_LAMBDA_CODE_DIR, "index.js"), !!this.buildOptions.minifyHandlers),
                ((_a = this.buildOptions) === null || _a === void 0 ? void 0 : _a.handler) ? fs_extra_1.default.copy(path_1.join(this.nextConfigDir, this.buildOptions.handler), path_1.join(this.outputDir, exports.IMAGE_LAMBDA_CODE_DIR, this.buildOptions.handler))
                    : Promise.resolve(),
                fs_extra_1.default.writeJson(path_1.join(this.outputDir, exports.IMAGE_LAMBDA_CODE_DIR, "manifest.json"), buildManifest),
                this.processAndCopyRoutesManifest(path_1.join(this.dotNextDir, "routes-manifest.json"), path_1.join(this.outputDir, exports.IMAGE_LAMBDA_CODE_DIR, "routes-manifest.json")),
                fs_extra_1.default.copy(path_1.join(path_1.default.dirname(require.resolve("@getjerry/lambda-at-edge/package.json")), "dist", "sharp_node_modules"), path_1.join(this.outputDir, exports.IMAGE_LAMBDA_CODE_DIR, "node_modules")),
                fs_extra_1.default.copy(path_1.join(this.dotNextDir, "images-manifest.json"), path_1.join(this.outputDir, exports.IMAGE_LAMBDA_CODE_DIR, "images-manifest.json"))
            ]);
        });
    }
    prepareBuildManifests() {
        var _a, _b;
        return __awaiter(this, void 0, void 0, function* () {
            const pagesManifest = yield this.readPagesManifest();
            const { logLambdaExecutionTimes = false, domainRedirects = {}, enableHTTPCompression = false, authentication = undefined } = this.buildOptions;
            this.normalizeDomainRedirects(domainRedirects);
            const defaultInvalidationGroupNumber = 1;
            const defaultBuildManifest = {
                buildId: exports.DEFAULT_BUILD_ID,
                logLambdaExecutionTimes,
                pages: {
                    ssr: {
                        dynamic: {},
                        nonDynamic: {}
                    },
                    html: {
                        dynamic: {},
                        nonDynamic: {}
                    }
                },
                publicFiles: {},
                trailingSlash: false,
                domainRedirects: domainRedirects,
                authentication: authentication,
                canonicalHostname: this.buildOptions.canonicalHostname,
                distributionId: this.buildOptions.distributionId,
                enableHTTPCompression,
                urlRewrites: this.buildOptions.urlRewrites,
                enableDebugMode: this.buildOptions.enableDebugMode,
                invalidationUrlGroups: (_a = this.buildOptions.invalidationUrlGroups) === null || _a === void 0 ? void 0 : _a.map((group) => {
                    return Object.assign(Object.assign({}, group), { maxAccessNumber: this.buildOptions.enableDebugMode
                            ? defaultInvalidationGroupNumber
                            : group.maxAccessNumber });
                }),
                notFoundPageMark: this.buildOptions.notFoundPageMark,
                permanentStaticPages: this.buildOptions.permanentStaticPages,
                sentry: this.buildOptions.sentry,
                abTests: this.buildOptions.abTests,
                enableRemoteInvalidation: this.buildOptions.enableRemoteInvalidation
            };
            const apiBuildManifest = {
                apis: {
                    dynamic: {},
                    nonDynamic: {}
                },
                domainRedirects: domainRedirects,
                authentication: authentication,
                enableHTTPCompression
            };
            const ssrPages = defaultBuildManifest.pages.ssr;
            const htmlPages = defaultBuildManifest.pages.html;
            const apiPages = apiBuildManifest.apis;
            const isHtmlPage = (path) => path.endsWith(".html");
            const isApiPage = (path) => path.startsWith("pages/api");
            Object.entries(pagesManifest).forEach(([route, pageFile]) => {
                const isOptionalCatchAllDynamicRoute = isDynamicRoute_1.isOptionalCatchAllRoute(route);
                const isOtherDynamicRoute = !isOptionalCatchAllDynamicRoute && isDynamicRoute_1.isDynamicRoute(route);
                let expressRoute = "";
                let optionalBaseRoute = "";
                if (isOtherDynamicRoute) {
                    expressRoute = expressifyDynamicRoute_1.expressifyDynamicRoute(route);
                }
                else if (isOptionalCatchAllDynamicRoute) {
                    expressRoute = expressifyDynamicRoute_1.expressifyOptionalCatchAllDynamicRoute(route);
                    optionalBaseRoute = route.split("/[[")[0];
                    optionalBaseRoute = optionalBaseRoute === "" ? "/" : optionalBaseRoute;
                }
                if (isHtmlPage(pageFile)) {
                    if (isOtherDynamicRoute) {
                        const route = expressRoute;
                        htmlPages.dynamic[route] = {
                            file: pageFile,
                            regex: pathToRegexStr_1.default(route)
                        };
                    }
                    else if (isOptionalCatchAllDynamicRoute) {
                        const route = expressRoute;
                        htmlPages.dynamic[route] = {
                            file: pageFile,
                            regex: pathToRegexStr_1.default(route)
                        };
                        htmlPages.nonDynamic[optionalBaseRoute] = pageFile;
                    }
                    else {
                        htmlPages.nonDynamic[route] = pageFile;
                    }
                }
                else if (isApiPage(pageFile)) {
                    if (isOtherDynamicRoute) {
                        const route = expressRoute;
                        apiPages.dynamic[route] = {
                            file: pageFile,
                            regex: pathToRegexStr_1.default(route)
                        };
                    }
                    else if (isOptionalCatchAllDynamicRoute) {
                        const route = expressRoute;
                        apiPages.dynamic[route] = {
                            file: pageFile,
                            regex: pathToRegexStr_1.default(route)
                        };
                        apiPages.nonDynamic[optionalBaseRoute] = pageFile;
                    }
                    else {
                        apiPages.nonDynamic[route] = pageFile;
                    }
                }
                else if (isOtherDynamicRoute) {
                    const route = expressRoute;
                    ssrPages.dynamic[route] = {
                        file: pageFile,
                        regex: pathToRegexStr_1.default(route)
                    };
                }
                else if (isOptionalCatchAllDynamicRoute) {
                    const route = expressRoute;
                    ssrPages.dynamic[route] = {
                        file: pageFile,
                        regex: pathToRegexStr_1.default(route)
                    };
                    ssrPages.nonDynamic[optionalBaseRoute] = pageFile;
                }
                else {
                    ssrPages.nonDynamic[route] = pageFile;
                }
            });
            const publicFiles = yield this.readPublicFiles();
            publicFiles.forEach((pf) => {
                defaultBuildManifest.publicFiles["/" + pf] = pf;
            });
            const nextConfigPath = path_1.default.join(this.nextConfigDir, "next.config.js");
            if (yield fs_extra_1.default.pathExists(nextConfigPath)) {
                const nextConfig = yield require(nextConfigPath);
                let normalisedNextConfig;
                if (typeof nextConfig === "object") {
                    normalisedNextConfig = nextConfig;
                }
                else if (typeof nextConfig === "function") {
                    normalisedNextConfig = nextConfig("phase-production-server", {});
                }
                defaultBuildManifest.trailingSlash = (_b = normalisedNextConfig === null || normalisedNextConfig === void 0 ? void 0 : normalisedNextConfig.trailingSlash) !== null && _b !== void 0 ? _b : false;
            }
            const imageBuildManifest = {
                domainRedirects: domainRedirects,
                enableHTTPCompression: enableHTTPCompression
            };
            return {
                defaultBuildManifest,
                apiBuildManifest,
                imageBuildManifest
            };
        });
    }
    buildStaticAssets(defaultBuildManifest, routesManifest) {
        return __awaiter(this, void 0, void 0, function* () {
            const basePath = routesManifest.basePath;
            const nextConfigDir = this.nextConfigDir;
            const nextStaticDir = this.nextStaticDir;
            const dotNextDirectory = path_1.default.join(this.nextConfigDir, ".next");
            const assetOutputDirectory = path_1.default.join(this.outputDir, exports.ASSETS_DIR);
            const normalizedBasePath = basePath ? basePath.slice(1) : "";
            const withBasePath = (key) => path_1.default.join(normalizedBasePath, key);
            const copyIfExists = (source, destination) => __awaiter(this, void 0, void 0, function* () {
                if (yield fs_extra_1.default.pathExists(source)) {
                    yield fs_extra_1.default.copy(source, destination);
                }
            });
            const copyBuildId = copyIfExists(path_1.default.join(dotNextDirectory, "BUILD_ID"), path_1.default.join(assetOutputDirectory, withBasePath("BUILD_ID")));
            console.log("----a11");
            const buildStaticFiles = yield readDirectoryFiles_1.default(path_1.default.join(dotNextDirectory, "static"));
            const staticFileAssets = buildStaticFiles
                .filter(filterOutDirectories_1.default)
                .map((fileItem) => __awaiter(this, void 0, void 0, function* () {
                const source = fileItem.path;
                const destination = path_1.default.join(assetOutputDirectory, withBasePath(path_1.default
                    .relative(path_1.default.resolve(nextConfigDir), source)
                    .replace(/^.next/, "_next")));
                return copyIfExists(source, destination);
            }));
            const pagesManifest = yield fs_extra_1.default.readJSON(path_1.default.join(dotNextDirectory, "serverless/pages-manifest.json"));
            const htmlPageAssets = Object.values(pagesManifest)
                .filter((pageFile) => pageFile.endsWith(".html"))
                .map((relativePageFilePath) => {
                const source = path_1.default.join(dotNextDirectory, `serverless/${relativePageFilePath}`);
                const destination = path_1.default.join(assetOutputDirectory, withBasePath(`static-pages/${exports.DEFAULT_BUILD_ID}/${relativePageFilePath.replace(/^pages\//, "")}`));
                return copyIfExists(source, destination);
            });
            const prerenderManifest = yield fs_extra_1.default.readJSON(path_1.default.join(dotNextDirectory, "prerender-manifest.json"));
            const prerenderManifestJSONPropFileAssets = Object.keys(prerenderManifest.routes).map((key) => {
                const source = path_1.default.join(dotNextDirectory, `serverless/pages/${key.endsWith("/") ? key + "index.json" : key + ".json"}`);
                const destination = path_1.default.join(assetOutputDirectory, withBasePath(prerenderManifest.routes[key].dataRoute.slice(1)));
                return copyIfExists(source, destination);
            });
            const prerenderManifestHTMLPageAssets = Object.keys(prerenderManifest.routes).map((key) => {
                const relativePageFilePath = key.endsWith("/")
                    ? path_1.default.join(key, "index.html")
                    : key + ".html";
                const source = path_1.default.join(dotNextDirectory, `serverless/pages/${relativePageFilePath}`);
                const destination = path_1.default.join(assetOutputDirectory, withBasePath(path_1.default.join("static-pages", exports.DEFAULT_BUILD_ID, relativePageFilePath)));
                return copyIfExists(source, destination);
            });
            const fallbackHTMLPageAssets = Object.values(prerenderManifest.dynamicRoutes || {})
                .filter(({ fallback }) => {
                return !!fallback;
            })
                .map((routeConfig) => {
                const fallback = routeConfig.fallback;
                const source = path_1.default.join(dotNextDirectory, `serverless/pages/${fallback}`);
                const destination = path_1.default.join(assetOutputDirectory, withBasePath(path_1.default.join("static-pages", exports.DEFAULT_BUILD_ID, fallback)));
                return copyIfExists(source, destination);
            });
            if (yield fs_extra_1.default.pathExists(path_1.default.join(nextStaticDir, "public", "static"))) {
                throw new Error("You cannot have assets in the directory [public/static] as they conflict with the static/* CloudFront cache behavior. Please move these assets into another directory.");
            }
            const buildPublicOrStaticDirectory = (directory) => __awaiter(this, void 0, void 0, function* () {
                const directoryPath = path_1.default.join(nextStaticDir, directory);
                if (!(yield fs_extra_1.default.pathExists(directoryPath))) {
                    return Promise.resolve([]);
                }
                const files = yield readDirectoryFiles_1.default(directoryPath);
                return files.filter(filterOutDirectories_1.default).map((fileItem) => {
                    const source = fileItem.path;
                    const destination = path_1.default.join(assetOutputDirectory, withBasePath(path_1.default.relative(path_1.default.resolve(nextStaticDir), fileItem.path)));
                    return fs_extra_1.default.copy(source, destination);
                });
            });
            const publicDirAssets = yield buildPublicOrStaticDirectory("public");
            const staticDirAssets = yield buildPublicOrStaticDirectory("static");
            return Promise.all([
                copyBuildId,
                ...staticFileAssets,
                ...htmlPageAssets,
                ...prerenderManifestJSONPropFileAssets,
                ...prerenderManifestHTMLPageAssets,
                ...fallbackHTMLPageAssets,
                ...publicDirAssets,
                ...staticDirAssets
            ]);
        });
    }
    cleanupDotNext() {
        return __awaiter(this, void 0, void 0, function* () {
            const exists = yield fs_extra_1.default.pathExists(this.dotNextDir);
            if (exists) {
                const fileItems = yield fs_extra_1.default.readdir(this.dotNextDir);
                yield Promise.all(fileItems
                    .filter((fileItem) => fileItem !== "cache")
                    .map((fileItem) => fs_extra_1.default.remove(path_1.join(this.dotNextDir, fileItem))));
            }
        });
    }
    build(debugMode) {
        return __awaiter(this, void 0, void 0, function* () {
            const { cmd, args, cwd, env: inputEnv, useServerlessTraceTarget } = Object.assign(defaultBuildOptions, this.buildOptions);
            const env = Object.assign(Object.assign({}, process.env), inputEnv);
            yield this.cleanupDotNext();
            yield fs_extra_1.default.emptyDir(path_1.join(this.outputDir, exports.DEFAULT_LAMBDA_CODE_DIR));
            yield fs_extra_1.default.emptyDir(path_1.join(this.outputDir, exports.API_LAMBDA_CODE_DIR));
            yield fs_extra_1.default.emptyDir(path_1.join(this.outputDir, exports.IMAGE_LAMBDA_CODE_DIR));
            yield fs_extra_1.default.emptyDir(path_1.join(this.outputDir, exports.ASSETS_DIR));
            const { restoreUserConfig } = yield createServerlessConfig_1.default(cwd, path_1.default.join(this.nextConfigDir), useServerlessTraceTarget);
            try {
                const subprocess = execa_1.default(cmd, args, {
                    cwd,
                    env: env
                });
                if (debugMode) {
                    subprocess.stdout.pipe(process.stdout);
                }
                yield subprocess;
            }
            finally {
                yield restoreUserConfig();
            }
            const { defaultBuildManifest, apiBuildManifest, imageBuildManifest } = yield this.prepareBuildManifests();
            yield this.buildDefaultLambda(defaultBuildManifest);
            const hasAPIPages = Object.keys(apiBuildManifest.apis.nonDynamic).length > 0 ||
                Object.keys(apiBuildManifest.apis.dynamic).length > 0;
            if (hasAPIPages) {
                yield this.buildApiLambda(apiBuildManifest);
            }
            const hasImageOptimizer = fs_extra_1.default.existsSync(path_1.join(this.dotNextDir, "images-manifest.json"));
            if (hasImageOptimizer) {
                yield this.buildImageLambda(imageBuildManifest);
            }
            const routesManifest = require(path_1.join(this.dotNextDir, "routes-manifest.json"));
            yield this.buildStaticAssets(defaultBuildManifest, routesManifest);
            const hasDynamicDataAssets = !lodash_1.isEmpty(defaultBuildManifest.invalidationUrlGroups);
            if (hasDynamicDataAssets) {
                yield this.buildDynamicDataAssets(defaultBuildManifest, routesManifest);
            }
            const hasPermanentStaticPages = !lodash_1.isEmpty(defaultBuildManifest.permanentStaticPages);
            if (hasPermanentStaticPages) {
                yield this.buildPermanentStaticPages(defaultBuildManifest, routesManifest);
            }
        });
    }
    normalizeDomainRedirects(domainRedirects) {
        for (const key in domainRedirects) {
            const destination = domainRedirects[key];
            let url;
            try {
                url = new URL(destination);
            }
            catch (error) {
                throw new Error(`domainRedirects: ${destination} is invalid. The URL is not in a valid URL format.`);
            }
            const { origin, pathname, searchParams } = url;
            if (!origin.startsWith("https://") && !origin.startsWith("http://")) {
                throw new Error(`domainRedirects: ${destination} is invalid. The URL must start with http:// or https://.`);
            }
            if (Array.from(searchParams).length > 0) {
                throw new Error(`domainRedirects: ${destination} is invalid. The URL must not contain query parameters.`);
            }
            let normalizedDomain = `${origin}${pathname}`;
            normalizedDomain = normalizedDomain.endsWith("/")
                ? normalizedDomain.slice(0, -1)
                : normalizedDomain;
            domainRedirects[key] = normalizedDomain;
        }
    }
    buildDynamicDataAssets(defaultBuildManifest, routesManifest) {
        return __awaiter(this, void 0, void 0, function* () {
            const basePath = routesManifest.basePath;
            const normalizedBasePath = basePath ? basePath.slice(1) : "";
            const directoryPath = path_1.default.join(this.outputDir, exports.ASSETS_DIR, normalizedBasePath, "_next", "data", exports.DEFAULT_BUILD_ID, invalidationUrlGroup_1.INVALIDATION_DATA_DIR);
            if (!fs_1.default.existsSync(directoryPath)) {
                fs_1.default.mkdirSync(directoryPath, { recursive: true });
            }
            const initAccessNumber = 0;
            lodash_1.map(defaultBuildManifest.invalidationUrlGroups || [], (group) => __awaiter(this, void 0, void 0, function* () {
                yield fs_extra_1.default.writeFile(path_1.join(directoryPath, invalidationUrlGroup_1.getGroupFilename(group)), JSON.stringify(Object.assign(Object.assign({}, group), { currentNumber: initAccessNumber })));
            }));
        });
    }
    buildPermanentStaticPages(defaultBuildManifest, routesManifest) {
        return __awaiter(this, void 0, void 0, function* () {
            if (lodash_1.isEmpty(defaultBuildManifest.permanentStaticPages)) {
                return;
            }
            const basePath = routesManifest.basePath;
            const normalizedBasePath = basePath ? basePath.slice(1) : "";
            const copyIfExists = (source, destination) => __awaiter(this, void 0, void 0, function* () {
                if (yield fs_extra_1.default.pathExists(source)) {
                    yield fs_extra_1.default.copy(source, destination);
                }
            });
            const sourcePath = path_1.default.join(this.outputDir, exports.ASSETS_DIR, normalizedBasePath, "static-pages", exports.DEFAULT_BUILD_ID);
            const directoryPath = path_1.default.join(sourcePath, permanentStaticPages_1.PERMANENT_STATIC_PAGES_DIR);
            if (!fs_1.default.existsSync(directoryPath)) {
                fs_1.default.mkdirSync(directoryPath, { recursive: true });
            }
            lodash_1.forEach(defaultBuildManifest.permanentStaticPages, (page) => {
                const source = path_1.default.join(sourcePath, page);
                const destination = path_1.default.join(directoryPath, page);
                return copyIfExists(source, destination);
            });
        });
    }
}
exports.default = Builder;
