123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.getDevModeHandle = exports.ɵcodegenFunctionsDirectory = exports.ɵcodegenPublicDirectory = exports.init = exports.build = exports.discover = exports.type = exports.support = exports.name = void 0;
- const child_process_1 = require("child_process");
- const promises_1 = require("fs/promises");
- const path_1 = require("path");
- const fs_extra_1 = require("fs-extra");
- const url_1 = require("url");
- const fs_1 = require("fs");
- const semver_1 = require("semver");
- const clc = require("colorette");
- const __1 = require("..");
- const prompt_1 = require("../../prompt");
- const error_1 = require("../../error");
- const utils_1 = require("./utils");
- const utils_2 = require("../utils");
- const utils_3 = require("../utils");
- const utils_4 = require("./utils");
- const constants_1 = require("./constants");
- const DEFAULT_BUILD_SCRIPT = ["next build"];
- const PUBLIC_DIR = "public";
- exports.name = "Next.js";
- exports.support = "experimental";
- exports.type = 2;
- const DEFAULT_NUMBER_OF_REASONS_TO_LIST = 5;
- function getNextVersion(cwd) {
- var _a;
- return (_a = (0, __1.findDependency)("next", { cwd, depth: 0, omitDev: false })) === null || _a === void 0 ? void 0 : _a.version;
- }
- function getReactVersion(cwd) {
- var _a;
- return (_a = (0, __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;
- if (!(await (0, fs_extra_1.pathExists)("next.config.js")) && !getNextVersion(dir))
- return;
- return { mayWantBackend: true, publicDirectory: (0, path_1.join)(dir, PUBLIC_DIR) };
- }
- exports.discover = discover;
- async function build(dir) {
- var _a, _b;
- const { default: nextBuild } = (0, __1.relativeRequire)(dir, "next/dist/build");
- await (0, utils_3.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";
- }
- await nextBuild(dir, null, false, false, true).catch((e) => {
- console.error(e.message);
- throw e;
- });
- const reasonsForBackend = [];
- const { distDir } = await getConfig(dir);
- if (await (0, utils_1.isUsingMiddleware)((0, path_1.join)(dir, distDir), false)) {
- reasonsForBackend.push("middleware");
- }
- if (await (0, utils_1.isUsingImageOptimization)((0, path_1.join)(dir, distDir))) {
- reasonsForBackend.push(`Image Optimization`);
- }
- if ((0, utils_1.isUsingAppDirectory)((0, path_1.join)(dir, distDir))) {
- reasonsForBackend.push("app directory (unstable)");
- }
- const prerenderManifest = await (0, utils_2.readJSON)((0, path_1.join)(dir, distDir, constants_1.PRERENDER_MANIFEST));
- const dynamicRoutesWithFallback = Object.entries(prerenderManifest.dynamicRoutes || {}).filter(([, it]) => it.fallback !== false);
- if (dynamicRoutesWithFallback.length > 0) {
- for (const [key] of dynamicRoutesWithFallback) {
- reasonsForBackend.push(`use of fallback ${key}`);
- }
- }
- const routesWithRevalidate = Object.entries(prerenderManifest.routes).filter(([, it]) => it.initialRevalidateSeconds);
- if (routesWithRevalidate.length > 0) {
- for (const [key] of routesWithRevalidate) {
- reasonsForBackend.push(`use of revalidate ${key}`);
- }
- }
- const pagesManifestJSON = await (0, utils_2.readJSON)((0, path_1.join)(dir, distDir, "server", constants_1.PAGES_MANIFEST));
- const prerenderedRoutes = Object.keys(prerenderManifest.routes);
- const dynamicRoutes = Object.keys(prerenderManifest.dynamicRoutes);
- const unrenderedPages = Object.keys(pagesManifestJSON).filter((it) => !(["/_app", "/", "/_error", "/_document", "/404"].includes(it) ||
- prerenderedRoutes.includes(it) ||
- dynamicRoutes.includes(it)));
- if (unrenderedPages.length > 0) {
- for (const key of unrenderedPages) {
- reasonsForBackend.push(`non-static route ${key}`);
- }
- }
- const manifest = await (0, utils_2.readJSON)((0, path_1.join)(dir, distDir, constants_1.ROUTES_MANIFEST));
- const { headers: nextJsHeaders = [], redirects: nextJsRedirects = [], rewrites: nextJsRewrites = [], } = manifest;
- const isEveryHeaderSupported = nextJsHeaders.every(utils_1.isHeaderSupportedByHosting);
- if (!isEveryHeaderSupported) {
- reasonsForBackend.push("advanced headers");
- }
- const headers = nextJsHeaders.filter(utils_1.isHeaderSupportedByHosting).map(({ source, headers }) => ({
- source: (0, utils_1.cleanEscapedChars)(source),
- headers,
- }));
- const isEveryRedirectSupported = nextJsRedirects.every(utils_1.isRedirectSupportedByHosting);
- if (!isEveryRedirectSupported) {
- reasonsForBackend.push("advanced redirects");
- }
- const redirects = nextJsRedirects
- .filter(utils_1.isRedirectSupportedByHosting)
- .map(({ source, destination, statusCode: type }) => ({
- source: (0, utils_1.cleanEscapedChars)(source),
- destination,
- type,
- }));
- const nextJsRewritesToUse = (0, utils_1.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.push("advanced rewrites");
- }
- const isEveryRewriteSupported = nextJsRewritesToUse.every(utils_1.isRewriteSupportedByHosting);
- if (!isEveryRewriteSupported) {
- reasonsForBackend.push("advanced rewrites");
- }
- const rewrites = nextJsRewritesToUse
- .filter(utils_1.isRewriteSupportedByHosting)
- .map(({ source, destination }) => ({
- source: (0, utils_1.cleanEscapedChars)(source),
- destination,
- }));
- const wantsBackend = reasonsForBackend.length > 0;
- if (wantsBackend) {
- const numberOfReasonsToList = process.env.DEBUG ? Infinity : DEFAULT_NUMBER_OF_REASONS_TO_LIST;
- console.log("Building a Cloud Function to run this application. This is needed due to:");
- for (const reason of reasonsForBackend.slice(0, numberOfReasonsToList)) {
- console.log(` • ${reason}`);
- }
- if (reasonsForBackend.length > numberOfReasonsToList) {
- console.log(` • and ${reasonsForBackend.length - numberOfReasonsToList} other reasons, use --debug to see more`);
- }
- console.log("");
- }
- return { wantsBackend, headers, redirects, rewrites };
- }
- exports.build = build;
- async function init(setup) {
- const language = await (0, prompt_1.promptOnce)({
- type: "list",
- default: "JavaScript",
- message: "What language would you like to use?",
- choices: ["JavaScript", "TypeScript"],
- });
- (0, child_process_1.execSync)(`npx --yes create-next-app@latest -e hello-world ${setup.hosting.source} --use-npm ${language === "TypeScript" ? "--ts" : ""}`, { stdio: "inherit" });
- }
- exports.init = init;
- async function ɵcodegenPublicDirectory(sourceDir, destDir) {
- const { distDir } = await getConfig(sourceDir);
- const publicPath = (0, path_1.join)(sourceDir, "public");
- await (0, promises_1.mkdir)((0, path_1.join)(destDir, "_next", "static"), { recursive: true });
- if (await (0, fs_extra_1.pathExists)(publicPath)) {
- await (0, fs_extra_1.copy)(publicPath, destDir);
- }
- await (0, fs_extra_1.copy)((0, path_1.join)(sourceDir, distDir, "static"), (0, path_1.join)(destDir, "_next", "static"));
- for (const file of ["index.html", "404.html", "500.html"]) {
- const pagesPath = (0, path_1.join)(sourceDir, distDir, "server", "pages", file);
- if (await (0, fs_extra_1.pathExists)(pagesPath)) {
- await (0, promises_1.copyFile)(pagesPath, (0, path_1.join)(destDir, file));
- continue;
- }
- const appPath = (0, path_1.join)(sourceDir, distDir, "server", "app", file);
- if (await (0, fs_extra_1.pathExists)(appPath)) {
- await (0, promises_1.copyFile)(appPath, (0, path_1.join)(destDir, file));
- }
- }
- const [middlewareManifest, prerenderManifest, routesManifest] = await Promise.all([
- (0, utils_2.readJSON)((0, path_1.join)(sourceDir, distDir, "server", constants_1.MIDDLEWARE_MANIFEST)),
- (0, utils_2.readJSON)((0, path_1.join)(sourceDir, distDir, constants_1.PRERENDER_MANIFEST)),
- (0, utils_2.readJSON)((0, path_1.join)(sourceDir, distDir, constants_1.ROUTES_MANIFEST)),
- ]);
- const middlewareMatcherRegexes = Object.values(middlewareManifest.middleware)
- .map((it) => it.matchers)
- .flat()
- .map((it) => new RegExp(it.regexp));
- const { redirects = [], rewrites = [], headers = [] } = routesManifest;
- const rewritesRegexesNotSupportedByHosting = (0, utils_1.getNextjsRewritesToUse)(rewrites)
- .filter((rewrite) => !(0, utils_1.isRewriteSupportedByHosting)(rewrite))
- .map((rewrite) => new RegExp(rewrite.regex));
- const redirectsRegexesNotSupportedByHosting = redirects
- .filter((redirect) => !(0, utils_1.isRedirectSupportedByHosting)(redirect))
- .map((redirect) => new RegExp(redirect.regex));
- const headersRegexesNotSupportedByHosting = headers
- .filter((header) => !(0, utils_1.isHeaderSupportedByHosting)(header))
- .map((header) => new RegExp(header.regex));
- const pathsUsingsFeaturesNotSupportedByHosting = [
- ...middlewareMatcherRegexes,
- ...rewritesRegexesNotSupportedByHosting,
- ...redirectsRegexesNotSupportedByHosting,
- ...headersRegexesNotSupportedByHosting,
- ];
- for (const [path, route] of Object.entries(prerenderManifest.routes)) {
- if (route.initialRevalidateSeconds ||
- pathsUsingsFeaturesNotSupportedByHosting.some((it) => path.match(it))) {
- continue;
- }
- const isReactServerComponent = route.dataRoute.endsWith(".rsc");
- const contentDist = (0, path_1.join)(sourceDir, distDir, "server", isReactServerComponent ? "app" : "pages");
- const parts = path.split("/").filter((it) => !!it);
- const partsOrIndex = parts.length > 0 ? parts : ["index"];
- const htmlPath = `${(0, path_1.join)(...partsOrIndex)}.html`;
- await (0, promises_1.mkdir)((0, path_1.join)(destDir, (0, path_1.dirname)(htmlPath)), { recursive: true });
- await (0, promises_1.copyFile)((0, path_1.join)(contentDist, htmlPath), (0, path_1.join)(destDir, htmlPath));
- if (!isReactServerComponent) {
- const dataPath = `${(0, path_1.join)(...partsOrIndex)}.json`;
- await (0, promises_1.mkdir)((0, path_1.join)(destDir, (0, path_1.dirname)(route.dataRoute)), { recursive: true });
- await (0, promises_1.copyFile)((0, path_1.join)(contentDist, dataPath), (0, path_1.join)(destDir, route.dataRoute));
- }
- }
- }
- exports.ɵcodegenPublicDirectory = ɵcodegenPublicDirectory;
- async function ɵcodegenFunctionsDirectory(sourceDir, destDir) {
- const { distDir } = await getConfig(sourceDir);
- const packageJson = await (0, utils_2.readJSON)((0, path_1.join)(sourceDir, "package.json"));
- if ((0, fs_1.existsSync)((0, path_1.join)(sourceDir, "next.config.js"))) {
- const dependencyTree = JSON.parse((0, child_process_1.spawnSync)("npm", ["ls", "--omit=dev", "--all", "--json"], {
- cwd: sourceDir,
- }).stdout.toString());
- const esbuildArgs = (0, utils_1.allDependencyNames)(dependencyTree)
- .map((it) => `--external:${it}`)
- .concat("--bundle", "--platform=node", `--target=node${__1.NODE_VERSION}`, `--outdir=${destDir}`, "--log-level=error");
- const bundle = (0, child_process_1.spawnSync)("npx", ["--yes", "esbuild", "next.config.js", ...esbuildArgs], {
- cwd: sourceDir,
- });
- if (bundle.status) {
- console.error(bundle.stderr.toString());
- throw new error_1.FirebaseError("Unable to bundle next.config.js for use in Cloud Functions");
- }
- }
- 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_4.hasUnoptimizedImage)(sourceDir, distDir)) &&
- ((0, utils_4.usesAppDirRouter)(sourceDir) || (await (0, utils_4.usesNextImage)(sourceDir, distDir)))) {
- packageJson.dependencies["sharp"] = "latest";
- }
- await (0, fs_extra_1.mkdirp)((0, path_1.join)(destDir, distDir));
- await (0, fs_extra_1.copy)((0, path_1.join)(sourceDir, distDir), (0, path_1.join)(destDir, distDir));
- return { packageJson, frameworksEntry: "next.js" };
- }
- exports.ɵcodegenFunctionsDirectory = ɵcodegenFunctionsDirectory;
- async function getDevModeHandle(dir, hostingEmulatorInfo) {
- if (!hostingEmulatorInfo) {
- if (await (0, utils_1.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.`);
- }
- }
- const { default: next } = (0, __1.relativeRequire)(dir, "next");
- 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 (req, res, next) => {
- const parsedUrl = (0, url_1.parse)(req.url, true);
- const proxy = (0, __1.createServerResponseProxy)(req, res, next);
- handler(req, proxy, parsedUrl);
- };
- }
- exports.getDevModeHandle = getDevModeHandle;
- async function getConfig(dir) {
- let config = {};
- if ((0, fs_1.existsSync)((0, path_1.join)(dir, "next.config.js"))) {
- const version = 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 } = (0, __1.relativeRequire)(dir, "next/dist/server/config");
- const { PHASE_PRODUCTION_BUILD } = (0, __1.relativeRequire)(dir, "next/constants");
- config = await loadConfig(PHASE_PRODUCTION_BUILD, dir, null);
- }
- else {
- try {
- config = await Promise.resolve().then(() => require((0, url_1.pathToFileURL)((0, path_1.join)(dir, "next.config.js")).toString()));
- }
- catch (e) {
- throw new Error("Unable to load next.config.js.");
- }
- }
- }
- return Object.assign({ distDir: ".next" }, config);
- }
|