start moving to tanstack router

This commit is contained in:
Alaister Young committed 2025-05-29 21:43:04 +08:00
1 parent ca1c1cd81a
commit 523ecb9d6e
14 files changed
+834 -907

No files matched your search

+1 -3
View File
@@ -8,11 +8,9 @@
# testing
/coverage
# react-router
/.react-router/
# production
/build
/dist
# misc
.DS_Store
@@ -1,8 +1,8 @@
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
import { useIsLoggedIn } from 'common'
import { get, handleError } from 'data/fetchers'
import { IS_PLATFORM } from 'lib/constants'
import { useProfile } from 'lib/profile'
import type { Permission, ResponseError } from 'types'
import { permissionKeys } from './keys'
@@ -23,14 +23,14 @@ export const usePermissionsQuery = <TData = PermissionsData>({
enabled = true,
...options
}: UseQueryOptions<PermissionsData, PermissionsError, TData> = {}) => {
const { profile } = useProfile()
const isSignedIn = useIsLoggedIn()
return useQuery<PermissionsData, PermissionsError, TData>(
permissionKeys.list(),
({ signal }) => getPermissions(signal),
{
...options,
enabled: IS_PLATFORM && enabled && profile !== undefined,
enabled: IS_PLATFORM && enabled && isSignedIn,
staleTime: 30 * 60 * 1000,
}
)
+12
View File
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Supabase</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/main.tsx"></script>
</body>
</html>
+3 -3
View File
@@ -1,4 +1,4 @@
import { Link as RemixLink } from 'react-router'
import { Link as TanstackRouterLink } from '@tanstack/react-router'
export default function Link({
children,
@@ -11,8 +11,8 @@ export default function Link({
passHref?: boolean
}) {
return (
<RemixLink to={href} {...props}>
<TanstackRouterLink to={href} {...props}>
{children}
</RemixLink>
</TanstackRouterLink>
)
}
+4 -13
View File
@@ -1,26 +1,17 @@
import { useLocation, useParams, useSearchParams, useNavigate } from 'react-router'
import { useLocation, useParams, useSearch, useNavigate } from '@tanstack/react-router'
import { BASE_PATH } from 'lib/constants'
import { NextRouter } from 'next/router'
import { parse } from 'querystring'
import { useMemo } from 'react'
export function useRouter(): NextRouter {
const params = useParams()
const [searchParams] = useSearchParams()
const params = useParams({ strict: false })
const searchParams = useSearch({ strict: false })
const location = useLocation()
const navigate = useNavigate()
const query = useMemo(() => {
let q: { [key: string]: string | string[] | undefined } = Object.fromEntries(
Array.from(new Set(searchParams.keys())).map((key) => {
const values = searchParams.getAll(key)
return [key, values.length > 1 ? values : values[0]]
})
)
q = { ...q, ...params }
return q
return { ...params, ...searchParams }
}, [params, searchParams])
return {
+22
View File
@@ -0,0 +1,22 @@
import { StrictMode } from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
const router = createRouter({ routeTree })
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}
const rootElement = document.getElementById('root')!
if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement)
root.render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>
)
}
+12 -8
View File
@@ -5,9 +5,9 @@
"type": "module",
"scripts": {
"preinstall": "npx only-allow pnpm",
"dev": "next dev --turbopack -p 8082",
"dev": "vite",
"dev:secrets:pull": "AWS_PROFILE=supabase-dev node ../../scripts/getSecrets.js -n local/studio",
"build": "next build && ./../../scripts/upload-static-assets.sh",
"build": "vite build && ./../../scripts/upload-static-assets.sh",
"start": "next start",
"lint": "next lint",
"clean": "rimraf node_modules tsconfig.tsbuildinfo .next .turbo",
@@ -18,7 +18,7 @@
"test:ci": "vitest --run --coverage",
"test:report": "open coverage/lcov-report/index.html",
"deploy:staging": "VERCEL_ORG_ID=team_E6KJ1W561hMTjon1QSwOh0WO VERCEL_PROJECT_ID=QmcmhbiAtCMFTAHCuGgQscNbke4TzgWULECctNcKmxWCoT vercel --prod -A .vercel/staging.json",
"typecheck": "react-router typegen && tsc --noEmit",
"typecheck": "tsc --noEmit",
"prettier:check": "prettier --check .",
"prettier:write": "prettier --write .",
"build:deno-types": "tsx scripts/deno-types.ts"
@@ -39,9 +39,7 @@
"@heroicons/react": "^2.1.3",
"@hookform/resolvers": "^3.1.1",
"@monaco-editor/react": "^4.6.0",
"@next/bundle-analyzer": "15.3.1",
"@number-flow/react": "^0.3.2",
"@sentry/nextjs": "^8.52.1",
"@std/path": "npm:@jsr/std__path@^1.0.8",
"@stripe/react-stripe-js": "^3.1.1",
"@stripe/stripe-js": "^5.5.0",
@@ -53,9 +51,10 @@
"@supabase/supabase-js": "catalog:",
"@tanstack/react-query": "4.35.7",
"@tanstack/react-query-devtools": "4.35.7",
"@tanstack/react-router": "^1.114.27",
"@tanstack/react-router-devtools": "^1.120.11",
"@uidotdev/usehooks": "^2.4.1",
"@vercel/flags": "^2.6.0",
"@vitejs/plugin-react": "^4.3.4",
"@zip.js/zip.js": "^2.7.29",
"ai": "^3.4.33",
"ai-commands": "workspace:*",
@@ -133,10 +132,11 @@
"zxcvbn": "^4.4.2"
},
"devDependencies": {
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
"@radix-ui/react-use-escape-keydown": "^1.0.3",
"@react-router/dev": "^7.1.1",
"@supabase/postgres-meta": "^0.64.4",
"@tailwindcss/container-queries": "^0.1.1",
"@tanstack/router-plugin": "^1.120.11",
"@testing-library/dom": "^10.0.0",
"@testing-library/jest-dom": "^6.6.0",
"@testing-library/react": "^16.0.0",
@@ -164,6 +164,7 @@
"@types/sqlstring": "^2.3.0",
"@types/uuid": "^8.3.4",
"@types/zxcvbn": "^4.4.1",
"@vitejs/plugin-react": "^4.3.4",
"@vitest/coverage-v8": "^3.0.9",
"@vitest/ui": "^3.0.0",
"api-types": "workspace:*",
@@ -181,10 +182,13 @@
"prettier": "3.2.4",
"raw-loader": "^4.0.2",
"require-in-the-middle": "^7.5.2",
"rollup-plugin-node-polyfills": "^0.2.1",
"tailwindcss": "^3.4.1",
"tsx": "^4.19.3",
"typescript": "~5.5.0",
"vite": "^6.2.6",
"vite": "^6.3.5",
"vite-plugin-circular-dependency": "^0.5.0",
"vite-plugin-cjs-interop": "^2.2.0",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^3.0.5"
}
-6
View File
@@ -1,6 +0,0 @@
import type { Config } from '@react-router/dev/config'
export default {
basename: process.env.NEXT_PUBLIC_BASE_PATH ?? '/',
ssr: false,
} satisfies Config
+88
View File
@@ -0,0 +1,88 @@
/* eslint-disable */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
// Import Routes
import { Route as rootRoute } from './routes/__root'
import { Route as IndexImport } from './routes/index'
// Create/Update Routes
const IndexRoute = IndexImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRoute,
} as any)
// Populate the FileRoutesByPath interface
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexImport
parentRoute: typeof rootRoute
}
}
}
// Create and export the route tree
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
}
export interface FileRoutesById {
__root__: typeof rootRoute
'/': typeof IndexRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/'
fileRoutesByTo: FileRoutesByTo
to: '/'
id: '__root__' | '/'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
}
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
}
export const routeTree = rootRoute
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
/* ROUTE_MANIFEST_START
{
"routes": {
"__root__": {
"filePath": "__root.tsx",
"children": [
"/"
]
},
"/": {
"filePath": "index.tsx"
}
}
}
ROUTE_MANIFEST_END */
+161
View File
@@ -0,0 +1,161 @@
import { createRootRoute, Outlet } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
import 'react-data-grid/lib/styles.css'
import 'styles/code.scss'
import 'styles/contextMenu.scss'
import 'styles/date-picker.scss'
import 'styles/editor.scss'
import 'styles/graphiql-base.scss'
import 'styles/grid.scss'
import 'styles/main.scss'
import 'styles/markdown-preview.scss'
import 'styles/monaco.scss'
import 'styles/react-data-grid-logs.scss'
import 'styles/reactflow.scss'
import 'styles/storage.scss'
import 'styles/stripe.scss'
import 'styles/toast.scss'
import 'styles/ui.scss'
import 'ui/build/css/themes/dark.css'
import 'ui/build/css/themes/light.css'
import { loader } from '@monaco-editor/react'
import * as Sentry from '@sentry/nextjs'
import { Hydrate, QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import dayjs from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat'
import relativeTime from 'dayjs/plugin/relativeTime'
import timezone from 'dayjs/plugin/timezone'
import utc from 'dayjs/plugin/utc'
import Head from 'next/head'
import { NuqsAdapter } from 'nuqs/adapters/next/pages'
import { ErrorInfo } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import { FeatureFlagProvider, ThemeProvider, useThemeSandbox } from 'common'
import MetaFaviconsPagesRouter from 'common/MetaFavicons/pages-router'
import { RouteValidationWrapper } from 'components/interfaces/App'
import { AppBannerContextProvider } from 'components/interfaces/App/AppBannerWrapperContext'
import { StudioCommandMenu } from 'components/interfaces/App/CommandMenu'
import { FeaturePreviewContextProvider } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import FeaturePreviewModal from 'components/interfaces/App/FeaturePreview/FeaturePreviewModal'
import { MonacoThemeProvider } from 'components/interfaces/App/MonacoThemeProvider'
import { GenerateSql } from 'components/interfaces/SqlGenerator/SqlGenerator'
import { ErrorBoundaryState } from 'components/ui/ErrorBoundaryState'
import { useRootQueryClient } from 'data/query-client'
// import { customFont, sourceCodePro } from 'fonts'
import { AuthProvider } from 'lib/auth'
import { getFlags as getConfigCatFlags } from 'lib/configcat'
import { API_URL, BASE_PATH, IS_PLATFORM } from 'lib/constants'
import { ProfileProvider } from 'lib/profile'
import { Telemetry } from 'lib/telemetry'
import HCaptchaLoadedStore from 'stores/hcaptcha-loaded-store'
import { AppPropsWithLayout } from 'types'
import { SonnerToaster, TooltipProvider } from 'ui'
import { CommandProvider } from 'ui-patterns/CommandMenu'
dayjs.extend(customParseFormat)
dayjs.extend(utc)
dayjs.extend(timezone)
dayjs.extend(relativeTime)
loader.config({
// [Joshen] Attempt for offline support/bypass ISP issues is to store the assets required for monaco
// locally. We're however, only storing the assets which we need (based on what the network tab loads
// while using monaco). If we end up facing more effort trying to maintain this, probably to either
// use cloudflare or find some way to pull all the files from a CDN via a CLI, rather than tracking individual files
// The alternative was to import * as monaco from 'monaco-editor' but i couldn't get it working
paths: {
vs: IS_PLATFORM
? 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.52.2/min/vs'
: `${BASE_PATH}/monaco-editor`,
},
})
// [Joshen TODO] Once we settle on the new nav layout - we'll need a lot of clean up in terms of our layout components
// a lot of them are unnecessary and introduce way too many cluttered CSS especially with the height styles that make
// debugging way too difficult. Ideal scenario is we just have one AppLayout to control the height and scroll areas of
// the dashboard, all other layout components should not be doing that
function CustomApp() {
const queryClient = useRootQueryClient()
const errorBoundaryHandler = (error: Error, info: ErrorInfo) => {
Sentry.withScope(function (scope) {
scope.setTag('globalErrorBoundary', true)
Sentry.captureException(error)
})
console.error(error.stack)
}
useThemeSandbox()
const isTestEnv = process.env.NEXT_PUBLIC_NODE_ENV === 'test'
return (
<ErrorBoundary FallbackComponent={ErrorBoundaryState} onError={errorBoundaryHandler}>
<QueryClientProvider client={queryClient}>
<NuqsAdapter>
<AuthProvider>
<FeatureFlagProvider
API_URL={API_URL}
enabled={IS_PLATFORM}
getConfigCatFlags={getConfigCatFlags}
>
<ProfileProvider>
<Head>
<title>Supabase</title>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
{/* [Alaister]: This has to be an inline style tag here and not a separate component due to next/font */}
{/* <style
dangerouslySetInnerHTML={{
__html: `:root{--font-custom:${customFont.style.fontFamily};--font-source-code-pro:${sourceCodePro.style.fontFamily};}`,
}}
/> */}
</Head>
<MetaFaviconsPagesRouter applicationName="Supabase Studio" />
<TooltipProvider delayDuration={0}>
<RouteValidationWrapper>
<ThemeProvider
defaultTheme="system"
themes={['dark', 'light', 'classic-dark']}
enableSystem
disableTransitionOnChange
>
<AppBannerContextProvider>
<CommandProvider>
<FeaturePreviewContextProvider>
<Outlet />
<StudioCommandMenu />
<GenerateSql />
<FeaturePreviewModal />
</FeaturePreviewContextProvider>
<SonnerToaster position="top-right" />
<MonacoThemeProvider />
</CommandProvider>
</AppBannerContextProvider>
</ThemeProvider>
</RouteValidationWrapper>
</TooltipProvider>
<Telemetry />
{!isTestEnv && (
<>
<HCaptchaLoadedStore />
<TanStackRouterDevtools />
<ReactQueryDevtools initialIsOpen={false} position="bottom-right" />
</>
)}
</ProfileProvider>
</FeatureFlagProvider>
</AuthProvider>
</NuqsAdapter>
</QueryClientProvider>
</ErrorBoundary>
)
}
export const Route = createRootRoute({
component: CustomApp,
})
+13
View File
@@ -0,0 +1,13 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/')({
component: Index,
})
function Index() {
return (
<div className="p-2">
<h3>Welcome Home!</h3>
</div>
)
}
+2 -9
View File
@@ -13,7 +13,7 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"rootDirs": [".", "./.react-router/types"],
"rootDirs": ["."],
"baseUrl": ".",
"downlevelIteration": true,
"incremental": true,
@@ -23,13 +23,6 @@
"useDefineForClassFields": true,
"types": ["node", "vite/client"]
},
"include": [
"**/*.ts",
"**/*.tsx",
"**/*.js",
"**/*.jsx",
"./../../packages/ui/src/**/*.d.ts",
".react-router/types/**/*"
],
"include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "./../../packages/ui/src/**/*.d.ts"],
"exclude": ["node_modules", "public/deno/*.ts"]
}
+9 -2
View File
@@ -1,5 +1,6 @@
import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill'
import { reactRouter } from '@react-router/dev/vite'
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
import react from '@vitejs/plugin-react'
import path from 'path'
import { defineConfig, loadEnv } from 'vite'
import circleDependency from 'vite-plugin-circular-dependency'
@@ -70,7 +71,13 @@ export default defineConfig(({ mode }) => {
'react-copy-to-clipboard',
],
}),
reactRouter(),
TanStackRouterVite({
target: 'react',
autoCodeSplitting: true,
routesDirectory: './routes',
generatedRouteTree: './routeTree.gen.ts',
}),
react(),
],
optimizeDeps: {
esbuildOptions: {
+504 -860
View File
File diff suppressed because it is too large. Load diff