import type { NextConfig } from "next"; const nextConfig = { cacheComponents: true, reactCompiler: true, pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"], images: { qualities: [50, 75, 100], remotePatterns: [ { protocol: "https", hostname: "ijyxfbpcm3itvdly.public.blob.vercel-storage.com", }, { protocol: "https", hostname: "avatars.githubusercontent.com", }, ], }, outputFileTracingIncludes: { "/notes/[slug]/opengraph-image": [ "./notes/**/*", "./app/opengraph-image.jpg", "./node_modules/**/@fontsource/inter/files/inter-latin-400-normal.woff", "./node_modules/**/@fontsource/inter/files/inter-latin-600-normal.woff", ], }, productionBrowserSourceMaps: true, experimental: { viewTransition: true, serverActions: { // fix CSRF errors from tor reverse proxy allowedOrigins: [ "jarv.is", ...(process.env.NEXT_PUBLIC_ONION_DOMAIN ? [process.env.NEXT_PUBLIC_ONION_DOMAIN] : []), ], }, staleTimes: { dynamic: 0, // disable client-side router cache for dynamic pages }, }, headers: async () => [ // https://community.torproject.org/onion-services/advanced/onion-location/ // oxlint-disable-next-line unicorn/no-useless-spread ...(process.env.NEXT_PUBLIC_ONION_DOMAIN ? [ { // only needed on actual pages, not static assets, so make a best effort by matching any path **without** a file // extension (aka a period) and/or an underscore (e.g. /_next/image). source: "/:path([^._]*)", headers: [ { key: "onion-location", value: `http://${process.env.NEXT_PUBLIC_ONION_DOMAIN}/:path`, }, ], }, ] : []), ], rewrites: async () => [ { // https://github.com/jakejarvis/tweets source: "/tweets/:path*", destination: "https://tweets-khaki.vercel.app/:path*", }, { source: "/y2k/:path*", destination: "https://y2k.pages.dev/:path*", }, ], redirects: async () => [ { source: "/pubkey.asc", destination: "https://keys.openpgp.org/pks/lookup?op=get&options=mr&search=0x3bc6e5776bf379d36f6714802b0c9cf251e69a39", permanent: false, }, // NOTE: don't remove this, it ensures de-AMPing the site hasn't offended our google overlords too badly! // https://developers.google.com/search/docs/advanced/experience/remove-amp#remove-only-amp { source: "/notes/:slug/amp.html", destination: "/notes/:slug", permanent: true, }, // mastodon via subdomain: // https://docs.joinmastodon.org/admin/config/#web_domain { source: "/.well-known/:path(host-meta|webfinger|nodeinfo)", destination: "https://fediverse.jarv.is/.well-known/:path", permanent: true, }, { source: "/@jake:path(/?|/.*)", destination: "https://fediverse.jarv.is/@jake:path", permanent: true, }, // remnants of previous sites/CMSes: { source: "/index.xml", destination: "/feed.xml", permanent: true }, { source: "/feed", destination: "/feed.xml", permanent: true }, { source: "/rss", destination: "/feed.xml", permanent: true }, { source: "/blog/(.*)", destination: "/notes", permanent: true }, { source: "/archives/(.*)", destination: "/notes", permanent: true }, // WordPress permalinks: { source: "/2016/02/28/millenial-with-hillary-clinton", destination: "/notes/millenial-with-hillary-clinton", permanent: true, }, { source: "/2018/12/04/how-to-shrink-linux-virtual-disk-vmware", destination: "/notes/how-to-shrink-linux-virtual-disk-vmware", permanent: true, }, { source: "/2018/12/10/cool-bash-tricks-for-your-terminal-dotfiles", destination: "/notes/cool-bash-tricks-for-your-terminal-dotfiles", permanent: true, }, ], } satisfies NextConfig; // my own macgyvered version of next-compose-plugins (RIP) const nextPlugins: Array< (config: NextConfig) => NextConfig | [(config: NextConfig) => NextConfig, any] > = [ require("@next/mdx")({ options: { remarkPlugins: [ "remark-frontmatter", "remark-mdx-frontmatter", "remark-gfm", "remark-smartypants", ], rehypePlugins: [ "rehype-unwrap-images", "rehype-slug", [ "rehype-wrapper", { className: "markdown", }, ], "rehype-mdx-code-props", "rehype-mdx-import-media", ], }, }), ]; export default (): NextConfig => nextPlugins.reduce( (acc, plugin) => (Array.isArray(plugin) ? plugin[0](acc, plugin[1]) : plugin(acc)), nextConfig, );