import type { InlineConfig } from 'vite' import { resolvePath } from '@vxrn/resolve' import FSExtra from 'fs-extra' import { join } from 'node:path' import type { VXRNOptionsFilled } from './getOptionsFilled' // essentially base web config not base everything export const dedupe = [ 'one', '@vxrn/safe-area', 'react', 'react-dom', 'react-dom/client', 'react-native-web', 'react-native-web-lite', '@react-navigation/core', '@react-navigation/native', '@react-navigation/elements', '@react-navigation/native-stack', '@react-navigation/bottom-tabs', '@tamagui/core', '@tamagui/web', '@tamagui/react-native-web', '@tamagui/react-native-web-lite', 'tamagui', 'react-native-reanimated', 'expo-modules-core', 'escape-string-regexp', ] /** * Returns fundamental Vite configs. * * Here we returns only configs, without plugins. Basic plugins are defined in * `getBaseVitePlugins`. By separating plugins and configs, we try to make * things more composable and avoid plugins to be nested. * * The file is named "getBaseViteConfig**Only**" because there's originally * a `getBaseViteConfig` that returns both plugins and configs. The "Only" * is added to prevent misuse. We can remove it later when things are settled. */ export async function getBaseViteConfig( config: Pick ): Promise> { const { root, mode } = config const postCSSPaths = [ join(root, 'postcss.config.js'), join(root, 'postcss.config.ts'), join(root, 'postcss.config.json'), ] const postCSSConfigPath = ( await Promise.all( postCSSPaths.map(async (x) => { if (await FSExtra.pathExists(x)) { return x } }) ) ).find((x) => typeof x === 'string') const reactNativePackageJsonPath = resolvePath('react-native/package.json', root) const { version } = await FSExtra.readJSON(reactNativePackageJsonPath) return { mode, // we load the config ourselves // if you disable this is disables auto-reloading config changes // configFile: false, // TODO make this documented / configurable through the plugins css: postCSSConfigPath ? { postcss: postCSSConfigPath, } : { transformer: 'lightningcss', lightningcss: { targets: { safari: (15 << 16) | (2 << 8), }, }, }, define: { __DEV__: `${mode === 'development'}`, 'process.env.NODE_ENV': `"${mode}"`, 'process.env.REACT_NATIVE_VERSION': `"${version}"`, }, resolve: { alias: [ // deep react-native/Libraries/* paths must be caught BEFORE the general // react-native → react-native-web alias, since react-native-web doesn't // have these native-only subpaths { find: /^react-native\/Libraries\/.*/, replacement: resolvePath('@vxrn/vite-plugin-metro/empty', import.meta.dirname), }, { find: 'react-native/package.json', replacement: resolvePath('react-native-web/package.json', root), }, { find: 'react-native', replacement: resolvePath('react-native-web', root), }, { find: 'react-native-safe-area-context', replacement: resolvePath('@vxrn/safe-area', root), }, // bundle size optimizations { find: 'query-string', replacement: resolvePath('@vxrn/query-string', root), }, { find: 'url-parse', replacement: resolvePath('@vxrn/url-parse', root), }, ], // TODO auto dedupe all include optimize deps? dedupe, }, build: {}, // enable OXC React Refresh so component registration ($RefreshReg$) is // injected into TSX/JSX output; the one:react-refresh-web compiler plugin // then wraps the output with the runtime preamble and import.meta.hot.accept // only enable in dev mode - in production the refresh plugin doesn't run, // leaving $RefreshReg$ undefined which crashes client-side JS oxc: { jsx: { refresh: mode === 'development', }, }, } satisfies Omit }