import { fileURLToPath } from "node:url" import * as Sentry from "@sentry/nextjs" import createJiti from "jiti" import { findMyBookingRoutes } from "@scandic-hotels/common/constants/routes/findMyBookingRoutes" import { login, logout, } from "@scandic-hotels/common/constants/routes/handleAuth" import { myPages } from "@scandic-hotels/common/constants/routes/myPages" import { myStay, preliminaryReceipt, } from "@scandic-hotels/common/constants/routes/myStay" import { myStay as myStayWebview, preliminaryReceipt as preliminaryReceiptWebview, } from "./constants/routes/webviews" const jiti = createJiti(fileURLToPath(import.meta.url)) jiti("./env/server") jiti("./env/client") /** @type {import('next').NextConfig} */ const nextConfig = { env: { BRANCH: process.env.BRANCH || "local", GIT_SHA: process.env.COMMIT_REF || "", }, poweredByHeader: false, eslint: { ignoreDuringBuilds: true }, trailingSlash: false, transpilePackages: [ "@scandic-hotels/common", "@scandic-hotels/trpc", "@scandic-hotels/booking-flow", "@scandic-hotels/design-system", ], experimental: { serverActions: { allowedOrigins: [ "*--web-scandic-hotels.netlify.app", "develop--web-scandic-hotels.netlify.app", "stage--web-scandic-hotels.netlify.app", "test--web-scandic-hotels.netlify.app", "master--web-scandic-hotels.netlify.app", "*.scandichotels.com", ], }, swcPlugins: [ [ "@swc/plugin-formatjs", { ast: true, }, ], ], }, images: { minimumCacheTTL: 2678400, // 31 days deviceSizes: [320, 420, 768, 900, 1024, 1200, 2400], imageSizes: [200, 400, 800, 1200, 1920], remotePatterns: [ { protocol: "https", hostname: "eu-images.contentstack.com", pathname: "/v3/assets/**", }, { protocol: "https", hostname: "scandichotels.com", }, { protocol: "https", hostname: "*.scandichotels.com", }, // Tripadvisor CDN for award images { protocol: "https", hostname: "static.tacdn.com", }, ], }, logging: { fetches: { fullUrl: true, }, }, output: "standalone", // eslint-disable-next-line @typescript-eslint/no-explicit-any webpack: function (config: any) { config.module.rules.push({ test: /\.(graphql|gql)/, exclude: /node_modules/, loader: "graphql-tag/loader", }) return config }, // https://nextjs.org/docs/app/api-reference/next-config-js/redirects#header-cookie-and-query-matching redirects() { // Param checks needs to be split as Next.js handles everything // in the missing array as AND, therefore they need to be separate // to handle when one or more are missing. // // Docs: all missing items must not match for the redirect to be applied. return [ { // ---------------------------------------- // hotel (hotelId) param missing // ---------------------------------------- source: "/:lang/hotelreservation/details", destination: "/:lang/hotelreservation/select-rate", missing: [ { key: "hotel", type: "query", value: undefined, }, ], permanent: false, }, { // ---------------------------------------- // hotel (hotelId) param has to be an integer // ---------------------------------------- source: "/:lang/hotelreservation/details", destination: "/:lang/hotelreservation/select-rate", missing: [ { key: "hotel", type: "query", value: "^[0-9]+$", }, ], permanent: false, }, { // ---------------------------------------- // fromdate param missing // ---------------------------------------- source: "/:lang/hotelreservation/details", destination: "/:lang/hotelreservation/select-rate", missing: [ { key: "fromdate", type: "query", value: undefined, }, ], permanent: false, }, { // ---------------------------------------- // fromdate param has to be a date // ---------------------------------------- source: "/:lang/hotelreservation/details", destination: "/:lang/hotelreservation/select-rate", missing: [ { key: "fromdate", type: "query", value: "^([12]\\d{3}-(0[1-9]|1[0-2])-(0?[1-9]|[12]\\d|3[01]))$", }, ], permanent: false, }, { // ---------------------------------------- // todate param missing // ---------------------------------------- source: "/:lang/hotelreservation/details", destination: "/:lang/hotelreservation/select-rate", missing: [ { key: "todate", type: "query", value: undefined, }, ], permanent: false, }, { // ---------------------------------------- // todate param has to be a date // ---------------------------------------- source: "/:lang/hotelreservation/details", destination: "/:lang/hotelreservation/select-rate", missing: [ { key: "todate", type: "query", value: "^([12]\\d{3}-(0[1-9]|1[0-2])-(0?[1-9]|[12]\\d|3[01]))$", }, ], permanent: false, }, { // ---------------------------------------- // room[0].adults param missing // ---------------------------------------- source: "/:lang/hotelreservation/details", destination: "/:lang/hotelreservation/select-rate", missing: [ { key: "room[0].adults", type: "query", value: undefined, }, ], permanent: false, }, { // ---------------------------------------- // room[0].adults param has to be an integer // ---------------------------------------- source: "/:lang/hotelreservation/details", destination: "/:lang/hotelreservation/select-rate", missing: [ { key: "room[0].adults", type: "query", value: "^[0-9]+$", }, ], permanent: false, }, { // ---------------------------------------- // room[0].ratecode param missing // ---------------------------------------- source: "/:lang/hotelreservation/details", destination: "/:lang/hotelreservation/select-rate", missing: [ { key: "room[0].ratecode", type: "query", value: undefined, }, ], permanent: false, }, { // ---------------------------------------- // room[0].roomtype param missing // ---------------------------------------- source: "/:lang/hotelreservation/details", destination: "/:lang/hotelreservation/select-rate", missing: [ { key: "room[0].roomtype", type: "query", value: undefined, }, ], permanent: false, }, ] }, rewrites() { return { beforeFiles: [ { source: login.da, destination: "/da/login" }, { source: login.de, destination: "/de/login" }, { source: login.fi, destination: "/fi/login" }, { source: login.no, destination: "/no/login" }, { source: login.sv, destination: "/sv/login" }, { source: logout.da, destination: "/da/logout" }, { source: logout.de, destination: "/de/logout" }, { source: logout.fi, destination: "/fi/logout" }, { source: logout.no, destination: "/no/logout" }, { source: logout.sv, destination: "/sv/logout" }, { source: `${myPages.en}/:path*`, destination: `/en/my-pages/:path*`, }, { source: `${myPages.da}/:path*`, destination: `/da/my-pages/:path*`, }, { source: `${myPages.de}/:path*`, destination: `/de/my-pages/:path*`, }, { source: `${myPages.fi}/:path*`, destination: `/fi/my-pages/:path*`, }, { source: `${myPages.no}/:path*`, destination: `/no/my-pages/:path*`, }, { source: `${myPages.sv}/:path*`, destination: `/sv/my-pages/:path*`, }, { source: "/:lang/hotelreservation/payment-callback/:status", destination: "/:lang/hotelreservation/payment-callback?status=:status", }, // Find my booking { source: findMyBookingRoutes.en, destination: "/en/hotelreservation/get-booking", }, { source: findMyBookingRoutes.da, destination: "/da/hotelreservation/get-booking", }, { source: findMyBookingRoutes.de, destination: "/de/hotelreservation/get-booking", }, { source: findMyBookingRoutes.fi, destination: "/fi/hotelreservation/get-booking", }, { source: findMyBookingRoutes.no, destination: "/no/hotelreservation/get-booking", }, { source: findMyBookingRoutes.sv, destination: "/sv/hotelreservation/get-booking", }, // My stay { source: `${myStay.en}`, destination: "/en/hotelreservation/my-stay", }, { source: `${myStay.sv}`, destination: "/sv/hotelreservation/my-stay", }, { source: `${myStay.da}`, destination: "/da/hotelreservation/my-stay", }, { source: `${myStay.de}`, destination: "/de/hotelreservation/my-stay", }, { source: `${myStay.fi}`, destination: "/fi/hotelreservation/my-stay", }, { source: `${myStay.no}`, destination: "/no/hotelreservation/my-stay", }, // My stay receipt { source: `${preliminaryReceipt.en}`, destination: "/en/hotelreservation/my-stay/receipt", }, { source: `${preliminaryReceipt.sv}`, destination: "/sv/hotelreservation/my-stay/receipt", }, { source: `${preliminaryReceipt.da}`, destination: "/da/hotelreservation/my-stay/receipt", }, { source: `${preliminaryReceipt.de}`, destination: "/de/hotelreservation/my-stay/receipt", }, { source: `${preliminaryReceipt.fi}`, destination: "/fi/hotelreservation/my-stay/receipt", }, { source: `${preliminaryReceipt.no}`, destination: "/no/hotelreservation/my-stay/receipt", }, // Webview My stay { source: `${myStayWebview.en}`, destination: "/en/webview/hotelreservation/my-stay", }, { source: `${myStayWebview.sv}`, destination: "/sv/webview/hotelreservation/my-stay", }, { source: `${myStayWebview.da}`, destination: "/da/webview/hotelreservation/my-stay", }, { source: `${myStayWebview.de}`, destination: "/de/webview/hotelreservation/my-stay", }, { source: `${myStayWebview.fi}`, destination: "/fi/webview/hotelreservation/my-stay", }, { source: `${myStayWebview.no}`, destination: "/no/webview/hotelreservation/my-stay", }, // Webview My stay receipt { source: `${preliminaryReceiptWebview.en}`, destination: "/en/webview/hotelreservation/my-stay/receipt", }, { source: `${preliminaryReceiptWebview.sv}`, destination: "/sv/webview/hotelreservation/my-stay/receipt", }, { source: `${preliminaryReceiptWebview.da}`, destination: "/da/webview/hotelreservation/my-stay/receipt", }, { source: `${preliminaryReceiptWebview.de}`, destination: "/de/webview/hotelreservation/my-stay/receipt", }, { source: `${preliminaryReceiptWebview.fi}`, destination: "/fi/webview/hotelreservation/my-stay/receipt", }, { source: `${preliminaryReceiptWebview.no}`, destination: "/no/webview/hotelreservation/my-stay/receipt", }, // Sitemap { source: `/sitemap-:id(\\d{1,}).xml`, destination: `/sitemap/:id`, }, { source: `/sitemap-index.xml`, destination: `/sitemap`, }, ], } }, } export default Sentry.withSentryConfig(nextConfig, { org: "scandic-hotels", project: "scandic-web", authToken: process.env.SENTRY_AUTH_TOKEN, // Only print logs for uploading source maps in CI silent: !process.env.CI, // Upload a larger set of source maps for prettier stack traces (increases build time) widenClientFileUpload: true, // Automatically annotate React components to show their full name in breadcrumbs and session replay reactComponentAnnotation: { enabled: true, }, disableLogger: true, })