UNPKG

firebase-tools

Version:
491 lines (490 loc) 26 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getDevModeHandle = exports.ɵcodegenFunctionsDirectory = exports.ɵcodegenPublicDirectory = exports.init = exports.build = exports.discover = exports.docsUrl = exports.type = exports.support = exports.name = exports.supportedRange = void 0; const child_process_1 = require("child_process"); const cross_spawn_1 = require("cross-spawn"); const promises_1 = require("fs/promises"); const path_1 = require("path"); const fs_extra_1 = require("fs-extra"); const url_1 = require("url"); const semver_1 = require("semver"); const clc = require("colorette"); const stream_chain_1 = require("stream-chain"); const stream_json_1 = require("stream-json"); const Pick_1 = require("stream-json/filters/Pick"); const StreamObject_1 = require("stream-json/streamers/StreamObject"); const fsutils_1 = require("../../fsutils"); const prompt_1 = require("../../prompt"); const error_1 = require("../../error"); const utils_1 = require("../utils"); const utils_2 = require("./utils"); const constants_1 = require("../constants"); const constants_2 = require("./constants"); const api_1 = require("../../hosting/api"); const logger_1 = require("../../logger"); const env_1 = require("../../functions/env"); const DEFAULT_BUILD_SCRIPT = ["next build"]; const PUBLIC_DIR = "public"; exports.supportedRange = "12 - 15.0"; exports.name = "Next.js"; exports.support = "preview"; exports.type = 2; exports.docsUrl = "https://firebase.google.com/docs/hosting/frameworks/nextjs"; const DEFAULT_NUMBER_OF_REASONS_TO_LIST = 5; function getReactVersion(cwd) { var _a; return (_a = (0, utils_1.findDependency)("react-dom", { cwd, omitDev: false })) === null || _a === void 0 ? void 0 : _a.version; } async function discover(dir) { if (!(await (0, fs_extra_1.pathExists)((0, path_1.join)(dir, "package.json")))) return; const version = (0, utils_2.getNextVersion)(dir); if (!(await (0, utils_2.whichNextConfigFile)(dir)) && !version) return; return { mayWantBackend: true, publicDirectory: (0, path_1.join)(dir, PUBLIC_DIR), version }; } exports.discover = discover; async function build(dir, target, context) { var _a, _b; await (0, utils_1.warnIfCustomBuildScript)(dir, exports.name, DEFAULT_BUILD_SCRIPT); const reactVersion = getReactVersion(dir); if (reactVersion && (0, semver_1.gte)(reactVersion, "18.0.0")) { process.env.__NEXT_REACT_ROOT = "true"; } let env = Object.assign({}, process.env); if (context === null || context === void 0 ? void 0 : context.projectId) { const projectEnvPath = (0, path_1.join)(dir, `.env.${context.projectId}`); if (await (0, fs_extra_1.pathExists)(projectEnvPath)) { const projectEnvVars = (0, env_1.parseStrict)((await (0, fs_extra_1.readFile)(projectEnvPath)).toString()); env = Object.assign(Object.assign({}, projectEnvVars), env); } } if ((context === null || context === void 0 ? void 0 : context.projectId) && (context === null || context === void 0 ? void 0 : context.site)) { const deploymentDomain = await (0, api_1.getDeploymentDomain)(context.projectId, context.site, context.hostingChannel); if (deploymentDomain) { env["VERCEL_URL"] = deploymentDomain; } } const cli = (0, utils_1.getNodeModuleBin)("next", dir); const nextBuild = new Promise((resolve, reject) => { var _a, _b; const buildProcess = (0, cross_spawn_1.spawn)(cli, ["build"], { cwd: dir, env }); (_a = buildProcess.stdout) === null || _a === void 0 ? void 0 : _a.on("data", (data) => logger_1.logger.info(data.toString())); (_b = buildProcess.stderr) === null || _b === void 0 ? void 0 : _b.on("data", (data) => logger_1.logger.info(data.toString())); buildProcess.on("error", (err) => { reject(new error_1.FirebaseError(`Unable to build your Next.js app: ${err}`)); }); buildProcess.on("exit", (code) => { resolve(code); }); }); await nextBuild; const reasonsForBackend = new Set(); const { distDir, trailingSlash, basePath: baseUrl } = await getConfig(dir); if (await (0, utils_2.isUsingMiddleware)((0, path_1.join)(dir, distDir), false)) { reasonsForBackend.add("middleware"); } if (await (0, utils_2.isUsingImageOptimization)(dir, distDir)) { reasonsForBackend.add(`Image Optimization`); } const prerenderManifest = await (0, utils_1.readJSON)((0, path_1.join)(dir, distDir, constants_2.PRERENDER_MANIFEST)); const dynamicRoutesWithFallback = Object.entries(prerenderManifest.dynamicRoutes || {}).filter(([, it]) => it.fallback !== false); if (dynamicRoutesWithFallback.length > 0) { for (const [key] of dynamicRoutesWithFallback) { reasonsForBackend.add(`use of fallback ${key}`); } } const routesWithRevalidate = Object.entries(prerenderManifest.routes).filter(([, it]) => it.initialRevalidateSeconds); if (routesWithRevalidate.length > 0) { for (const [, { srcRoute }] of routesWithRevalidate) { reasonsForBackend.add(`use of revalidate ${srcRoute}`); } } const pagesManifestJSON = await (0, utils_1.readJSON)((0, path_1.join)(dir, distDir, "server", constants_2.PAGES_MANIFEST)); const prerenderedRoutes = Object.keys(prerenderManifest.routes); const dynamicRoutes = Object.keys(prerenderManifest.dynamicRoutes); const unrenderedPages = (0, utils_2.getNonStaticRoutes)(pagesManifestJSON, prerenderedRoutes, dynamicRoutes); for (const key of unrenderedPages) { reasonsForBackend.add(`non-static route ${key}`); } const manifest = await (0, utils_1.readJSON)((0, path_1.join)(dir, distDir, constants_2.ROUTES_MANIFEST)); const { headers: nextJsHeaders = [], redirects: nextJsRedirects = [], rewrites: nextJsRewrites = [], i18n: nextjsI18n, } = manifest; const isEveryHeaderSupported = nextJsHeaders.map(utils_2.cleanI18n).every(utils_2.isHeaderSupportedByHosting); if (!isEveryHeaderSupported) { reasonsForBackend.add("advanced headers"); } const headers = nextJsHeaders .map(utils_2.cleanI18n) .filter(utils_2.isHeaderSupportedByHosting) .map(({ source, headers }) => ({ source: (0, utils_2.cleanEscapedChars)(source), headers, })); const [appPathsManifest, appPathRoutesManifest, serverReferenceManifest] = await Promise.all([ (0, utils_1.readJSON)((0, path_1.join)(dir, distDir, "server", constants_2.APP_PATHS_MANIFEST)).catch(() => undefined), (0, utils_1.readJSON)((0, path_1.join)(dir, distDir, constants_2.APP_PATH_ROUTES_MANIFEST)).catch(() => undefined), (0, utils_1.readJSON)((0, path_1.join)(dir, distDir, "server", constants_2.SERVER_REFERENCE_MANIFEST)).catch(() => undefined), ]); if (appPathRoutesManifest) { const { headers: headersFromMetaFiles, pprRoutes } = await (0, utils_2.getAppMetadataFromMetaFiles)(dir, distDir, baseUrl, appPathRoutesManifest); headers.push(...headersFromMetaFiles); for (const route of pprRoutes) { reasonsForBackend.add(`route with PPR ${route}`); } if (appPathsManifest) { const unrenderedServerComponents = (0, utils_2.getNonStaticServerComponents)(appPathsManifest, appPathRoutesManifest, prerenderedRoutes, dynamicRoutes); const notFoundPageKey = ["/_not-found", "/_not-found/page"].find((key) => unrenderedServerComponents.has(key)); if (notFoundPageKey && (await (0, utils_2.hasStaticAppNotFoundComponent)(dir, distDir))) { unrenderedServerComponents.delete(notFoundPageKey); } for (const key of unrenderedServerComponents) { reasonsForBackend.add(`non-static component ${key}`); } } if (serverReferenceManifest) { const routesWithServerAction = (0, utils_2.getRoutesWithServerAction)(serverReferenceManifest, appPathRoutesManifest); for (const key of routesWithServerAction) { reasonsForBackend.add(`route with server action ${key}`); } } } const isEveryRedirectSupported = nextJsRedirects .filter((it) => !it.internal) .every(utils_2.isRedirectSupportedByHosting); if (!isEveryRedirectSupported) { reasonsForBackend.add("advanced redirects"); } const redirects = nextJsRedirects .map(utils_2.cleanI18n) .filter(utils_2.isRedirectSupportedByHosting) .map(({ source, destination, statusCode: type }) => ({ source: (0, utils_2.cleanEscapedChars)(source), destination, type, })); const nextJsRewritesToUse = (0, utils_2.getNextjsRewritesToUse)(nextJsRewrites); if (!Array.isArray(nextJsRewrites) && (((_a = nextJsRewrites.afterFiles) === null || _a === void 0 ? void 0 : _a.length) || ((_b = nextJsRewrites.fallback) === null || _b === void 0 ? void 0 : _b.length))) { reasonsForBackend.add("advanced rewrites"); } const isEveryRewriteSupported = nextJsRewritesToUse.every(utils_2.isRewriteSupportedByHosting); if (!isEveryRewriteSupported) { reasonsForBackend.add("advanced rewrites"); } const rewrites = nextJsRewritesToUse .filter(utils_2.isRewriteSupportedByHosting) .map(utils_2.cleanI18n) .map(({ source, destination }) => ({ source: (0, utils_2.cleanEscapedChars)(source), destination, })); const wantsBackend = reasonsForBackend.size > 0; if (wantsBackend) { logger_1.logger.info("Building a Cloud Function to run this application. This is needed due to:"); for (const reason of Array.from(reasonsForBackend).slice(0, DEFAULT_NUMBER_OF_REASONS_TO_LIST)) { logger_1.logger.info(` • ${reason}`); } for (const reason of Array.from(reasonsForBackend).slice(DEFAULT_NUMBER_OF_REASONS_TO_LIST)) { logger_1.logger.debug(` • ${reason}`); } if (reasonsForBackend.size > DEFAULT_NUMBER_OF_REASONS_TO_LIST && !process.env.DEBUG) { logger_1.logger.info(` • and ${reasonsForBackend.size - DEFAULT_NUMBER_OF_REASONS_TO_LIST} other reasons, use --debug to see more`); } logger_1.logger.info(""); } const i18n = !!nextjsI18n; return { wantsBackend, headers, redirects, rewrites, trailingSlash, i18n, baseUrl, }; } exports.build = build; async function init(setup, config) { const language = await (0, prompt_1.select)({ default: "TypeScript", message: "What language would you like to use?", choices: [ { name: "JavaScript", value: "js" }, { name: "TypeScript", value: "ts" }, ], }); (0, child_process_1.execSync)(`npx --yes create-next-app@"${exports.supportedRange}" -e hello-world ` + `${setup.hosting.source} --use-npm --${language}`, { stdio: "inherit", cwd: config.projectDir }); } exports.init = init; async function ɵcodegenPublicDirectory(sourceDir, destDir, _, context) { const { distDir, i18n, basePath } = await getConfig(sourceDir); let matchingI18nDomain = undefined; if (i18n === null || i18n === void 0 ? void 0 : i18n.domains) { const siteDomains = await (0, api_1.getAllSiteDomains)(context.project, context.site); matchingI18nDomain = i18n.domains.find(({ domain }) => siteDomains.includes(domain)); } const singleLocaleDomain = !i18n || ((matchingI18nDomain || i18n).locales || []).length <= 1; const publicPath = (0, path_1.join)(sourceDir, "public"); await (0, promises_1.mkdir)((0, path_1.join)(destDir, basePath, "_next", "static"), { recursive: true }); if (await (0, fs_extra_1.pathExists)(publicPath)) { await (0, fs_extra_1.copy)(publicPath, (0, path_1.join)(destDir, basePath)); } await (0, fs_extra_1.copy)((0, path_1.join)(sourceDir, distDir, "static"), (0, path_1.join)(destDir, basePath, "_next", "static")); const [middlewareManifest, prerenderManifest, routesManifest, pagesManifest, appPathRoutesManifest, serverReferenceManifest,] = await Promise.all([ (0, utils_1.readJSON)((0, path_1.join)(sourceDir, distDir, "server", constants_2.MIDDLEWARE_MANIFEST)), (0, utils_1.readJSON)((0, path_1.join)(sourceDir, distDir, constants_2.PRERENDER_MANIFEST)), (0, utils_1.readJSON)((0, path_1.join)(sourceDir, distDir, constants_2.ROUTES_MANIFEST)), (0, utils_1.readJSON)((0, path_1.join)(sourceDir, distDir, "server", constants_2.PAGES_MANIFEST)), (0, utils_1.readJSON)((0, path_1.join)(sourceDir, distDir, constants_2.APP_PATH_ROUTES_MANIFEST)).catch(() => ({})), (0, utils_1.readJSON)((0, path_1.join)(sourceDir, distDir, "server", constants_2.SERVER_REFERENCE_MANIFEST)).catch(() => ({ node: {}, edge: {}, encryptionKey: "" })), ]); const appPathRoutesEntries = Object.entries(appPathRoutesManifest); const middlewareMatcherRegexes = (0, utils_2.getMiddlewareMatcherRegexes)(middlewareManifest); const { redirects = [], rewrites = [], headers = [] } = routesManifest; const rewritesRegexesNotSupportedByHosting = (0, utils_2.getNextjsRewritesToUse)(rewrites) .filter((rewrite) => !(0, utils_2.isRewriteSupportedByHosting)(rewrite)) .map(utils_2.cleanI18n) .map((rewrite) => new RegExp(rewrite.regex)); const redirectsRegexesNotSupportedByHosting = redirects .filter((it) => !it.internal) .filter((redirect) => !(0, utils_2.isRedirectSupportedByHosting)(redirect)) .map(utils_2.cleanI18n) .map((redirect) => new RegExp(redirect.regex)); const headersRegexesNotSupportedByHosting = headers .filter((header) => !(0, utils_2.isHeaderSupportedByHosting)(header)) .map((header) => new RegExp(header.regex)); const pathsUsingsFeaturesNotSupportedByHosting = [ ...middlewareMatcherRegexes, ...rewritesRegexesNotSupportedByHosting, ...redirectsRegexesNotSupportedByHosting, ...headersRegexesNotSupportedByHosting, ]; const staticRoutesUsingServerActions = (0, utils_2.getRoutesWithServerAction)(serverReferenceManifest, appPathRoutesManifest); const pagesManifestLikePrerender = Object.fromEntries(Object.entries(pagesManifest) .filter(([, srcRoute]) => srcRoute.endsWith(".html")) .map(([path]) => { return [ path, { srcRoute: null, initialRevalidateSeconds: false, dataRoute: "", experimentalPPR: false, prefetchDataRoute: "", }, ]; })); const routesToCopy = Object.assign(Object.assign({}, prerenderManifest.routes), pagesManifestLikePrerender); const { pprRoutes } = await (0, utils_2.getAppMetadataFromMetaFiles)(sourceDir, distDir, basePath, appPathRoutesManifest); await Promise.all(Object.entries(routesToCopy).map(async ([path, route]) => { var _a, _b; if (route.initialRevalidateSeconds) { logger_1.logger.debug(`skipping ${path} due to revalidate`); return; } if (pathsUsingsFeaturesNotSupportedByHosting.some((it) => path.match(it))) { logger_1.logger.debug(`skipping ${path} due to it matching an unsupported rewrite/redirect/header or middlware`); return; } if (staticRoutesUsingServerActions.some((it) => path === it)) { logger_1.logger.debug(`skipping ${path} due to server action`); return; } const appPathRoute = route.srcRoute && ((_a = appPathRoutesEntries.find(([, it]) => it === route.srcRoute)) === null || _a === void 0 ? void 0 : _a[0]); const contentDist = (0, path_1.join)(sourceDir, distDir, "server", appPathRoute ? "app" : "pages"); const sourceParts = path.split("/").filter((it) => !!it); const locale = (i18n === null || i18n === void 0 ? void 0 : i18n.locales.includes(sourceParts[0])) ? sourceParts[0] : undefined; const includeOnThisDomain = !locale || !matchingI18nDomain || matchingI18nDomain.defaultLocale === locale || !matchingI18nDomain.locales || matchingI18nDomain.locales.includes(locale); if (!includeOnThisDomain) { logger_1.logger.debug(`skipping ${path} since it is for a locale not deployed on this domain`); return; } const sourcePartsOrIndex = sourceParts.length > 0 ? sourceParts : ["index"]; const destParts = sourceParts.slice(locale ? 1 : 0); const destPartsOrIndex = destParts.length > 0 ? destParts : ["index"]; const isDefaultLocale = !locale || ((_b = (matchingI18nDomain || i18n)) === null || _b === void 0 ? void 0 : _b.defaultLocale) === locale; let sourcePath = (0, path_1.join)(contentDist, ...sourcePartsOrIndex); let localizedDestPath = !singleLocaleDomain && locale && (0, path_1.join)(destDir, constants_1.I18N_ROOT, locale, basePath, ...destPartsOrIndex); let defaultDestPath = isDefaultLocale && (0, path_1.join)(destDir, basePath, ...destPartsOrIndex); if (!(0, fsutils_1.fileExistsSync)(sourcePath) && (0, fsutils_1.fileExistsSync)(`${sourcePath}.html`)) { sourcePath += ".html"; if (pprRoutes.includes(path)) { logger_1.logger.debug(`skipping ${path} due to ppr`); return; } if (localizedDestPath) localizedDestPath += ".html"; if (defaultDestPath) defaultDestPath += ".html"; } else if (appPathRoute && (0, path_1.basename)(appPathRoute) === "route" && (0, fsutils_1.fileExistsSync)(`${sourcePath}.body`)) { sourcePath += ".body"; } else if (!(0, fs_extra_1.pathExistsSync)(sourcePath)) { console.error(`Cannot find ${path} in your compiled Next.js application.`); return; } if (localizedDestPath) { await (0, promises_1.mkdir)((0, path_1.dirname)(localizedDestPath), { recursive: true }); await (0, promises_1.copyFile)(sourcePath, localizedDestPath); } if (defaultDestPath) { await (0, promises_1.mkdir)((0, path_1.dirname)(defaultDestPath), { recursive: true }); await (0, promises_1.copyFile)(sourcePath, defaultDestPath); } if (route.dataRoute && !appPathRoute) { const dataSourcePath = `${(0, path_1.join)(...sourcePartsOrIndex)}.json`; const dataDestPath = (0, path_1.join)(destDir, basePath, route.dataRoute); await (0, promises_1.mkdir)((0, path_1.dirname)(dataDestPath), { recursive: true }); await (0, promises_1.copyFile)((0, path_1.join)(contentDist, dataSourcePath), dataDestPath); } })); } exports.ɵcodegenPublicDirectory = ɵcodegenPublicDirectory; async function ɵcodegenFunctionsDirectory(sourceDir, destDir, target, context) { const { distDir } = await getConfig(sourceDir); const packageJson = await (0, utils_1.readJSON)((0, path_1.join)(sourceDir, "package.json")); const configFile = await (0, utils_2.whichNextConfigFile)(sourceDir); if (configFile) { try { let esbuildPath = (0, utils_2.findEsbuildPath)(); if (!esbuildPath || !(0, fs_extra_1.pathExistsSync)(esbuildPath)) { console.warn("esbuild not found, installing..."); (0, utils_2.installEsbuild)(constants_2.ESBUILD_VERSION); esbuildPath = (0, utils_2.findEsbuildPath)(); if (!esbuildPath || !(0, fs_extra_1.pathExistsSync)(esbuildPath)) { throw new error_1.FirebaseError("Failed to locate esbuild after installation."); } } const esbuild = require(esbuildPath); if (!esbuild) { throw new error_1.FirebaseError(`Failed to load esbuild from path: ${esbuildPath}`); } const productionDeps = await new Promise((resolve) => { const dependencies = []; const npmLs = (0, cross_spawn_1.spawn)("npm", ["ls", "--omit=dev", "--all", "--json=true"], { cwd: sourceDir, timeout: constants_1.NPM_COMMAND_TIMEOUT_MILLIES, }); const pipeline = (0, stream_chain_1.chain)([ npmLs.stdout, (0, stream_json_1.parser)({ packValues: false, packKeys: true, streamValues: false }), (0, Pick_1.pick)({ filter: "dependencies" }), (0, StreamObject_1.streamObject)(), ({ key, value }) => [ key, ...(0, utils_2.allDependencyNames)(value), ], ]); pipeline.on("data", (it) => dependencies.push(it)); pipeline.on("end", () => { resolve([...new Set(dependencies)]); }); }); const esbuildArgs = { entryPoints: [(0, path_1.join)(sourceDir, configFile)], outfile: (0, path_1.join)(destDir, configFile), bundle: true, platform: "node", target: `node${constants_1.NODE_VERSION}`, logLevel: "error", external: productionDeps, }; if (configFile === "next.config.mjs") { esbuildArgs.format = "esm"; } const bundle = await esbuild.build(esbuildArgs); if (bundle.errors && bundle.errors.length > 0) { throw new error_1.FirebaseError(bundle.errors.toString()); } } catch (e) { console.warn(`Unable to bundle ${configFile} for use in Cloud Functions, proceeding with deploy but problems may be encountered.`); console.error(e.message || e); await (0, fs_extra_1.copy)((0, path_1.join)(sourceDir, configFile), (0, path_1.join)(destDir, configFile)); } } if (await (0, fs_extra_1.pathExists)((0, path_1.join)(sourceDir, "public"))) { await (0, promises_1.mkdir)((0, path_1.join)(destDir, "public")); await (0, fs_extra_1.copy)((0, path_1.join)(sourceDir, "public"), (0, path_1.join)(destDir, "public")); } if (await (0, utils_2.isUsingImageOptimization)(sourceDir, distDir)) { packageJson.dependencies["sharp"] = constants_1.SHARP_VERSION; } const dotEnv = {}; if ((context === null || context === void 0 ? void 0 : context.projectId) && (context === null || context === void 0 ? void 0 : context.site)) { const deploymentDomain = await (0, api_1.getDeploymentDomain)(context.projectId, context.site, context.hostingChannel); if (deploymentDomain) { dotEnv["VERCEL_URL"] = deploymentDomain; } } const [productionDistDirfiles] = await Promise.all([ (0, utils_2.getProductionDistDirFiles)(sourceDir, distDir), (0, fs_extra_1.mkdirp)((0, path_1.join)(destDir, distDir)), ]); await Promise.all(productionDistDirfiles.map((file) => (0, fs_extra_1.copy)((0, path_1.join)(sourceDir, distDir, file), (0, path_1.join)(destDir, distDir, file), { recursive: true, }))); return { packageJson, frameworksEntry: "next.js", dotEnv }; } exports.ɵcodegenFunctionsDirectory = ɵcodegenFunctionsDirectory; async function getDevModeHandle(dir, _, hostingEmulatorInfo) { if (!hostingEmulatorInfo) { if (await (0, utils_2.isUsingMiddleware)(dir, true)) { throw new error_1.FirebaseError(`${clc.bold("firebase serve")} does not support Next.js Middleware. Please use ${clc.bold("firebase emulators:start")} instead.`); } } let next = await (0, utils_1.relativeRequire)(dir, "next"); if ("default" in next) next = next.default; const nextApp = next({ dev: true, dir, hostname: hostingEmulatorInfo === null || hostingEmulatorInfo === void 0 ? void 0 : hostingEmulatorInfo.host, port: hostingEmulatorInfo === null || hostingEmulatorInfo === void 0 ? void 0 : hostingEmulatorInfo.port, }); const handler = nextApp.getRequestHandler(); await nextApp.prepare(); return (0, utils_1.simpleProxy)(async (req, res) => { const parsedUrl = (0, url_1.parse)(req.url, true); await handler(req, res, parsedUrl); }); } exports.getDevModeHandle = getDevModeHandle; async function getConfig(dir) { var _a; var _b; let config = {}; const configFile = await (0, utils_2.whichNextConfigFile)(dir); if (configFile) { const version = (0, utils_2.getNextVersion)(dir); if (!version) throw new Error("Unable to find the next dep, try NPM installing?"); if ((0, semver_1.gte)(version, "12.0.0")) { const [{ default: loadConfig }, { PHASE_PRODUCTION_BUILD }] = await Promise.all([ (0, utils_1.relativeRequire)(dir, "next/dist/server/config"), (0, utils_1.relativeRequire)(dir, "next/constants"), ]); config = await loadConfig(PHASE_PRODUCTION_BUILD, dir); } else { try { config = await (_a = (0, url_1.pathToFileURL)((0, path_1.join)(dir, configFile)).toString(), Promise.resolve().then(() => require(_a))); } catch (e) { throw new Error(`Unable to load ${configFile}.`); } } } (0, utils_1.validateLocales)((_b = config.i18n) === null || _b === void 0 ? void 0 : _b.locales); return Object.assign({ distDir: ".next", trailingSlash: false, basePath: "/" }, config); }