UNPKG

vike

Version:

(Replaces Next.js/Nuxt) 🔨 Composable framework to build advanced applications with flexibility and stability.

261 lines (260 loc) • 12.3 kB
export { optimizeDeps }; export { resolveOptimizeDeps }; import { findPageFiles } from '../../shared/findPageFiles.js'; import { assert } from '../../../../utils/assert.js'; import { createDebug } from '../../../../utils/debug.js'; import { isArray } from '../../../../utils/isArray.js'; import { isFilePathAbsoluteFilesystem } from '../../../../utils/isFilePathAbsoluteFilesystem.js'; import { assertImportIsNpmPackage, getNpmPackageName } from '../../../../utils/parseNpmPackage.js'; import { requireResolveOptional } from '../../../../utils/requireResolve.js'; import { isVirtualFileId } from '../../../../utils/virtualFileId.js'; import { getVikeConfigInternal } from '../../shared/resolveVikeConfigInternal.js'; import { analyzeClientEntries } from '../build/pluginBuildConfig.js'; import { virtualFileIdGlobalEntryClientCR, virtualFileIdGlobalEntryClientSR, } from '../../../../shared-server-node/virtualFileId.js'; import { getFilePathResolved } from '../../shared/getFilePath.js'; import { getConfigValueSourcesRelevant } from '../pluginVirtualFiles/getConfigValueSourcesRelevant.js'; import '../../assertEnvVite.js'; const debug = createDebug('vike:optimizeDeps'); // Add dependencies that cannot be discovered by Vite during the scanning phase const LATE_DISCOVERED = [ // Workaround for https://github.com/vitejs/vite-plugin-react/issues/650 // - The issue was closed as completed with https://github.com/vitejs/vite/pull/20495 but it doesn't fix the issue and the workaround is still needed. // - TO-DO/eventually: try removing the workaround and see if the CI fails (at test/@cloudflare_vite-plugin/) — maybe the issue will get fixed at some point. 'react/jsx-dev-runtime', // Workaround for https://github.com/vikejs/vike/issues/2823#issuecomment-3514325487 '@compiled/react/runtime', ]; // TO-DO/eventually: remove this. // Avoid following warning for older vike-photon versions: // ``` // [11:32:49.768][/test/photon-vercel/.test-dev.test.ts][pnpm run dev][stderr] Failed to resolve dependency: vike > @brillout/require-shim, present in ssr 'optimizeDeps.include' // ``` // https://github.com/vikejs/vike-photon/issues/56 // https://github.com/vikejs/vike/pull/3091 const ALWAYS_REMOVE = ['@brillout/require-shim', 'vike > @brillout/require-shim']; const optimizeDeps = { optimizeDeps: { exclude: [ // We must exclude Vike's client runtime so it can import virtual modules 'vike/client', 'vike/client/router', ], include: [ // Avoid: // ``` // 9:28:58 AM [vite] ✨ new dependencies optimized: @brillout/json-serializer/parse // 9:28:58 AM [vite] ✨ optimized dependencies changed. reloading // ``` 'vike > @brillout/json-serializer/parse', 'vike > @brillout/json-serializer/stringify', 'vike > @brillout/picocolors', ], }, ssr: { optimizeDeps: { exclude: [ '@brillout/import', '@brillout/json-serializer', /* Adding @brillout/picocolors breaks the UD + Cloudflare test, we don't know why. TO-DO/eventually: re-add it and see if Vike's CI is green. '@brillout/picocolors', */ '@brillout/vite-plugin-server-entry', 'vike', ], }, }, }; // Populate optimizeDeps with dynamic entries: // - User's + files (i.e. Vike entries) // - Late discovered dependencies (if they exist) // - Make server environments inherit from ssr.optimizeDeps (it isn't the case by default) async function resolveOptimizeDeps(config) { const vikeConfig = await getVikeConfigInternal(); const { _pageConfigs: pageConfigs } = vikeConfig; // Retrieve user's + files (i.e. Vike entries) const { entriesClient, entriesServer, includeClient, includeServer } = await getPageDeps(config, pageConfigs); // Add late discovered dependencies, if they exist LATE_DISCOVERED.forEach((dep) => { const userRootDir = config.root; const resolved = requireResolveOptional({ importPath: dep, userRootDir, importerFilePath: null }); const resolvedInsideRepo = resolved && resolved.startsWith(userRootDir); if (resolvedInsideRepo) { // We add `dep` only if `resolvedInsideRepo === true` otherwise Vite logs a warning like the following. // - ```console // [11:22:42.464][/examples/vue-full][npm run dev][stderr] Failed to resolve dependency: react/jsx-dev-runtime, present in client 'optimizeDeps.include' // ``` // - ```console // [12:24:53.225][/test/@cloudflare_vite-plugin/test-dev.test.ts][npm run dev][stderr] Failed to resolve dependency: @compiled/react/runtime, present in ssr 'optimizeDeps.include' // ``` includeClient.push(dep); includeServer.push(dep); } else if (config.optimizeDeps.include?.includes(dep)) { // Monorepo => always `resolvedInsideRepo === false` — we use this other approach to workaround missing 'react/jsx-dev-runtime' includeServer.push(dep); } }); // Set optimizeDeps (client-side) config.optimizeDeps.include = add(config.optimizeDeps.include, includeClient); config.optimizeDeps.entries = add(config.optimizeDeps.entries, entriesClient); // Set optimizeDeps (server-side) for (const envName in config.environments) { const env = config.environments[envName]; if (env.consumer === 'server' && env.optimizeDeps.noDiscovery === false) { env.optimizeDeps.include = add(env.optimizeDeps.include, includeServer); env.optimizeDeps.entries = add(env.optimizeDeps.entries, entriesServer); } } // Remove @brillout/require-shim config.optimizeDeps.include = remove(config.optimizeDeps.include); config.optimizeDeps.entries = remove(config.optimizeDeps.entries); for (const envName in config.environments) { const env = config.environments[envName]; env.optimizeDeps.include = remove(env.optimizeDeps.include ?? []); env.optimizeDeps.entries = remove(env.optimizeDeps.entries ?? []); } // Debug if (debug.isActivated) debug('optimizeDeps', { 'config.optimizeDeps.entries': config.optimizeDeps.entries, 'config.optimizeDeps.include': config.optimizeDeps.include, 'config.optimizeDeps.exclude': config.optimizeDeps.exclude, // @ts-ignore Vite doesn't seem to support ssr.optimizeDeps.entries (vite@7.0.6, July 2025) 'config.ssr.optimizeDeps.entries': config.ssr.optimizeDeps.entries, 'config.ssr.optimizeDeps.include': config.ssr.optimizeDeps.include, 'config.ssr.optimizeDeps.exclude': config.ssr.optimizeDeps.exclude, }); } async function getPageDeps(config, pageConfigs) { let entriesClient = []; let entriesServer = []; let includeClient = []; let includeServer = []; const addEntry = (e, isForClientSide, definedAt) => { assert(e); // optimizeDeps.entries expects filesystem absolute paths assert(isVirtualFileId(e) || isFilePathAbsoluteFilesystem(e)); if (isExcluded(e, isForClientSide, definedAt)) return; if (isForClientSide) { entriesClient.push(e); } else { entriesServer.push(e); } }; const addInclude = (e, isForClientSide, definedAt) => { assert(e); // optimizeDeps.include expects npm packages assert(!e.startsWith('/')); // Shouldn't be a path alias, as path aliases would need to be added to optimizeDeps.entries instead of optimizeDeps.include assertImportIsNpmPackage(e); if (isExcluded(e, isForClientSide, definedAt)) return; if (isForClientSide) { includeClient.push(e); } else { includeServer.push(e); } }; const isExcluded = (e, isForClientSide, definedAt) => { const exclude = isForClientSide ? config.optimizeDeps.exclude : config.ssr.optimizeDeps.exclude; if (!exclude) return false; if (definedAt?.importPathAbsolute) { const npmPackageName = getNpmPackageName(definedAt.importPathAbsolute); if (npmPackageName && exclude.includes(npmPackageName)) return true; } return exclude.includes(e); }; // V1 design { ; [true, false].forEach((isForClientSide) => { pageConfigs.forEach((pageConfig) => { Object.entries(pageConfig.configValueSources).forEach(([configName]) => { const runtimeEnv = { isForClientSide, isDev: true, // TO-DO/eventually/remove-server-router: let's eventually remove support for Server Routing isClientRouting: true, }; const sourcesRelevant = getConfigValueSourcesRelevant(configName, runtimeEnv, pageConfig); sourcesRelevant.forEach((configValueSource) => { if (!configValueSource.valueLoadedViaImport && !configValueSource.valueIsFilePath) return; const { definedAt } = configValueSource; if (definedAt.definedBy) return; if (definedAt.filePathAbsoluteUserRootDir !== null) { addEntry( // optimizeDeps.entries expects filesystem absolute paths definedAt.filePathAbsoluteFilesystem, isForClientSide, definedAt); } else { addInclude( // optimizeDeps.include expects npm packages definedAt.importPathAbsolute, isForClientSide, definedAt); } }); }); }); }); } // V0.4 design { const pageFiles = await findPageFiles(config, ['.page', '.page.client'], true); const userRootDir = config.root; pageFiles.forEach((filePathAbsoluteUserRootDir) => { const entry = getFilePathResolved({ filePathAbsoluteUserRootDir, userRootDir }); const { filePathAbsoluteFilesystem } = entry; addEntry(filePathAbsoluteFilesystem, true); }); } // Add virtual files. // - This doesn't work: Vite's dep optimizer doesn't seem to be able to crawl virtual files. // - Should we make it work? E.g. by creating a temporary file at node_modules/.vike/virtualFiles.js // - Or should we remove it? And make sure getPageDeps() also works for aliased import paths // - If we do, then we need to adjust include/entries (maybe by making include === entries -> will Vite complain?) { const { hasClientRouting, hasServerRouting, clientEntries } = analyzeClientEntries(pageConfigs, config); Object.values(clientEntries).forEach((e) => addEntry(e, true)); if (hasClientRouting) addEntry(virtualFileIdGlobalEntryClientCR, true); if (hasServerRouting) addEntry(virtualFileIdGlobalEntryClientSR, true); } entriesClient = entriesClient; entriesServer = entriesServer; includeClient = includeClient; includeServer = includeServer; return { entriesClient, entriesServer, includeClient, includeServer, }; } function add(input, listAddendum) { const list = normalizeInput(input); listAddendum.forEach((e) => { if (!list.includes(e)) list.push(e); }); return list; } function normalizeInput(input) { const list = !input ? [] : isArray(input) ? unique(input) : [input]; return list; } function unique(arr) { const arrUnique = Array.from(new Set(arr)); return arr.length !== arrUnique.length ? arrUnique : arr; } function remove(input) { let list = normalizeInput(input); list = list.filter((e) => !ALWAYS_REMOVE.includes(e)); return list; }