UNPKG

vike

Version:

The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.

231 lines (230 loc) 11.2 kB
import './assertEnvApi.js'; export { resolveViteConfigFromUser }; export { isOnlyResolvingUserConfig }; export { getVikeConfigInternalEarly }; export { getViteRoot }; export { assertViteRoot }; export { normalizeViteRoot }; import { loadConfigFromFile, mergeConfig, resolveConfig } from 'vite'; import { getVikeConfigInternal, getVikeConfigFromCliOrEnv, setVikeConfigContext, isVikeConfigContextSet, } from '../vite/shared/resolveVikeConfigInternal.js'; import path from 'node:path'; import { assert, assertUsage, assertWarning } from '../../utils/assert.js'; import { getGlobalObject } from '../../utils/getGlobalObject.js'; import { pick } from '../../utils/pick.js'; import { toPosixPath } from '../../utils/path.js'; import pc from '@brillout/picocolors'; import { getEnvVarObject } from '../vite/shared/getEnvVarObject.js'; import { getVikeApiOperation, isVikeCliOrApi } from '../../shared-server-node/api-context.js'; import { getViteCommandFromCli } from '../vite/shared/isViteCli.js'; const globalObject = getGlobalObject('api/prepareViteApiCall.ts', {}); async function resolveViteConfigFromUser() { const { viteContext } = getVikeApiContext(); assert(viteContext); const viteInfo = await getViteInfo(viteContext); const { viteConfigFromUserResolved } = viteInfo; const { viteConfigResolved } = await assertViteRoot2(viteInfo.root, viteConfigFromUserResolved, viteContext); return { viteConfigResolved, // ONLY USE if strictly necessary. (We plan to remove assertViteRoot2() as explained in the comments of that function.) viteConfigFromUserResolved, }; } async function getVikeConfigInternalEarly() { assert(!globalObject.isOnlyResolvingUserConfig); // ensure no infinite loop if (!isVikeConfigContextSet()) { const viteContext = getViteContext(); const viteInfo = await getViteInfo(viteContext); setVikeConfigContext_(viteInfo, viteContext); } return await getVikeConfigInternal(); } function setVikeConfigContext_(viteInfo, viteContext) { setVikeConfigContext({ userRootDir: viteInfo.root, isDev: viteContext === 'dev', vikeVitePluginOptions: viteInfo.vikeVitePluginOptions, }); } function isOnlyResolvingUserConfig() { return globalObject.isOnlyResolvingUserConfig; } async function getViteRoot(viteContext) { if (!globalObject.root) await getViteInfo(viteContext); assert(globalObject.root); return globalObject.root; } async function getViteInfo(viteContext) { const { viteConfigFromUserVikeApiOptions } = getVikeApiContext(); let viteConfigFromUserResolved = clone(viteConfigFromUserVikeApiOptions ?? {}); // Precedence: // 1. (highest precedence) | viteConfigFromUserEnvVar | VITE_CONFIG // 2. | viteConfigFromUserVikeMode | VIKE_CONFIG & Vike CLI options — only `+mode` // 2. | viteConfigFromUserVikeApiOptions | Vike API options // 3. (lowest precedence) | viteConfigFromUserViteFile | vite.config.js // Resolve Vike's +mode setting { const viteConfigFromUserVikeMode = pick(getVikeConfigFromCliOrEnv().vikeConfigFromCliOrEnv, ['mode']); if (Object.keys(viteConfigFromUserVikeMode).length > 0) { viteConfigFromUserResolved = merge(viteConfigFromUserResolved ?? {}, viteConfigFromUserVikeMode); } } // Resolve VITE_CONFIG const viteConfigFromUserEnvVar = getEnvVarObject('VITE_CONFIG'); if (viteConfigFromUserEnvVar) { viteConfigFromUserResolved = merge(viteConfigFromUserResolved ?? {}, viteConfigFromUserEnvVar); } // Resolve vite.config.js globalObject.isOnlyResolvingUserConfig = true; const viteConfigFromUserViteConfigFile = await loadViteConfigFile(viteConfigFromUserResolved, viteContext); globalObject.isOnlyResolvingUserConfig = false; // Correct precedence, replicates Vite: // https://github.com/vitejs/vite/blob/4f5845a3182fc950eb9cd76d7161698383113b18/packages/vite/src/node/config.ts#L1001 const viteConfigResolved = merge(viteConfigFromUserViteConfigFile ?? {}, viteConfigFromUserResolved ?? {}); const root = normalizeViteRoot(viteConfigResolved.root ?? process.cwd()); globalObject.root = root; // - Find options `vike(options)` set in vite.config.js // - TO-DO/next-major-release: remove // - Add Vike's Vite plugin if missing let vikeVitePluginOptions; const found = findVikeVitePlugin(viteConfigResolved); if (found) { vikeVitePluginOptions = found.vikeVitePluginOptions; } else { // Show a warning because Vike supports Vite's CLI (as well as third-party CLIs). // - Encourage users to define a vite.config.js file that also works with Vite's CLI (and potentially other third-party CLIs). // - Vike-based frameworks, such as DocPress, allow their users to omit defining a vite.config.js file. assertWarning(!viteConfigFromUserViteConfigFile, // Only show the warning if the user defined a vite.config.js file "Omitting Vike's Vite plugin (inside your vite.config.js) is deprecated — make sure to always add Vike's Vite plugin https://vike.dev/vite-plugin", { onlyOnce: true }); // Add Vike to plugins if not present. // Using a dynamic import because the script calling the Vike API may not live in the same place as vite.config.js, thus vike/plugin may resolved to two different node_modules/vike directories. const { plugin: vikePlugin } = await import('../vite/index.js'); viteConfigFromUserResolved = { ...viteConfigFromUserResolved, plugins: [...(viteConfigFromUserResolved?.plugins ?? []), vikePlugin()], }; const res = findVikeVitePlugin(viteConfigFromUserResolved); assert(res); vikeVitePluginOptions = res.vikeVitePluginOptions; } assert(vikeVitePluginOptions); return { root, vikeVitePluginOptions, viteConfigFromUserResolved }; } /** `c2` overrides `c1` */ function merge(c1, c2) { return mergeConfig(c1, c2); } function clone(c) { return mergeConfig({}, c); } function findVikeVitePlugin(viteConfig) { let vikeVitePluginOptions; let vikeVitePuginFound = false; viteConfig?.plugins?.forEach((p) => { if (p && '_vikeVitePluginOptions' in p) { vikeVitePuginFound = true; const options = p._vikeVitePluginOptions; vikeVitePluginOptions ?? (vikeVitePluginOptions = {}); Object.assign(vikeVitePluginOptions, options); } }); if (!vikeVitePuginFound) return null; return { vikeVitePluginOptions }; } // Copied from https://github.com/vitejs/vite/blob/4f5845a3182fc950eb9cd76d7161698383113b18/packages/vite/src/node/config.ts#L961-L1005 async function loadViteConfigFile(viteConfigFromUserResolved, viteContext) { const viteContextResolved = resolveViteContext(viteConfigFromUserResolved, viteContext); const [inlineConfig, command, defaultMode, _defaultNodeEnv, isPreview] = viteContextResolved; let config = inlineConfig; let mode = inlineConfig.mode || defaultMode; const configEnv = { mode, command, isSsrBuild: command === 'build' && !!config.build?.ssr, isPreview, }; let { configFile } = config; if (configFile !== false) { const loadResult = await loadConfigFromFile(configEnv, configFile, config.root, config.logLevel, config.customLogger); if (!loadResult) return null; assert(loadResult.config); return loadResult.config; } return null; } function getViteContext() { const vikeApiOperation = getVikeApiOperation(); const viteCommand = getViteCommandFromCli(); assert(!(viteCommand && vikeApiOperation)); if (vikeApiOperation) return getViteContextWithOperation(vikeApiOperation.operation); assert(!isVikeCliOrApi()); if (viteCommand === 'dev' || viteCommand === 'optimize') { return 'dev'; } if (viteCommand === 'build') { return 'build'; } if (viteCommand === 'preview') { return 'preview'; } // Third-party CLIs. // - Component development (e.g. Storybook) => let's consider it development // - Testing (e.g. Vitest) => let's consider it development return 'dev'; } function getViteContextWithOperation(operation) { if (operation === 'build' || operation === 'prerender') { return 'build'; } if (operation === 'preview') { return 'preview'; } if (operation === 'dev') { return 'dev'; } assert(false); } function getVikeApiContext() { const vikeApiOperation = getVikeApiOperation(); if (!vikeApiOperation) return { viteConfigFromUserVikeApiOptions: null, viteContext: null }; const { options, operation } = vikeApiOperation; const viteConfigFromUserVikeApiOptions = options.viteConfig; const viteContext = getViteContextWithOperation(operation); return { viteConfigFromUserVikeApiOptions, viteContext }; } function resolveViteContext(inlineConfig = {}, viteContext) { const isBuild = viteContext === 'build'; const isPreview = viteContext === 'preview'; const isDev = viteContext === 'dev'; const command = isBuild ? 'build' : 'serve'; const defaultMode = isDev ? 'development' : 'production'; const defaultNodeEnv = defaultMode; const viteContextResolved = [inlineConfig, command, defaultMode, defaultNodeEnv, isPreview]; return viteContextResolved; } function normalizeViteRoot(root) { // `path.resolve(viteConfigFromUserViteFile.configFile, root)` could be more intuitive than `path.resolve(process.cwd(), root)` but we replicate Vite's behavior (`vite.config.js` should follow Vite's API), see: // https://github.com/vitejs/vite/blob/4f5845a3182fc950eb9cd76d7161698383113b18/packages/vite/src/node/config.ts#L1063 return toPosixPath( // Equivalent to `path.resolve(process.cwd(), root)` path.resolve(root)); } const errMsg = `A Vite plugin is modifying the Vite setting ${pc.cyan('root')} which is forbidden`; async function assertViteRoot2(root, viteConfigFromUserResolved, viteContext) { const viteContextResolved = resolveViteContext(viteConfigFromUserResolved, viteContext); // We can eventually remove this resolveConfig() call (along with removing the whole assertViteRoot2() function which is redundant with the assertViteRoot() function) so that Vike doesn't make any resolveConfig() (except for pre-rendering and preview which is required). But let's keep it for now, just to see whether calling resolveConfig() can be problematic. const viteConfigResolved = await resolveConfig(...viteContextResolved); assertUsage(normalizeViteRoot(viteConfigResolved.root) === normalizeViteRoot(root), errMsg); return { viteConfigResolved }; } function assertViteRoot(rootResolvedEarly, config) { const rootResolved = config.root; const rootGlobal = globalObject.root; if (rootGlobal) assert(normalizeViteRoot(rootGlobal) === normalizeViteRoot(rootResolvedEarly)); assertUsage(normalizeViteRoot(rootResolvedEarly) === normalizeViteRoot(rootResolved), errMsg); }