Files
supabase/apps/studio/next.config.ts
T
9eab4f8fbf build(studio): Vite/TanStack-Start build pipeline behind flag (stack 1/6, from #46424) (#47107)
**Stack 1/6** of the TanStack Start migration (#46424), split into
reviewable, independently-mergeable PRs.

> [!IMPORTANT]
> **Next stays the default and only active framework after this PR.**
This wires up the Vite/TanStack-Start build pipeline behind the
`STUDIO_FRAMEWORK` flag, but there are no TanStack routes yet — so the
TanStack build isn't functional or tested until later PRs in the stack.
Nothing about the Next build, dev, or deploy changes behaviourally here.

## What's in this PR
- **Dispatch:** `dev`/`build`/`start` now go through
`scripts/dispatch.js`, which runs the Next variant unless
`STUDIO_FRAMEWORK=tanstack`. The original commands are preserved as
`dev:next`/`build:next`/`start:next`.
- **Build pipeline:** `vite.config.ts`, `serve.js`, `smoke-server.mjs`,
vite/tanstack deps, `turbo.jsonc`.
- **`tsconfig.json`:** `jsx: react-jsx`, `moduleResolution: Bundler`,
`target: ES2022`. Because `include` is `**/*.ts(x)`, this re-typechecks
the whole app, so the companion adaptations below land with it.
- **Shared adaptations (companions to the tsconfig change):**
`BufferSource` casts, `packages/ui` unused-`React` import removals, etc.
- **Routing/middleware plumbing:** `next.config.ts` +
`redirects.shared.ts` (redirect rules now shared with `vercel.ts`),
`proxy.ts`/`start.ts` middleware + `hosted-api-allowlist.ts`.

## Verification
Run locally off `master`: frozen install ✓, `studio` typecheck ✓, **Next
build ✓** (compiles + generates all routes), lint ratchet ✓ ("some rules
improved"), prettier ✓.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a hosted API endpoint allowlist to return 404 for non-supported
`/api/*` routes.
* Introduced a TanStack route-migration checklist and expanded TanStack
Start routing support.
* **Improvements**
* Enhanced deployment refresh/detection by tightening cookie handling
for “latest deployment” updates.
* Centralized redirect/maintenance-mode rules for consistent platform vs
self-hosted behavior.
* Improved production serving with a dedicated static + proxy server and
a post-build smoke test.
* **Dependencies**
* Updated TanStack-related packages and React Table/query tooling
versions.
* **Documentation / Chores**
* Updated formatting and tooling config; added shared build environment
parsing utilities.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
2026-06-24 17:55:22 +08:00

251 lines
7.5 KiB
TypeScript

/* eslint-disable no-restricted-exports */
import bundleAnalyzer from '@next/bundle-analyzer'
import { withSentryConfig } from '@sentry/nextjs'
import type { NextConfig } from 'next'
import { getCSP } from './csp'
import {
getMaintenanceRedirects,
PLATFORM_REDIRECTS,
SELF_HOSTED_REDIRECTS,
SHARED_REDIRECTS,
} from './redirects.shared'
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})
function getAssetPrefix() {
// If not force enabled, but not production env, disable CDN
if (process.env.FORCE_ASSET_CDN !== '1' && process.env.VERCEL_ENV !== 'production') {
return undefined
}
// Force disable CDN
if (process.env.FORCE_ASSET_CDN === '-1') {
return undefined
}
const SUPABASE_ASSETS_URL =
process.env.NEXT_PUBLIC_ENVIRONMENT === 'staging'
? 'https://frontend-assets.supabase.green'
: 'https://frontend-assets.supabase.com'
return `${SUPABASE_ASSETS_URL}/${process.env.SITE_NAME}/${process.env.VERCEL_GIT_COMMIT_SHA?.substring(0, 12) ?? 'unknown'}`
}
const marketplaceApiUrl = process.env.NEXT_PUBLIC_MARKETPLACE_API_URL
? new URL(process.env.NEXT_PUBLIC_MARKETPLACE_API_URL)
: null
const marketplaceApiProtocol: 'http' | 'https' | null =
marketplaceApiUrl?.protocol === 'https:'
? 'https'
: marketplaceApiUrl?.protocol === 'http:'
? 'http'
: null
// Use `satisfies` instead of `: NextConfig` so TypeScript preserves narrow
// inferred types (e.g. async headers → Promise). This avoids TS2345 when
// wrapper functions (bundle-analyzer, sentry) resolve their `next` peer
// types to a different major version than studio's own next dependency.
const nextConfig = {
basePath: process.env.NEXT_PUBLIC_BASE_PATH,
assetPrefix: getAssetPrefix(),
output: 'standalone',
experimental: {
clientRouterFilter: false,
},
async rewrites() {
return [
{
source: `/.well-known/vercel/flags`,
destination: `https://supabase.com/.well-known/vercel/flags`,
basePath: false as const,
},
]
},
async redirects() {
// Rules live in `redirects.shared.ts` (shared with `vercel.ts`). Next
// auto-prepends `basePath` to source and destination on its own,
// except for the special `/` → basePath bounce below which opts out
// via `basePath: false`.
const isPlatform = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true'
const maintenance = process.env.MAINTENANCE_MODE === 'true'
return [
...(isPlatform ? PLATFORM_REDIRECTS : SELF_HOSTED_REDIRECTS),
...SHARED_REDIRECTS,
...(process.env.NEXT_PUBLIC_BASE_PATH?.length
? [
{
source: '/',
destination: process.env.NEXT_PUBLIC_BASE_PATH,
basePath: false as const,
permanent: false,
},
]
: []),
...getMaintenanceRedirects(maintenance),
]
},
async headers() {
return [
{
source: '/(.*?)',
headers: [
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-Content-Type-Options',
value: 'no-sniff',
},
{
key: 'Strict-Transport-Security',
value:
process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' && process.env.VERCEL === '1'
? 'max-age=31536000; includeSubDomains; preload'
: '',
},
{
key: 'Content-Security-Policy',
value:
process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' ? getCSP() : "frame-ancestors 'none';",
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
],
},
{
source: '/.well-known/vercel/flags',
headers: [
{
key: 'content-type',
value: 'application/json',
},
],
},
{
source: '/img/:slug*',
headers: [{ key: 'cache-control', value: 'public, max-age=2592000' }],
},
{
source: '/favicon/:slug*',
headers: [{ key: 'cache-control', value: 'public, max-age=86400' }],
},
{
source: '/(.*).ts',
headers: [{ key: 'content-type', value: 'text/typescript' }],
},
]
},
images: {
dangerouslyAllowSVG: false,
remotePatterns: [
{
protocol: 'https',
hostname: 'github.com',
port: '',
pathname: '**',
},
{
protocol: 'https',
hostname: 'avatars.githubusercontent.com',
port: '',
pathname: '/u/*',
},
{
protocol: 'https',
hostname: 'api-frameworks.vercel.sh',
port: '',
pathname: '**',
},
{
protocol: 'https',
hostname: 'vercel.com',
port: '',
pathname: '**',
},
...(marketplaceApiUrl
? [
{
...(marketplaceApiProtocol ? { protocol: marketplaceApiProtocol } : {}),
hostname: marketplaceApiUrl.hostname,
port: marketplaceApiUrl.port,
pathname: '**',
},
]
: []),
],
},
transpilePackages: ['ui', 'ui-patterns', 'common', 'shared-data', 'api-types', 'icons'],
serverExternalPackages: ['libpg-query'],
turbopack: {
rules: {
'*.md': {
loaders: ['raw-loader'],
as: '*.js',
},
// special case for Deno libs to be loaded as a raw text. They're passed as raw text to the Monaco editor.
'edge-runtime.d.ts': {
loaders: ['raw-loader'],
as: '*.js',
},
'lib.deno.d.ts': {
loaders: ['raw-loader'],
as: '*.js',
},
},
},
onDemandEntries: {
maxInactiveAge: 24 * 60 * 60 * 1000,
pagesBufferLength: 100,
},
typescript: {
// Typechecking is run via GitHub Action only for efficiency
// For production, we run typechecks separate from the build command (pnpm typecheck && pnpm build)
ignoreBuildErrors: true,
},
} satisfies NextConfig
// Make sure adding Sentry options is the last code to run before exporting, to
// ensure that your source maps include changes from all other Webpack plugins
const platformConfig =
process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' ? withBundleAnalyzer(nextConfig) : nextConfig
export default process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' && process.env.VERCEL === '1'
? withSentryConfig(platformConfig, {
silent: true,
// For all available options, see:
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
// 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,
},
// Automatically tree-shake Sentry logger statements to reduce bundle size
disableLogger: true,
// Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
// See the following for more information:
// https://docs.sentry.io/product/crons/
// https://vercel.com/docs/cron-jobs
automaticVercelMonitors: true,
// Annotate bundles at build time so thirdPartyErrorFilterIntegration can
// distinguish our code from browser extensions / injected scripts at runtime.
unstable_sentryWebpackPluginOptions: {
applicationKey: 'supabase-studio',
},
})
: platformConfig