diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index b827958d1bf0..2cda654167ed 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -2,14 +2,13 @@ import fs from 'fs' import path from 'path' import react from '@vitejs/plugin-react' -import type { InputOption } from 'rollup' -import type { ConfigEnv, UserConfig, PluginOption } from 'vite' +import type { PluginOption } from 'vite' import { normalizePath } from 'vite' import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getConfig, getPaths } from '@redwoodjs/project-config' -import { getEnvVarDefinitions } from './envVarDefinitions' +import { getDefaultViteConfig } from './lib/getDefaultViteConfig' import handleJsAsJsx from './plugins/vite-plugin-jsx-loader' import removeFromBundle from './plugins/vite-plugin-remove-from-bundle' import swapApolloProvider from './plugins/vite-plugin-swap-apollo-provider' @@ -126,114 +125,7 @@ export default function redwoodPluginVite(): PluginOption[] { }, // ---------- End Bundle injection ---------- - config: (options: UserConfig, env: ConfigEnv): UserConfig => { - let apiHost = process.env.REDWOOD_API_HOST - apiHost ??= rwConfig.api.host - apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' - - let apiPort - if (process.env.REDWOOD_API_PORT) { - apiPort = parseInt(process.env.REDWOOD_API_PORT) - } else { - apiPort = rwConfig.api.port - } - - return { - root: rwPaths.web.src, - // Disabling for now, let babel handle this for consistency - // resolve: { - // alias: [ - // { - // find: 'src', - // replacement: redwoodPaths.web.src, - // }, - // ], - // }, - envPrefix: 'REDWOOD_ENV_', - publicDir: path.join(rwPaths.web.base, 'public'), - define: getEnvVarDefinitions(), - css: { - // @NOTE config path is relative to where vite.config.js is if you use relative path - // postcss: './config/', - postcss: rwPaths.web.config, - }, - server: { - open: rwConfig.browser.open, - port: rwConfig.web.port, - host: true, // Listen to all hosts - proxy: { - [rwConfig.web.apiUrl]: { - target: `http://${apiHost}:${apiPort}`, - changeOrigin: false, - // Remove the `.redwood/functions` part, but leave the `/graphql` - rewrite: (path) => path.replace(rwConfig.web.apiUrl, ''), - configure: (proxy) => { - // @MARK: this is a hack to prevent showing confusing proxy errors on startup - // because Vite launches so much faster than the API server. - let waitingForApiServer = true - - // Wait for 2.5s, then restore regular proxy error logging - setTimeout(() => { - waitingForApiServer = false - }, 2500) - - proxy.on('error', (err, _req, res) => { - if ( - waitingForApiServer && - err.message.includes('ECONNREFUSED') - ) { - err.stack = - '⌛ API Server launching, please refresh your page...' - } - const msg = { - errors: [ - { - message: - 'The RedwoodJS API server is not available or is currently reloading. Please refresh.', - }, - ], - } - - res.writeHead(203, { - 'Content-Type': 'application/json', - 'Cache-Control': 'no-cache', - }) - res.write(JSON.stringify(msg)) - res.end() - }) - }, - }, - }, - }, - build: { - outDir: options.build?.outDir || rwPaths.web.dist, - emptyOutDir: true, - manifest: !env.ssrBuild ? 'client-build-manifest.json' : undefined, - sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, // Note that this can be boolean or 'inline' - rollupOptions: { - input: getRollupInput(!!env.ssrBuild), - }, - }, - legacy: { - buildSsrCjsExternalHeuristics: rwConfig.experimental?.rsc?.enabled - ? false - : env.ssrBuild, - }, - optimizeDeps: { - esbuildOptions: { - // @MARK this is because JS projects in Redwood don't have .jsx extensions - loader: { - '.js': 'jsx', - }, - // Node.js global to browser globalThis - // @MARK unsure why we need this, but required for DevFatalErrorPage atleast - define: { - global: 'globalThis', - }, - }, - }, - } - }, + config: getDefaultViteConfig(rwConfig, rwPaths), }, // We can remove when streaming is stable rwConfig.experimental.streamingSsr.enabled && swapApolloProvider(), @@ -259,31 +151,3 @@ export default function redwoodPluginVite(): PluginOption[] { }), ] } - -/** - * This function configures how vite (actually Rollup) will bundle. - * - * By default, the entry point is the index.html file - even if you don't specify it in RollupOptions - * - * With streaming SSR, out entrypoint is different - either entry.client.tsx or entry.server.tsx - * and the html file is not used at all, because it is defined in Document.tsx - * - * @param ssr {boolean} Whether to return the SSR inputs or not - * @returns Rollup input Options - */ -function getRollupInput(ssr: boolean): InputOption | undefined { - const rwConfig = getConfig() - const rwPaths = getPaths() - - // @NOTE once streaming ssr is out of experimental, this will become the default - if (rwConfig.experimental.streamingSsr.enabled) { - return ssr - ? { - 'entry.server': rwPaths.web.entryServer as string, - Document: rwPaths.web.document, // We need the document for React's fallback - } - : (rwPaths.web.entryClient as string) - } - - return rwPaths.web.html -} diff --git a/packages/vite/src/envVarDefinitions.ts b/packages/vite/src/lib/envVarDefinitions.ts similarity index 100% rename from packages/vite/src/envVarDefinitions.ts rename to packages/vite/src/lib/envVarDefinitions.ts diff --git a/packages/vite/src/lib/getDefaultViteConfig.ts b/packages/vite/src/lib/getDefaultViteConfig.ts new file mode 100644 index 000000000000..7f7fe77cb4b4 --- /dev/null +++ b/packages/vite/src/lib/getDefaultViteConfig.ts @@ -0,0 +1,149 @@ +import path from 'node:path' + +import type { InputOption } from 'rollup' +import type { ConfigEnv, UserConfig } from 'vite' + +import type { Config, Paths } from '@redwoodjs/project-config' +import { getConfig, getPaths } from '@redwoodjs/project-config' + +import { getEnvVarDefinitions } from './envVarDefinitions' + +export function getDefaultViteConfig(rwConfig: Config, rwPaths: Paths) { + return (options: UserConfig, env: ConfigEnv): UserConfig => { + let apiHost = process.env.REDWOOD_API_HOST + apiHost ??= rwConfig.api.host + apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' + + let apiPort + if (process.env.REDWOOD_API_PORT) { + apiPort = parseInt(process.env.REDWOOD_API_PORT) + } else { + apiPort = rwConfig.api.port + } + + return { + root: rwPaths.web.src, + // Disabling for now, let babel handle this for consistency + // resolve: { + // alias: [ + // { + // find: 'src', + // replacement: redwoodPaths.web.src, + // }, + // ], + // }, + envPrefix: 'REDWOOD_ENV_', + publicDir: path.join(rwPaths.web.base, 'public'), + define: getEnvVarDefinitions(), + css: { + // @NOTE config path is relative to where vite.config.js is if you use relative path + // postcss: './config/', + postcss: rwPaths.web.config, + }, + server: { + open: rwConfig.browser.open, + port: rwConfig.web.port, + host: true, // Listen to all hosts + proxy: { + [rwConfig.web.apiUrl]: { + target: `http://${apiHost}:${apiPort}`, + changeOrigin: false, + // Remove the `.redwood/functions` part, but leave the `/graphql` + rewrite: (path) => path.replace(rwConfig.web.apiUrl, ''), + configure: (proxy) => { + // @MARK: this is a hack to prevent showing confusing proxy errors on startup + // because Vite launches so much faster than the API server. + let waitingForApiServer = true + + // Wait for 2.5s, then restore regular proxy error logging + setTimeout(() => { + waitingForApiServer = false + }, 2500) + + proxy.on('error', (err, _req, res) => { + if ( + waitingForApiServer && + err.message.includes('ECONNREFUSED') + ) { + err.stack = + '⌛ API Server launching, please refresh your page...' + } + const msg = { + errors: [ + { + message: + 'The RedwoodJS API server is not available or is currently reloading. Please refresh.', + }, + ], + } + + res.writeHead(203, { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache', + }) + res.write(JSON.stringify(msg)) + res.end() + }) + }, + }, + }, + }, + build: { + outDir: options.build?.outDir || rwPaths.web.dist, + emptyOutDir: true, + manifest: !env.ssrBuild ? 'client-build-manifest.json' : undefined, + sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, // Note that this can be boolean or 'inline' + rollupOptions: { + input: getRollupInput(!!env.ssrBuild), + }, + }, + legacy: { + buildSsrCjsExternalHeuristics: rwConfig.experimental?.rsc?.enabled + ? false + : env.ssrBuild, + }, + optimizeDeps: { + esbuildOptions: { + // @MARK this is because JS projects in Redwood don't have .jsx extensions + loader: { + '.js': 'jsx', + }, + // Node.js global to browser globalThis + // @MARK unsure why we need this, but required for DevFatalErrorPage + // at least + define: { + global: 'globalThis', + }, + }, + }, + } + } +} + +/** + * This function configures how vite (actually Rollup) will bundle. + * + * By default, the entry point is the index.html file - even if you don't specify it in RollupOptions + * + * With streaming SSR, out entrypoint is different - either entry.client.tsx or entry.server.tsx + * and the html file is not used at all, because it is defined in Document.tsx + * + * @param ssr {boolean} Whether to return the SSR inputs or not + * @returns Rollup input Options + */ +function getRollupInput(ssr: boolean): InputOption | undefined { + const rwConfig = getConfig() + const rwPaths = getPaths() + + // @NOTE once streaming ssr is out of experimental, this will become the default + if (rwConfig.experimental.streamingSsr.enabled) { + return ssr + ? { + 'entry.server': rwPaths.web.entryServer as string, + Document: rwPaths.web.document, // We need the document for React's fallback + } + : (rwPaths.web.entryClient as string) + } + + return rwPaths.web.html +} diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index 66a00286b17a..62daf2b290f0 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -6,7 +6,7 @@ import { build as viteBuild } from 'vite' import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' -import { getEnvVarDefinitions } from '../envVarDefinitions' +import { getEnvVarDefinitions } from '../lib/envVarDefinitions' import { onWarn } from '../lib/onWarn' import { ensureProcessDirWeb } from '../utils' diff --git a/packages/vite/src/rsc/rscBuildForServer.ts b/packages/vite/src/rsc/rscBuildForServer.ts index 5016015165e0..5b3c66188fb2 100644 --- a/packages/vite/src/rsc/rscBuildForServer.ts +++ b/packages/vite/src/rsc/rscBuildForServer.ts @@ -6,7 +6,7 @@ import { build as viteBuild } from 'vite' import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' -import { getEnvVarDefinitions } from '../envVarDefinitions' +import { getEnvVarDefinitions } from '../lib/envVarDefinitions' import { onWarn } from '../lib/onWarn' import { rscTransformPlugin } from './rscVitePlugins'