UNPKG

firebase-tools

Version:
299 lines (298 loc) 14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.installEsbuild = exports.getGlobalEsbuildVersion = exports.findEsbuildPath = exports.whichNextConfigFile = exports.getProductionDistDirFiles = exports.getRoutesWithServerAction = exports.hasStaticAppNotFoundComponent = exports.getNextVersion = exports.getBuildId = exports.getAppMetadataFromMetaFiles = exports.getNonStaticServerComponents = exports.getNonStaticRoutes = exports.getMiddlewareMatcherRegexes = exports.allDependencyNames = exports.isUsingAppDirectory = exports.isUsingNextImageInAppDirectory = exports.isUsingImageOptimization = exports.isUsingMiddleware = exports.hasUnoptimizedImage = exports.usesNextImage = exports.usesAppDirRouter = exports.getNextjsRewritesToUse = exports.isHeaderSupportedByHosting = exports.isRedirectSupportedByHosting = exports.isRewriteSupportedByHosting = exports.cleanI18n = exports.cleanCustomRouteI18n = exports.cleanEscapedChars = exports.I18N_SOURCE = void 0; const fs_1 = require("fs"); const fs_extra_1 = require("fs-extra"); const path_1 = require("path"); const promises_1 = require("fs/promises"); const glob_1 = require("glob"); const semver_1 = require("semver"); const utils_1 = require("../utils"); const constants_1 = require("./constants"); const fsutils_1 = require("../../fsutils"); const utils_2 = require("../../utils"); const child_process_1 = require("child_process"); const error_1 = require("../../error"); exports.I18N_SOURCE = /\/:nextInternalLocale(\([^\)]+\))?/; function cleanEscapedChars(path) { return path.replace(/\\([(){}:+?*])/g, (a, b) => b); } exports.cleanEscapedChars = cleanEscapedChars; function cleanCustomRouteI18n(path) { return path.replace(exports.I18N_SOURCE, ""); } exports.cleanCustomRouteI18n = cleanCustomRouteI18n; function cleanI18n(it) { const [, localesRegex] = it.source.match(exports.I18N_SOURCE) || [undefined, undefined]; const source = localesRegex ? cleanCustomRouteI18n(it.source) : it.source; const destination = "destination" in it && localesRegex ? cleanCustomRouteI18n(it.destination) : it.destination; const regex = "regex" in it && localesRegex ? it.regex.replace(`(?:/${localesRegex})`, "") : it.regex; return Object.assign(Object.assign({}, it), { source, destination, regex }); } exports.cleanI18n = cleanI18n; function isRewriteSupportedByHosting(rewrite) { return !("has" in rewrite || "missing" in rewrite || (0, utils_1.isUrl)(rewrite.destination) || rewrite.destination.includes("?")); } exports.isRewriteSupportedByHosting = isRewriteSupportedByHosting; function isRedirectSupportedByHosting(redirect) { return !("has" in redirect || "missing" in redirect || "internal" in redirect || redirect.destination.includes("?")); } exports.isRedirectSupportedByHosting = isRedirectSupportedByHosting; function isHeaderSupportedByHosting(header) { return !("has" in header || "missing" in header); } exports.isHeaderSupportedByHosting = isHeaderSupportedByHosting; function getNextjsRewritesToUse(nextJsRewrites) { if (Array.isArray(nextJsRewrites)) { return nextJsRewrites.map(cleanI18n); } if (nextJsRewrites === null || nextJsRewrites === void 0 ? void 0 : nextJsRewrites.beforeFiles) { return nextJsRewrites.beforeFiles.map(cleanI18n); } return []; } exports.getNextjsRewritesToUse = getNextjsRewritesToUse; function usesAppDirRouter(sourceDir) { const appPathRoutesManifestPath = (0, path_1.join)(sourceDir, constants_1.APP_PATH_ROUTES_MANIFEST); return (0, fs_1.existsSync)(appPathRoutesManifestPath); } exports.usesAppDirRouter = usesAppDirRouter; async function usesNextImage(sourceDir, distDir) { const exportMarker = await (0, utils_1.readJSON)((0, path_1.join)(sourceDir, distDir, constants_1.EXPORT_MARKER)); return exportMarker.isNextImageImported; } exports.usesNextImage = usesNextImage; async function hasUnoptimizedImage(sourceDir, distDir) { const imagesManifest = await (0, utils_1.readJSON)((0, path_1.join)(sourceDir, distDir, constants_1.IMAGES_MANIFEST)); return imagesManifest.images.unoptimized; } exports.hasUnoptimizedImage = hasUnoptimizedImage; async function isUsingMiddleware(dir, isDevMode) { if (isDevMode) { const [middlewareJs, middlewareTs] = await Promise.all([ (0, fs_extra_1.pathExists)((0, path_1.join)(dir, "middleware.js")), (0, fs_extra_1.pathExists)((0, path_1.join)(dir, "middleware.ts")), ]); return middlewareJs || middlewareTs; } else { const middlewareManifest = await (0, utils_1.readJSON)((0, path_1.join)(dir, "server", constants_1.MIDDLEWARE_MANIFEST)); return Object.keys(middlewareManifest.middleware).length > 0; } } exports.isUsingMiddleware = isUsingMiddleware; async function isUsingImageOptimization(projectDir, distDir) { let isNextImageImported = await usesNextImage(projectDir, distDir); if (!isNextImageImported && isUsingAppDirectory((0, path_1.join)(projectDir, distDir))) { if (await isUsingNextImageInAppDirectory(projectDir, distDir)) { isNextImageImported = true; } } if (isNextImageImported) { const imagesManifest = await (0, utils_1.readJSON)((0, path_1.join)(projectDir, distDir, constants_1.IMAGES_MANIFEST)); return !imagesManifest.images.unoptimized; } return false; } exports.isUsingImageOptimization = isUsingImageOptimization; async function isUsingNextImageInAppDirectory(projectDir, nextDir) { const nextImagePath = ["node_modules", "next", "dist", "client", "image"]; const nextImageString = utils_2.IS_WINDOWS ? nextImagePath.join(path_1.sep + path_1.sep) : (0, path_1.join)(...nextImagePath); const files = (0, glob_1.sync)((0, path_1.join)(projectDir, nextDir, "server", "**", "*client-reference-manifest.js")); for (const filepath of files) { const fileContents = await (0, promises_1.readFile)(filepath, "utf-8"); if (fileContents.includes(nextImageString)) { return true; } } return false; } exports.isUsingNextImageInAppDirectory = isUsingNextImageInAppDirectory; function isUsingAppDirectory(dir) { const appPathRoutesManifestPath = (0, path_1.join)(dir, constants_1.APP_PATH_ROUTES_MANIFEST); return (0, fsutils_1.fileExistsSync)(appPathRoutesManifestPath); } exports.isUsingAppDirectory = isUsingAppDirectory; function allDependencyNames(mod) { if (!mod.dependencies) return []; const dependencyNames = Object.keys(mod.dependencies).reduce((acc, it) => [...acc, it, ...allDependencyNames(mod.dependencies[it])], []); return dependencyNames; } exports.allDependencyNames = allDependencyNames; function getMiddlewareMatcherRegexes(middlewareManifest) { const middlewareObjectValues = Object.values(middlewareManifest.middleware); let middlewareMatchers; if (middlewareManifest.version === 1) { middlewareMatchers = middlewareObjectValues.map((page) => ({ regexp: page.regexp })); } else { middlewareMatchers = middlewareObjectValues .map((page) => page.matchers) .flat(); } return middlewareMatchers.map((matcher) => new RegExp(matcher.regexp)); } exports.getMiddlewareMatcherRegexes = getMiddlewareMatcherRegexes; function getNonStaticRoutes(pagesManifestJSON, prerenderedRoutes, dynamicRoutes) { const nonStaticRoutes = Object.entries(pagesManifestJSON) .filter(([it, src]) => !((0, path_1.extname)(src) !== ".js" || ["/_app", "/_error", "/_document"].includes(it) || prerenderedRoutes.includes(it) || dynamicRoutes.includes(it))) .map(([it]) => it); return nonStaticRoutes; } exports.getNonStaticRoutes = getNonStaticRoutes; function getNonStaticServerComponents(appPathsManifest, appPathRoutesManifest, prerenderedRoutes, dynamicRoutes) { const nonStaticServerComponents = Object.entries(appPathsManifest) .filter(([it, src]) => { if ((0, path_1.extname)(src) !== ".js") return; const path = appPathRoutesManifest[it]; return !(prerenderedRoutes.includes(path) || dynamicRoutes.includes(path)); }) .map(([it]) => it); return new Set(nonStaticServerComponents); } exports.getNonStaticServerComponents = getNonStaticServerComponents; async function getAppMetadataFromMetaFiles(sourceDir, distDir, basePath, appPathRoutesManifest) { const headers = []; const pprRoutes = []; await Promise.all(Object.entries(appPathRoutesManifest).map(async ([key, source]) => { if (!["route", "page"].includes((0, path_1.basename)(key))) return; const parts = source.split("/").filter((it) => !!it); const partsOrIndex = parts.length > 0 ? parts : ["index"]; const routePath = (0, path_1.join)(sourceDir, distDir, "server", "app", ...partsOrIndex); const metadataPath = `${routePath}.meta`; if ((0, fsutils_1.dirExistsSync)(routePath) && (0, fsutils_1.fileExistsSync)(metadataPath)) { const meta = await (0, utils_1.readJSON)(metadataPath); if (meta.headers) headers.push({ source: path_1.posix.join(basePath, source), headers: Object.entries(meta.headers).map(([key, value]) => ({ key, value })), }); if (meta.postponed) pprRoutes.push(source); } })); return { headers, pprRoutes }; } exports.getAppMetadataFromMetaFiles = getAppMetadataFromMetaFiles; async function getBuildId(distDir) { const buildId = await (0, promises_1.readFile)((0, path_1.join)(distDir, "BUILD_ID")); return buildId.toString(); } exports.getBuildId = getBuildId; function getNextVersion(cwd) { const dependency = (0, utils_1.findDependency)("next", { cwd, depth: 0, omitDev: false }); if (!dependency) return undefined; const nextVersionSemver = (0, semver_1.coerce)(dependency.version); if (!nextVersionSemver) return dependency.version; return nextVersionSemver.toString(); } exports.getNextVersion = getNextVersion; async function hasStaticAppNotFoundComponent(sourceDir, distDir) { return (0, fs_extra_1.pathExists)((0, path_1.join)(sourceDir, distDir, "server", "app", "_not-found.html")); } exports.hasStaticAppNotFoundComponent = hasStaticAppNotFoundComponent; function getRoutesWithServerAction(serverReferenceManifest, appPathRoutesManifest) { const routesWithServerAction = new Set(); for (const key of Object.keys(serverReferenceManifest)) { if (key !== "edge" && key !== "node") continue; const edgeOrNode = serverReferenceManifest[key]; for (const actionId of Object.keys(edgeOrNode)) { if (!edgeOrNode[actionId].layer) continue; for (const [route, type] of Object.entries(edgeOrNode[actionId].layer)) { if (type === constants_1.WEBPACK_LAYERS.actionBrowser) { routesWithServerAction.add(appPathRoutesManifest[route.replace("app", "")]); } } } } return Array.from(routesWithServerAction); } exports.getRoutesWithServerAction = getRoutesWithServerAction; async function getProductionDistDirFiles(sourceDir, distDir) { return (0, glob_1.glob)("**", { ignore: [(0, path_1.join)("cache", "webpack", "*-development", "**"), (0, path_1.join)("cache", "eslint", "**")], cwd: (0, path_1.join)(sourceDir, distDir), nodir: true, absolute: false, }); } exports.getProductionDistDirFiles = getProductionDistDirFiles; async function whichNextConfigFile(dir) { for (const file of constants_1.CONFIG_FILES) { if (await (0, fs_extra_1.pathExists)((0, path_1.join)(dir, file))) return file; } return null; } exports.whichNextConfigFile = whichNextConfigFile; function findEsbuildPath() { var _a; try { const esbuildBinPath = (_a = (0, child_process_1.execSync)("npx which esbuild", { encoding: "utf8" })) === null || _a === void 0 ? void 0 : _a.trim(); if (!esbuildBinPath) { return null; } const globalVersion = getGlobalEsbuildVersion(esbuildBinPath); if (globalVersion && !(0, semver_1.satisfies)(globalVersion, constants_1.ESBUILD_VERSION)) { console.warn(`Warning: Global esbuild version (${globalVersion}) does not match the required version (${constants_1.ESBUILD_VERSION}).`); } return (0, path_1.resolve)((0, path_1.dirname)(esbuildBinPath), "../esbuild"); } catch (error) { console.error(`Failed to find esbuild with npx which: ${error}`); return null; } } exports.findEsbuildPath = findEsbuildPath; function getGlobalEsbuildVersion(binPath) { var _a; try { const versionOutput = (_a = (0, child_process_1.execSync)(`"${binPath}" --version`, { encoding: "utf8" })) === null || _a === void 0 ? void 0 : _a.trim(); if (!versionOutput) { return null; } const versionMatch = versionOutput.match(/(\d+\.\d+\.\d+)/); return versionMatch ? versionMatch[0] : null; } catch (error) { console.error(`Failed to get global esbuild version: ${error}`); return null; } } exports.getGlobalEsbuildVersion = getGlobalEsbuildVersion; function installEsbuild(version) { const installCommand = `npm install esbuild@${version} --no-save`; try { (0, child_process_1.execSync)(installCommand, { stdio: "inherit" }); } catch (error) { if (error instanceof error_1.FirebaseError) { throw error; } else { throw new error_1.FirebaseError(`Failed to install esbuild: ${error}`, { original: error }); } } } exports.installEsbuild = installEsbuild;