2023-05-10 12:45:49 +00:00
import { existsSync } from 'node:fs'
2021-01-22 22:02:33 +00:00
import * as vite from 'vite'
2023-06-16 14:19:53 +00:00
import { basename , dirname , join , resolve } from 'pathe'
2023-03-29 10:59:57 +00:00
import type { Nuxt , ViteConfig } from '@nuxt/schema'
2023-04-07 16:02:47 +00:00
import { addVitePlugin , isIgnored , logger , resolvePath } from '@nuxt/kit'
2022-05-13 10:49:30 +00:00
import replace from '@rollup/plugin-replace'
2021-11-03 13:04:42 +00:00
import { sanitizeFilePath } from 'mlly'
2022-09-12 10:12:41 +00:00
import { withoutLeadingSlash } from 'ufo'
import { filename } from 'pathe/utils'
2022-10-03 13:37:36 +00:00
import { resolveTSConfig } from 'pkg-types'
2023-06-16 14:19:53 +00:00
import { consola } from 'consola'
2021-05-24 11:14:10 +00:00
import { buildClient } from './client'
import { buildServer } from './server'
2021-07-15 10:18:34 +00:00
import virtual from './plugins/virtual'
2021-05-24 11:14:10 +00:00
import { warmupViteServer } from './utils/warmup'
2021-10-13 20:08:26 +00:00
import { resolveCSSOptions } from './css'
2022-07-07 16:26:04 +00:00
import { composableKeysPlugin } from './plugins/composable-keys'
2023-03-08 11:56:41 +00:00
import { logLevelMap } from './utils/logger'
2023-06-20 18:28:44 +00:00
import { ssrStylesPlugin } from './plugins/ssr-styles'
2021-04-29 11:51:54 +00:00
export interface ViteBuildContext {
2021-01-22 22:02:33 +00:00
nuxt : Nuxt
2023-03-29 10:59:57 +00:00
config : ViteConfig
2022-08-04 15:24:35 +00:00
entry : string
2022-03-11 08:41:27 +00:00
clientServer? : vite.ViteDevServer
ssrServer? : vite.ViteDevServer
2021-01-22 22:02:33 +00:00
}
export async function bundle ( nuxt : Nuxt ) {
2023-06-16 14:19:53 +00:00
// https://github.com/vitejs/vite/blob/8fe69524d25d45290179175ba9b9956cbce87a91/packages/vite/src/node/constants.ts#L38
const viteConfigPrefix = resolve ( nuxt . options . rootDir , 'vite.config' )
const viteConfigFile = await resolvePath ( viteConfigPrefix ) . catch ( ( ) = > null )
if ( viteConfigFile && viteConfigFile !== viteConfigPrefix ) {
consola . warn ( ` Using \` ${ basename ( viteConfigFile ) } \` is not supported together with Nuxt. Use \` options.vite \` instead. You can read more in \` https://nuxt.com/docs/api/configuration/nuxt-config#vite \` . ` )
}
2022-09-09 09:54:20 +00:00
const useAsyncEntry = nuxt . options . experimental . asyncEntry ||
( nuxt . options . vite . devBundler === 'vite-node' && nuxt . options . dev )
const entry = await resolvePath ( resolve ( nuxt . options . appDir , useAsyncEntry ? 'entry.async' : 'entry' ) )
2023-05-10 12:45:49 +00:00
let allowDirs = [
nuxt . options . appDir ,
nuxt . options . workspaceDir ,
. . . nuxt . options . _layers . map ( l = > l . config . rootDir ) ,
. . . Object . values ( nuxt . apps ) . flatMap ( app = > [
. . . app . components . map ( c = > dirname ( c . filePath ) ) ,
. . . app . plugins . map ( p = > dirname ( p . src ) ) ,
. . . app . middleware . map ( m = > dirname ( m . path ) ) ,
. . . Object . values ( app . layouts || { } ) . map ( l = > dirname ( l . file ) ) ,
dirname ( nuxt . apps . default . rootComponent ! ) ,
dirname ( nuxt . apps . default . errorComponent ! )
] )
] . filter ( d = > d && existsSync ( d ) )
for ( const dir of allowDirs ) {
allowDirs = allowDirs . filter ( d = > ! d . startsWith ( dir ) || d === dir )
}
2023-07-24 17:32:12 +00:00
const { $client , $server , . . . viteConfig } = nuxt . options . vite
2021-01-22 22:02:33 +00:00
const ctx : ViteBuildContext = {
nuxt ,
2022-09-09 09:54:20 +00:00
entry ,
2021-04-29 11:51:54 +00:00
config : vite.mergeConfig (
{
2023-03-07 12:18:47 +00:00
logLevel : logLevelMap [ nuxt . options . logLevel ] ? ? logLevelMap . info ,
2021-04-29 11:51:54 +00:00
resolve : {
alias : {
. . . nuxt . options . alias ,
'#app' : nuxt . options . appDir ,
2021-07-23 14:58:38 +00:00
// We need this resolution to be present before the following entry, but it
// will be filled in client/server configs
2021-10-27 08:03:10 +00:00
'#build/plugins' : '' ,
2021-07-15 10:50:09 +00:00
'#build' : nuxt . options . buildDir ,
2021-04-29 11:51:54 +00:00
'web-streams-polyfill/ponyfill/es2018' : 'unenv/runtime/mock/empty' ,
// Cannot destructure property 'AbortController' of ..
'abort-controller' : 'unenv/runtime/mock/empty'
}
} ,
optimizeDeps : {
2023-07-04 07:27:34 +00:00
include : [ 'vue' ] ,
2023-02-14 00:36:46 +00:00
exclude : [ 'nuxt/app' ]
2021-04-29 11:51:54 +00:00
} ,
2022-02-25 19:11:01 +00:00
css : resolveCSSOptions ( nuxt ) ,
2023-03-01 15:08:23 +00:00
define : { __NUXT_VERSION__ : JSON.stringify ( nuxt . _version ) } ,
2021-04-29 11:51:54 +00:00
build : {
2023-01-10 11:51:53 +00:00
copyPublicDir : false ,
2021-07-15 10:18:34 +00:00
rollupOptions : {
2022-09-12 10:12:41 +00:00
output : {
2023-03-02 15:28:15 +00:00
sourcemapIgnoreList : ( relativeSourcePath ) = > {
2023-03-03 10:35:25 +00:00
return relativeSourcePath . includes ( 'node_modules' ) || relativeSourcePath . includes ( ctx . nuxt . options . buildDir )
2023-03-02 15:28:15 +00:00
} ,
2022-09-12 10:12:41 +00:00
sanitizeFileName : sanitizeFilePath ,
// https://github.com/vitejs/vite/tree/main/packages/vite/src/node/build.ts#L464-L478
assetFileNames : nuxt.options.dev
? undefined
: chunk = > withoutLeadingSlash ( join ( nuxt . options . app . buildAssetsDir , ` ${ sanitizeFilePath ( filename ( chunk . name ! ) ) } .[hash].[ext] ` ) )
}
2022-07-17 15:10:27 +00:00
} ,
watch : {
exclude : nuxt.options.ignore
2021-07-15 10:18:34 +00:00
}
2021-04-29 11:51:54 +00:00
} ,
2021-07-15 10:18:34 +00:00
plugins : [
2023-03-07 21:06:15 +00:00
composableKeysPlugin . vite ( {
sourcemap : nuxt.options.sourcemap.server || nuxt . options . sourcemap . client ,
rootDir : nuxt.options.rootDir ,
composables : nuxt.options.optimization.keyedComposables
} ) ,
2022-05-13 10:49:30 +00:00
replace ( {
. . . Object . fromEntries ( [ ';' , '(' , '{' , '}' , ' ' , '\t' , '\n' ] . map ( d = > [ ` ${ d } global. ` , ` ${ d } globalThis. ` ] ) ) ,
preventAssignment : true
} ) ,
2022-07-21 10:44:33 +00:00
virtual ( nuxt . vfs )
2021-07-15 10:18:34 +00:00
] ,
2022-03-17 22:17:59 +00:00
vue : {
reactivityTransform : nuxt.options.experimental.reactivityTransform
} ,
2021-07-14 14:37:07 +00:00
server : {
2022-06-22 18:07:54 +00:00
watch : { ignored : isIgnored } ,
2021-07-14 14:37:07 +00:00
fs : {
2023-05-10 12:45:49 +00:00
allow : [ . . . new Set ( allowDirs ) ]
2021-07-14 14:37:07 +00:00
}
2021-07-15 10:18:34 +00:00
}
2023-03-29 10:59:57 +00:00
} satisfies ViteConfig ,
2023-07-24 17:32:12 +00:00
viteConfig
2021-04-29 11:51:54 +00:00
)
2021-01-22 22:02:33 +00:00
}
2022-06-22 18:07:54 +00:00
// In build mode we explicitly override any vite options that vite is relying on
// to detect whether to inject production or development code (such as HMR code)
if ( ! nuxt . options . dev ) {
2022-08-15 13:40:06 +00:00
ctx . config . server ! . watch = undefined
ctx . config . build ! . watch = undefined
2022-06-22 18:07:54 +00:00
}
2022-10-03 13:37:36 +00:00
// Add type-checking
if ( ctx . nuxt . options . typescript . typeCheck === true || ( ctx . nuxt . options . typescript . typeCheck === 'build' && ! ctx . nuxt . options . dev ) ) {
const checker = await import ( 'vite-plugin-checker' ) . then ( r = > r . default )
addVitePlugin ( checker ( {
vueTsc : {
tsconfigPath : await resolveTSConfig ( ctx . nuxt . options . rootDir )
}
2023-06-05 14:58:18 +00:00
} ) , { server : nuxt.options.ssr } )
2022-10-03 13:37:36 +00:00
}
2023-06-05 14:58:18 +00:00
await nuxt . callHook ( 'vite:extend' , ctx )
2023-06-20 18:28:44 +00:00
if ( ! ctx . nuxt . options . dev ) {
const chunksWithInlinedCSS = new Set < string > ( )
const clientCSSMap = { }
nuxt . hook ( 'vite:extendConfig' , ( config , { isServer } ) = > {
config . plugins ! . push ( ssrStylesPlugin ( {
srcDir : ctx.nuxt.options.srcDir ,
clientCSSMap ,
chunksWithInlinedCSS ,
shouldInline : ctx.nuxt.options.experimental.inlineSSRStyles ,
components : ctx.nuxt.apps.default.components ,
globalCSS : ctx.nuxt.options.css ,
mode : isServer ? 'server' : 'client' ,
entry : ctx.entry
} ) )
} )
// Remove CSS entries for files that will have inlined styles
ctx . nuxt . hook ( 'build:manifest' , ( manifest ) = > {
for ( const key in manifest ) {
const entry = manifest [ key ]
const shouldRemoveCSS = chunksWithInlinedCSS . has ( key ) && ! entry . isEntry
2023-06-30 04:25:43 +00:00
if ( entry . isEntry && chunksWithInlinedCSS . has ( key ) ) {
// @ts-expect-error internal key
entry . _globalCSS = true
}
2023-06-20 18:28:44 +00:00
if ( shouldRemoveCSS && entry . css ) {
entry . css = [ ]
}
}
} )
}
2022-03-14 10:19:37 +00:00
nuxt . hook ( 'vite:serverCreated' , ( server : vite.ViteDevServer , env ) = > {
2022-02-08 00:10:42 +00:00
// Invalidate virtual modules when templates are re-generated
ctx . nuxt . hook ( 'app:templatesGenerated' , ( ) = > {
for ( const [ id , mod ] of server . module Graph.idToModuleMap ) {
2022-07-21 14:21:58 +00:00
if ( id . startsWith ( 'virtual:' ) ) {
2022-02-08 00:10:42 +00:00
server . module Graph.invalidateModule ( mod )
}
}
} )
2022-09-14 15:59:56 +00:00
if (
nuxt . options . vite . warmupEntry !== false &&
2023-01-19 19:37:07 +00:00
// https://github.com/nuxt/nuxt/issues/14898
2022-09-14 15:59:56 +00:00
! ( env . isServer && ctx . nuxt . options . vite . devBundler !== 'legacy' )
) {
2022-08-15 16:01:34 +00:00
const start = Date . now ( )
2022-08-15 16:03:00 +00:00
warmupViteServer ( server , [ join ( '/@fs/' , ctx . entry ) ] , env . isServer )
2022-08-15 16:01:34 +00:00
. then ( ( ) = > logger . info ( ` Vite ${ env . isClient ? 'client' : 'server' } warmed up in ${ Date . now ( ) - start } ms ` ) )
. catch ( logger . error )
}
2021-04-29 11:51:54 +00:00
} )
2022-07-17 14:17:07 +00:00
2021-04-29 11:51:54 +00:00
await buildClient ( ctx )
2022-01-13 12:39:23 +00:00
await buildServer ( ctx )
2021-01-22 22:02:33 +00:00
}