chore(studio): inline NEXT_PUBLIC_* env vars in the Vite build

Use vite's loadEnv to read the .env hierarchy + process.env, filter to
NEXT_PUBLIC_*, and pass them to define so process.env.NEXT_PUBLIC_X is
replaced with its value at build time (matching Next.js behaviour).

Without this, every process.env.NEXT_PUBLIC_* access in the browser
bundle would be undefined since process isn't defined in browsers.
This commit is contained in:
Alaister Young committed 2026-04-21 16:15:11 +08:00
1 parent 3dc3e4cd5e
commit bca8a7a0cf
1 file changed
+39 -26
+39 -26
View File
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url'
import { devtools } from '@tanstack/devtools-vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import viteReact from '@vitejs/plugin-react'
import { defineConfig, type Plugin } from 'vite'
import { defineConfig, loadEnv, type Plugin } from 'vite'
const rootDir = path.dirname(fileURLToPath(import.meta.url))
const compatRoot = path.resolve(rootDir, 'compat/next')
@@ -48,29 +48,42 @@ function nextCompat(): Plugin {
}
}
export default defineConfig({
server: {
port: 3000,
},
resolve: {
tsconfigPaths: true,
},
ssr: {
// `lodash` is CJS; its named-export interop fails in Node ESM unless bundled.
// `next/*` must be bundled so our nextCompat shim wins — otherwise Vite's
// SSR externalizer leaves `next/router` as a runtime package import and
// Node resolves it to Next's real module.
noExternal: ['lodash', /^next(\/|$)/],
},
plugins: [
nextCompat(),
devtools(),
tanstackStart({
srcDirectory: './',
spa: {
enabled: true,
},
}),
viteReact(),
],
export default defineConfig(({ mode }) => {
// Inline NEXT_PUBLIC_* env vars at build time so `process.env.NEXT_PUBLIC_*`
// works in the browser bundle (mirrors Next.js behaviour). loadEnv reads the
// standard .env file hierarchy and merges with process.env.
const env = loadEnv(mode, rootDir, '')
const publicEnvDefines = Object.fromEntries(
Object.entries(env)
.filter(([key]) => key.startsWith('NEXT_PUBLIC_'))
.map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)])
)
return {
server: {
port: 3000,
},
resolve: {
tsconfigPaths: true,
},
define: publicEnvDefines,
ssr: {
// `lodash` is CJS; its named-export interop fails in Node ESM unless bundled.
// `next/*` must be bundled so our nextCompat shim wins — otherwise Vite's
// SSR externalizer leaves `next/router` as a runtime package import and
// Node resolves it to Next's real module.
noExternal: ['lodash', /^next(\/|$)/],
},
plugins: [
nextCompat(),
devtools(),
tanstackStart({
srcDirectory: './',
spa: {
enabled: true,
},
}),
viteReact(),
],
}
})