chore(studio): switch to type: module and convert configs to ESM/TS (#44839)

Switch studio's package.json to `"type": "module"` so the package runs
as native ESM. This aligns the runtime module system with what we
actually write (`import`/`export`), improves tree-shaking, and reduces
friction with ESM-only dependencies.

**Changed:**
- `next.config.js` → `next.config.ts` – ESM imports/exports, proper TS
types, fixed type narrowing on redirect `has` and `basePath` fields
- `csp.js` → `csp.ts` – `module.exports.getCSP` → named `export
function`
- `tailwind.config.js` → `tailwind.config.ts` – ESM imports
- `postcss.config.js` – `module.exports` → `export default` (stays `.js`
since PostCSS doesn't support TS configs)

**Removed:**
- Unused `path` import in next config
- Deprecated Sentry `hideSourceMaps` option (default behavior in Sentry
v10)

**Added:**
- Type declaration for `config/tailwind.config` CJS package

## To test

- A general smoke test of studio should suffice

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

* **Refactor**
* Modernized the Studio package to ES module style and improved
TypeScript typings and config declarations to reduce build/runtime
issues.
* Updated styling and post-processing configuration format for more
consistent tooling behavior.

* **Chores**
* Updated code ownership entries to reflect migrated/renamed
configuration files.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
This commit is contained in:
Alaister YoungandAlaister Young authored and GitHub committed 2026-04-14 18:44:28 +09:00
1 parent 14e9f4ccef
commit f8183479e7
12 files changed
+61 -52

No files matched your search

+1 -1
View File
@@ -14,7 +14,7 @@
/docker/ @supabase/cli @supabase/self-hosted
/apps/studio/csp.js @supabase/security
/apps/studio/csp.ts @supabase/security
/apps/studio/components/interfaces/Billing/Payment @supabase/security
/apps/studio/components/interfaces/Organization/Documents/ @supabase/security
/apps/studio/pages/new/index.tsx @supabase/security
@@ -40,7 +40,7 @@ const Streamdown = dynamic<StreamdownProps>(
// Streamdown splits ordered lists with complex content (e.g. code blocks) into
// separate <ol> elements. The `start` attribute preserves semantics for screen
// readers, while `counterReset` is what actually fixes the visible numbering —
// the prose config (tailwind.config.js) uses a custom CSS counter named "item"
// the prose config (tailwind.config.ts) uses a custom CSS counter named "item"
// with `listStyleType: 'none'`, so the `start` attribute alone has no visual effect.
export const OrderedList = memo(({ children, start }: { children?: ReactNode; start?: number }) => (
<ol
+1 -1
View File
@@ -203,7 +203,7 @@ export const AIEditor = ({
})
if (language === 'javascript' || language === 'typescript') {
// The Deno libs are loaded as a raw text via raw-loader in next.config.js. They're passed as raw text to the
// The Deno libs are loaded as a raw text via raw-loader in next.config.ts. They're passed as raw text to the
// Monaco editor.
import('@/public/deno/edge-runtime.d.ts' as string)
.then((module) => {
+1 -1
View File
@@ -80,7 +80,7 @@ const PUSHER_URL_WS = 'wss://*.pusher.com'
const GOOGLE_MAPS_API_URL = 'https://maps.googleapis.com'
module.exports.getCSP = function getCSP() {
export function getCSP() {
const DEFAULT_SRC_URLS = [
API_URL,
SUPABASE_URL,
+1 -1
View File
@@ -9,7 +9,7 @@
const { defineConfig } = require('eslint/config')
const tsparser = require('@typescript-eslint/parser')
const requireSafeSqlFragment = require('./eslint-rules/require-safe-sql-fragment')
const requireSafeSqlFragment = require('./eslint-rules/require-safe-sql-fragment.cjs')
const studioPlugin = {
rules: {
+1 -1
View File
@@ -105,7 +105,7 @@ Sentry.init({
// Tag errors whose stack trace only contains third-party frames (browser extensions,
// injected scripts, etc.). This uses build-time code annotation via the applicationKey
// in next.config.js to reliably distinguish our code from third-party code.
// in next.config.ts to reliably distinguish our code from third-party code.
// We use 'apply-tag' instead of 'drop' so that beforeSend can exempt error boundary
// crashes — these may originate in third-party code but are caused by first-party bugs.
return [
@@ -1,11 +1,14 @@
const { withSentryConfig } = require('@sentry/nextjs')
const withBundleAnalyzer = require('@next/bundle-analyzer')({
/* 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'
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})
const { getCSP } = require('./csp')
// Required for nextjs standalone build
const path = require('path')
function getAssetPrefix() {
// If not force enabled, but not production env, disable CDN
@@ -23,13 +26,10 @@ function getAssetPrefix() {
? '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)}`
return `${SUPABASE_ASSETS_URL}/${process.env.SITE_NAME}/${process.env.VERCEL_GIT_COMMIT_SHA?.substring(0, 12) ?? 'unknown'}`
}
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
const nextConfig: NextConfig = {
basePath: process.env.NEXT_PUBLIC_BASE_PATH,
assetPrefix: getAssetPrefix(),
output: 'standalone',
@@ -53,7 +53,7 @@ const nextConfig = {
source: '/',
has: [
{
type: 'query',
type: 'query' as const,
key: 'next',
value: 'new-project',
},
@@ -468,7 +468,7 @@ const nextConfig = {
{
source: '/',
destination: process.env.NEXT_PUBLIC_BASE_PATH,
basePath: false,
basePath: false as const,
permanent: false,
},
]
@@ -616,38 +616,34 @@ const nextConfig = {
const platformConfig =
process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' ? withBundleAnalyzer(nextConfig) : nextConfig
module.exports =
process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' && process.env.VERCEL === '1'
? withSentryConfig(platformConfig, {
silent: true,
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/
// 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,
// 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 annotate React components to show their full name in breadcrumbs and session replay
reactComponentAnnotation: {
enabled: true,
},
// Hides source maps from generated client bundles
hideSourceMaps: true,
// Automatically tree-shake Sentry logger statements to reduce bundle size
disableLogger: 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,
// 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
// Annotate bundles at build time so thirdPartyErrorFilterIntegration can
// distinguish our code from browser extensions / injected scripts at runtime.
unstable_sentryWebpackPluginOptions: {
applicationKey: 'supabase-studio',
},
})
: platformConfig
+1
View File
@@ -2,6 +2,7 @@
"name": "studio",
"version": "0.0.9",
"private": true,
"type": "module",
"scripts": {
"preinstall": "npx only-allow pnpm",
"dev": "next dev -p ${STUDIO_PORT:-8082}",
+4 -1
View File
@@ -1,4 +1,7 @@
module.exports = {
/* eslint-disable no-restricted-exports */
/* eslint-disable import/no-anonymous-default-export */
export default {
plugins: {
tailwindcss: {},
},
@@ -1,6 +1,9 @@
const config = require('config/tailwind.config')
/* eslint-disable no-restricted-exports */
module.exports = config({
import containerQueries from '@tailwindcss/container-queries'
import tailwindConfig from 'config/tailwind.config'
export default tailwindConfig({
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
@@ -8,7 +11,7 @@ module.exports = config({
'./../../packages/ui/src/**/*.{tsx,ts,js}',
'./../../packages/ui-patterns/src/**/*.{tsx,ts,js}',
],
plugins: [require('@tailwindcss/container-queries')],
plugins: [containerQueries],
theme: {
extend: {
fontSize: {
+6
View File
@@ -0,0 +1,6 @@
declare module 'config/tailwind.config' {
import type { Config } from 'tailwindcss'
const wrapper: (tailwindConfig: Partial<Config>) => Config
// eslint-disable-next-line no-restricted-exports
export default wrapper
}