2024-03-08 22:58:37 +00:00
import { dirname , join , normalize , relative , resolve } from 'pathe'
2023-04-07 16:02:47 +00:00
import { createDebugger , createHooks } from 'hookable'
2024-04-19 08:48:19 +00:00
import ignore from 'ignore'
2022-12-11 21:44:52 +00:00
import type { LoadNuxtOptions } from '@nuxt/kit'
2024-04-19 08:48:19 +00:00
import { addBuildPlugin , addComponent , addPlugin , addRouteMiddleware , addServerPlugin , addVitePlugin , addWebpackPlugin , installModule , loadNuxtConfig , logger , nuxtCtx , resolveAlias , resolveFiles , resolveIgnorePatterns , resolvePath , tryResolveModule , useNitro } from '@nuxt/kit'
2024-03-09 06:07:39 +00:00
import { resolvePath as _resolvePath } from 'mlly'
2024-06-13 17:51:44 +00:00
import type { Nuxt , NuxtHooks , NuxtModule , NuxtOptions } from 'nuxt/schema'
2024-03-17 00:18:31 +00:00
import type { PackageJson } from 'pkg-types'
import { readPackageJSON , resolvePackageJSON } from 'pkg-types'
2024-06-07 14:24:56 +00:00
import { hash } from 'ohash'
2023-01-20 12:10:58 +00:00
2022-06-10 13:33:16 +00:00
import escapeRE from 'escape-string-regexp'
2022-09-05 13:46:47 +00:00
import fse from 'fs-extra'
2024-05-15 10:51:14 +00:00
import { withTrailingSlash , withoutLeadingSlash } from 'ufo'
2024-04-05 18:08:32 +00:00
2023-08-28 16:46:49 +00:00
import defu from 'defu'
2024-05-23 11:35:21 +00:00
import { gt , satisfies } from 'semver'
2021-08-11 20:28:38 +00:00
import pagesModule from '../pages/module'
2022-04-05 14:02:29 +00:00
import metaModule from '../head/module'
2021-08-11 20:28:38 +00:00
import componentsModule from '../components/module'
2022-08-23 14:22:11 +00:00
import importsModule from '../imports/module'
2024-04-05 18:08:32 +00:00
2021-08-11 21:26:47 +00:00
import { distDir , pkgDir } from '../dirs'
2021-10-02 18:31:00 +00:00
import { version } from '../../package.json'
2024-05-03 12:57:28 +00:00
import { scriptsStubsPreset } from '../imports/presets'
2024-01-12 11:22:01 +00:00
import { ImportProtectionPlugin , nuxtImportProtections } from './plugins/import-protection'
2023-08-25 13:57:25 +00:00
import type { UnctxTransformPluginOptions } from './plugins/unctx'
2022-04-01 09:55:23 +00:00
import { UnctxTransformPlugin } from './plugins/unctx'
2023-03-07 09:30:05 +00:00
import type { TreeShakeComposablesPluginOptions } from './plugins/tree-shake'
import { TreeShakeComposablesPlugin } from './plugins/tree-shake'
2022-10-26 12:43:37 +00:00
import { DevOnlyPlugin } from './plugins/dev-only'
2023-04-03 13:18:24 +00:00
import { LayerAliasingPlugin } from './plugins/layer-aliasing'
2021-10-06 17:59:35 +00:00
import { addModuleTranspiles } from './modules'
2022-04-07 11:28:04 +00:00
import { initNitro } from './nitro'
2023-01-23 18:07:21 +00:00
import schemaModule from './schema'
2023-06-19 23:00:03 +00:00
import { RemovePluginMetadataPlugin } from './plugins/plugin-metadata'
2023-08-07 22:57:35 +00:00
import { AsyncContextInjectionPlugin } from './plugins/async-context'
2023-08-07 22:05:29 +00:00
import { resolveDeepImportsPlugin } from './plugins/resolve-deep-imports'
2024-05-07 14:04:21 +00:00
import { prehydrateTransformPlugin } from './plugins/prehydrate'
2020-07-02 13:02:35 +00:00
2021-04-02 11:47:01 +00:00
export function createNuxt ( options : NuxtOptions ) : Nuxt {
2021-08-27 12:51:40 +00:00
const hooks = createHooks < NuxtHooks > ( )
2021-04-02 11:47:01 +00:00
2021-04-15 19:17:44 +00:00
const nuxt : Nuxt = {
2021-10-02 18:31:00 +00:00
_version : version ,
2021-04-02 11:47:01 +00:00
options ,
hooks ,
callHook : hooks.callHook ,
2021-08-27 12:51:40 +00:00
addHooks : hooks.addHooks ,
2021-04-15 19:17:44 +00:00
hook : hooks.hook ,
ready : ( ) = > initNuxt ( nuxt ) ,
2024-06-12 19:55:24 +00:00
close : async ( ) = > {
await hooks . callHook ( 'close' , nuxt )
hooks . removeAllHooks ( )
} ,
2023-05-09 22:46:03 +00:00
vfs : { } ,
2024-04-05 18:08:32 +00:00
apps : { } ,
2020-08-17 19:12:34 +00:00
}
2021-04-15 19:17:44 +00:00
return nuxt
2020-08-02 15:50:35 +00:00
}
2024-03-17 00:18:31 +00:00
const nightlies = {
2024-04-05 18:08:32 +00:00
'nitropack' : 'nitropack-nightly' ,
'h3' : 'h3-nightly' ,
'nuxt' : 'nuxt-nightly' ,
2024-03-17 00:18:31 +00:00
'@nuxt/schema' : '@nuxt/schema-nightly' ,
2024-04-05 18:08:32 +00:00
'@nuxt/kit' : '@nuxt/kit-nightly' ,
2024-03-17 00:18:31 +00:00
}
2024-05-21 08:18:36 +00:00
const keyDependencies = [
'@nuxt/kit' ,
'@nuxt/schema' ,
]
2021-04-02 11:47:01 +00:00
async function initNuxt ( nuxt : Nuxt ) {
// Register user hooks
2023-12-08 12:51:57 +00:00
for ( const config of nuxt . options . _layers . map ( layer = > layer . config ) . reverse ( ) ) {
if ( config . hooks ) {
nuxt . hooks . addHooks ( config . hooks )
}
}
2020-07-02 13:02:35 +00:00
2024-05-15 10:51:14 +00:00
// Restart Nuxt when layer directories are added or removed
const layersDir = withTrailingSlash ( resolve ( nuxt . options . rootDir , 'layers' ) )
nuxt . hook ( 'builder:watch' , ( event , relativePath ) = > {
const path = resolve ( nuxt . options . srcDir , relativePath )
if ( event === 'addDir' || event === 'unlinkDir' ) {
if ( path . startsWith ( layersDir ) ) {
return nuxt . callHook ( 'restart' , { hard : true } )
}
}
} )
2021-06-24 14:06:16 +00:00
// Set nuxt instance for useNuxt
nuxtCtx . set ( nuxt )
nuxt . hook ( 'close' , ( ) = > nuxtCtx . unset ( ) )
2024-03-15 17:59:41 +00:00
const coreTypePackages = nuxt . options . typescript . hoist || [ ]
2024-03-17 00:18:31 +00:00
const packageJSON = await readPackageJSON ( nuxt . options . rootDir ) . catch ( ( ) = > ( { } ) as PackageJson )
const dependencies = new Set ( [ . . . Object . keys ( packageJSON . dependencies || { } ) , . . . Object . keys ( packageJSON . devDependencies || { } ) ] )
2024-03-09 06:48:15 +00:00
const paths = Object . fromEntries ( await Promise . all ( coreTypePackages . map ( async ( pkg ) = > {
2024-03-17 00:18:31 +00:00
// ignore packages that exist in `package.json` as these can be resolved by TypeScript
if ( dependencies . has ( pkg ) && ! ( pkg in nightlies ) ) { return [ ] }
// deduplicate types for nightly releases
if ( pkg in nightlies ) {
const nightly = nightlies [ pkg as keyof typeof nightlies ]
const path = await _resolvePath ( nightly , { url : nuxt.options.modulesDir } ) . then ( r = > resolvePackageJSON ( r ) ) . catch ( ( ) = > null )
if ( path ) {
return [ [ pkg , [ dirname ( path ) ] ] , [ nightly , [ dirname ( path ) ] ] ]
}
}
2024-03-09 06:07:39 +00:00
const path = await _resolvePath ( pkg , { url : nuxt.options.modulesDir } ) . then ( r = > resolvePackageJSON ( r ) ) . catch ( ( ) = > null )
2024-03-17 00:18:31 +00:00
if ( path ) {
return [ [ pkg , [ dirname ( path ) ] ] ]
}
return [ ]
} ) ) . then ( r = > r . flat ( ) ) )
2024-03-08 22:58:37 +00:00
// Set nitro resolutions for types that might be obscured with shamefully-hoist=false
nuxt . options . nitro . typescript = defu ( nuxt . options . nitro . typescript , {
2024-04-05 18:08:32 +00:00
tsConfig : { compilerOptions : { paths : { . . . paths } } } ,
2024-03-08 22:58:37 +00:00
} )
2022-04-20 08:52:39 +00:00
// Add nuxt types
2021-10-07 10:09:09 +00:00
nuxt . hook ( 'prepare:types' , ( opts ) = > {
2022-04-20 08:52:39 +00:00
opts . references . push ( { types : 'nuxt' } )
2024-06-12 16:32:53 +00:00
opts . references . push ( { path : resolve ( nuxt . options . buildDir , 'types/app-defaults.d.ts' ) } )
2022-02-07 10:20:01 +00:00
opts . references . push ( { path : resolve ( nuxt . options . buildDir , 'types/plugins.d.ts' ) } )
2022-01-19 18:10:38 +00:00
// Add vue shim
if ( nuxt . options . typescript . shim ) {
2022-02-07 10:20:01 +00:00
opts . references . push ( { path : resolve ( nuxt . options . buildDir , 'types/vue-shim.d.ts' ) } )
2022-01-19 18:10:38 +00:00
}
2024-05-22 22:18:58 +00:00
// Add shims for `#build/*` imports that do not already have matching types
opts . references . push ( { path : resolve ( nuxt . options . buildDir , 'types/build.d.ts' ) } )
2022-02-08 19:09:44 +00:00
// Add module augmentations directly to NuxtConfig
opts . references . push ( { path : resolve ( nuxt . options . buildDir , 'types/schema.d.ts' ) } )
2022-08-17 15:23:13 +00:00
opts . references . push ( { path : resolve ( nuxt . options . buildDir , 'types/app.config.d.ts' ) } )
2022-09-12 13:41:15 +00:00
2024-03-08 22:58:37 +00:00
// Set Nuxt resolutions for types that might be obscured with shamefully-hoist=false
2024-03-09 06:56:21 +00:00
opts . tsConfig . compilerOptions = defu ( opts . tsConfig . compilerOptions , { paths : { . . . paths } } )
2024-03-08 22:58:37 +00:00
2022-09-12 13:41:15 +00:00
for ( const layer of nuxt . options . _layers ) {
const declaration = join ( layer . cwd , 'index.d.ts' )
if ( fse . existsSync ( declaration ) ) {
opts . references . push ( { path : declaration } )
}
}
2021-10-07 10:09:09 +00:00
} )
2024-05-03 12:57:28 +00:00
// Prompt to install `@nuxt/scripts` if user has configured it
// @ts-expect-error scripts types are not present as the module is not installed
if ( nuxt . options . scripts ) {
if ( ! nuxt . options . _modules . some ( m = > m === '@nuxt/scripts' || m === '@nuxt/scripts-nightly' ) ) {
await import ( '../core/features' ) . then ( ( { installNuxtModule } ) = > installNuxtModule ( '@nuxt/scripts' ) )
}
}
2023-10-10 11:14:55 +00:00
// Add plugin normalization plugin
2023-06-19 23:00:03 +00:00
addBuildPlugin ( RemovePluginMetadataPlugin ( nuxt ) )
2022-01-24 13:25:23 +00:00
// Add import protection
const config = {
rootDir : nuxt.options.rootDir ,
2022-09-10 11:51:17 +00:00
// Exclude top-level resolutions by plugins
2024-03-21 13:18:09 +00:00
exclude : [ join ( nuxt . options . srcDir , 'index.html' ) ] ,
2024-04-05 18:08:32 +00:00
patterns : nuxtImportProtections ( nuxt ) ,
2022-01-24 13:25:23 +00:00
}
2023-05-02 11:17:41 +00:00
addVitePlugin ( ( ) = > ImportProtectionPlugin . vite ( config ) )
addWebpackPlugin ( ( ) = > ImportProtectionPlugin . webpack ( config ) )
2022-01-24 13:25:23 +00:00
2023-08-07 22:05:29 +00:00
// add resolver for modules used in virtual files
addVitePlugin ( ( ) = > resolveDeepImportsPlugin ( nuxt ) )
2024-05-07 14:04:21 +00:00
// Add transform for `onPrehydrate` lifecycle hook
addBuildPlugin ( prehydrateTransformPlugin ( nuxt ) )
2023-04-03 13:18:24 +00:00
if ( nuxt . options . experimental . localLayerAliases ) {
// Add layer aliasing support for ~, ~~, @ and @@ aliases
2023-05-02 11:17:41 +00:00
addVitePlugin ( ( ) = > LayerAliasingPlugin . vite ( {
2023-08-24 12:06:44 +00:00
sourcemap : ! ! nuxt . options . sourcemap . server || ! ! nuxt . options . sourcemap . client ,
2023-05-18 14:10:12 +00:00
dev : nuxt.options.dev ,
root : nuxt.options.srcDir ,
2023-04-03 13:18:24 +00:00
// skip top-level layer (user's project) as the aliases will already be correctly resolved
2024-04-05 18:08:32 +00:00
layers : nuxt.options._layers.slice ( 1 ) ,
2023-04-03 13:18:24 +00:00
} ) )
2023-05-02 11:17:41 +00:00
addWebpackPlugin ( ( ) = > LayerAliasingPlugin . webpack ( {
2023-08-24 12:06:44 +00:00
sourcemap : ! ! nuxt . options . sourcemap . server || ! ! nuxt . options . sourcemap . client ,
2023-05-18 14:10:12 +00:00
dev : nuxt.options.dev ,
root : nuxt.options.srcDir ,
2023-04-03 13:18:24 +00:00
// skip top-level layer (user's project) as the aliases will already be correctly resolved
layers : nuxt.options._layers.slice ( 1 ) ,
2024-04-05 18:08:32 +00:00
transform : true ,
2023-04-03 13:18:24 +00:00
} ) )
}
2023-08-25 13:57:25 +00:00
nuxt . hook ( 'modules:done' , async ( ) = > {
2023-03-07 09:30:05 +00:00
// Add unctx transform
2023-04-10 21:57:13 +00:00
const options = {
2023-08-24 12:06:44 +00:00
sourcemap : ! ! nuxt . options . sourcemap . server || ! ! nuxt . options . sourcemap . client ,
2023-08-25 13:57:25 +00:00
transformerOptions : {
. . . nuxt . options . optimization . asyncTransforms ,
2024-04-05 18:08:32 +00:00
helperModule : await tryResolveModule ( 'unctx' , nuxt . options . module sDir ) ? ? 'unctx' ,
} ,
2023-08-25 13:57:25 +00:00
} satisfies UnctxTransformPluginOptions
2023-05-02 11:17:41 +00:00
addVitePlugin ( ( ) = > UnctxTransformPlugin . vite ( options ) )
addWebpackPlugin ( ( ) = > UnctxTransformPlugin . webpack ( options ) )
2023-03-07 09:30:05 +00:00
// Add composable tree-shaking optimisations
2023-04-03 13:18:24 +00:00
const serverTreeShakeOptions : TreeShakeComposablesPluginOptions = {
2023-08-24 12:06:44 +00:00
sourcemap : ! ! nuxt . options . sourcemap . server ,
2024-04-05 18:08:32 +00:00
composables : nuxt.options.optimization.treeShake.composables.server ,
2023-03-07 09:30:05 +00:00
}
if ( Object . keys ( serverTreeShakeOptions . composables ) . length ) {
2023-05-02 11:17:41 +00:00
addVitePlugin ( ( ) = > TreeShakeComposablesPlugin . vite ( serverTreeShakeOptions ) , { client : false } )
addWebpackPlugin ( ( ) = > TreeShakeComposablesPlugin . webpack ( serverTreeShakeOptions ) , { client : false } )
2023-03-07 09:30:05 +00:00
}
2023-04-03 13:18:24 +00:00
const clientTreeShakeOptions : TreeShakeComposablesPluginOptions = {
2023-08-24 12:06:44 +00:00
sourcemap : ! ! nuxt . options . sourcemap . client ,
2024-04-05 18:08:32 +00:00
composables : nuxt.options.optimization.treeShake.composables.client ,
2023-03-07 09:30:05 +00:00
}
if ( Object . keys ( clientTreeShakeOptions . composables ) . length ) {
2023-05-02 11:17:41 +00:00
addVitePlugin ( ( ) = > TreeShakeComposablesPlugin . vite ( clientTreeShakeOptions ) , { server : false } )
addWebpackPlugin ( ( ) = > TreeShakeComposablesPlugin . webpack ( clientTreeShakeOptions ) , { server : false } )
2023-03-07 09:30:05 +00:00
}
2023-01-25 12:52:00 +00:00
} )
2022-04-01 09:55:23 +00:00
2022-07-07 16:04:38 +00:00
if ( ! nuxt . options . dev ) {
2022-10-26 12:43:37 +00:00
// DevOnly component tree-shaking - build time only
2023-08-24 12:06:44 +00:00
addVitePlugin ( ( ) = > DevOnlyPlugin . vite ( { sourcemap : ! ! nuxt . options . sourcemap . server || ! ! nuxt . options . sourcemap . client } ) )
addWebpackPlugin ( ( ) = > DevOnlyPlugin . webpack ( { sourcemap : ! ! nuxt . options . sourcemap . server || ! ! nuxt . options . sourcemap . client } ) )
2022-07-07 16:04:38 +00:00
}
2023-11-23 21:12:28 +00:00
if ( nuxt . options . dev ) {
// Add plugin to check if layouts are defined without NuxtLayout being instantiated
addPlugin ( resolve ( nuxt . options . appDir , 'plugins/check-if-layout-used' ) )
}
2024-03-15 23:36:47 +00:00
if ( nuxt . options . dev && nuxt . options . features . devLogs ) {
2024-03-27 11:42:43 +00:00
addPlugin ( resolve ( nuxt . options . appDir , 'plugins/dev-server-logs' ) )
2024-03-15 23:36:47 +00:00
addServerPlugin ( resolve ( distDir , 'core/runtime/nitro/dev-server-logs' ) )
nuxt . options . nitro = defu ( nuxt . options . nitro , {
externals : {
2024-04-05 18:08:32 +00:00
inline : [ /#internal\/dev-server-logs-options/ ] ,
2024-03-15 23:36:47 +00:00
} ,
virtual : {
2024-04-05 18:08:32 +00:00
'#internal/dev-server-logs-options' : ( ) = > ` export const rootDir = ${ JSON . stringify ( nuxt . options . rootDir ) } ; ` ,
} ,
2024-03-15 23:36:47 +00:00
} )
}
2023-08-07 22:57:35 +00:00
// Transform initial composable call within `<script setup>` to preserve context
if ( nuxt . options . experimental . asyncContext ) {
addBuildPlugin ( AsyncContextInjectionPlugin ( nuxt ) )
}
2022-09-05 13:46:47 +00:00
// TODO: [Experimental] Avoid emitting assets when flag is enabled
2023-12-25 14:03:29 +00:00
if ( nuxt . options . features . noScripts && ! nuxt . options . dev ) {
2022-09-05 13:46:47 +00:00
nuxt . hook ( 'build:manifest' , async ( manifest ) = > {
for ( const file in manifest ) {
if ( manifest [ file ] . resourceType === 'script' ) {
await fse . rm ( resolve ( nuxt . options . buildDir , 'dist/client' , withoutLeadingSlash ( nuxt . options . app . buildAssetsDir ) , manifest [ file ] . file ) , { force : true } )
manifest [ file ] . file = ''
}
}
} )
}
2023-02-14 00:23:12 +00:00
// Transpile #app if it is imported directly from subpath export
nuxt . options . build . transpile . push ( 'nuxt/app' )
2022-06-10 13:33:16 +00:00
// Transpile layers within node_modules
nuxt . options . build . transpile . push (
2024-04-05 18:08:32 +00:00
. . . nuxt . options . _layers . filter ( i = > i . cwd . includes ( 'node_modules' ) ) . map ( i = > i . cwd as string ) ,
2022-06-10 13:33:16 +00:00
)
2024-05-24 10:17:35 +00:00
// Ensure we can resolve dependencies within layers
nuxt . options . module sDir.push ( . . . nuxt . options . _layers . map ( l = > resolve ( l . cwd , 'node_modules' ) ) )
2021-04-02 11:47:01 +00:00
// Init user modules
2022-10-27 10:36:37 +00:00
await nuxt . callHook ( 'modules:before' )
2024-06-07 10:17:37 +00:00
const module sToInstall = new Map < string | NuxtModule , Record < string , any > > ( )
2023-03-03 17:52:55 +00:00
const watchedPaths = new Set < string > ( )
const specifiedModules = new Set < string > ( )
for ( const _mod of nuxt . options . module s ) {
const mod = Array . isArray ( _mod ) ? _mod [ 0 ] : _mod
if ( typeof mod !== 'string' ) { continue }
const modPath = await resolvePath ( resolveAlias ( mod ) )
specifiedModules . add ( modPath )
}
// Automatically register user modules
for ( const config of nuxt . options . _layers . map ( layer = > layer . config ) . reverse ( ) ) {
2023-09-12 14:27:28 +00:00
const module sDir = ( config . rootDir === nuxt . options . rootDir ? nuxt.options : config ) . dir ? . module s || 'modules'
2023-03-03 17:52:55 +00:00
const layerModules = await resolveFiles ( config . srcDir , [
2023-09-12 14:27:28 +00:00
` ${ module sDir } /*{ ${ nuxt . options . extensions . join ( ',' ) } } ` ,
2024-04-05 18:08:32 +00:00
` ${ module sDir } /*/index{ ${ nuxt . options . extensions . join ( ',' ) } } ` ,
2023-03-03 17:52:55 +00:00
] )
for ( const mod of layerModules ) {
2023-07-26 09:16:01 +00:00
watchedPaths . add ( mod )
2023-03-03 17:52:55 +00:00
if ( specifiedModules . has ( mod ) ) { continue }
specifiedModules . add ( mod )
2024-06-07 10:17:37 +00:00
module sToInstall.set ( mod , { } )
2023-03-03 17:52:55 +00:00
}
}
// Register user and then ad-hoc modules
2024-06-07 10:17:37 +00:00
for ( const key of [ 'modules' , '_modules' ] as const ) {
for ( const item of nuxt . options [ key as 'modules' ] ) {
if ( item ) {
const [ key , options = { } ] = Array . isArray ( item ) ? item : [ item ]
if ( ! module sToInstall.has ( key ) ) {
module sToInstall.set ( key , options )
}
}
}
}
2023-03-03 17:52:55 +00:00
2021-11-02 11:27:25 +00:00
// Add <NuxtWelcome>
addComponent ( {
name : 'NuxtWelcome' ,
2023-03-06 11:33:40 +00:00
priority : 10 , // built-in that we do not expect the user to override
2024-05-01 13:10:33 +00:00
filePath : resolve ( nuxt . options . appDir , 'components/welcome' ) ,
2021-11-02 11:27:25 +00:00
} )
2022-03-14 10:47:24 +00:00
addComponent ( {
name : 'NuxtLayout' ,
2023-03-06 11:33:40 +00:00
priority : 10 , // built-in that we do not expect the user to override
2024-04-05 18:08:32 +00:00
filePath : resolve ( nuxt . options . appDir , 'components/nuxt-layout' ) ,
2022-03-14 10:47:24 +00:00
} )
2022-03-16 15:49:53 +00:00
// Add <NuxtErrorBoundary>
addComponent ( {
name : 'NuxtErrorBoundary' ,
2023-03-06 11:33:40 +00:00
priority : 10 , // built-in that we do not expect the user to override
2024-04-05 18:08:32 +00:00
filePath : resolve ( nuxt . options . appDir , 'components/nuxt-error-boundary' ) ,
2022-03-16 15:49:53 +00:00
} )
2021-11-15 11:57:38 +00:00
// Add <ClientOnly>
addComponent ( {
name : 'ClientOnly' ,
2023-03-06 11:33:40 +00:00
priority : 10 , // built-in that we do not expect the user to override
2024-04-05 18:08:32 +00:00
filePath : resolve ( nuxt . options . appDir , 'components/client-only' ) ,
2021-11-15 11:57:38 +00:00
} )
2022-10-26 12:43:37 +00:00
// Add <DevOnly>
addComponent ( {
name : 'DevOnly' ,
2023-03-06 11:33:40 +00:00
priority : 10 , // built-in that we do not expect the user to override
2024-04-05 18:08:32 +00:00
filePath : resolve ( nuxt . options . appDir , 'components/dev-only' ) ,
2022-10-26 12:43:37 +00:00
} )
2022-04-19 19:13:55 +00:00
// Add <ServerPlaceholder>
addComponent ( {
name : 'ServerPlaceholder' ,
2023-03-06 11:33:40 +00:00
priority : 10 , // built-in that we do not expect the user to override
2024-04-05 18:08:32 +00:00
filePath : resolve ( nuxt . options . appDir , 'components/server-placeholder' ) ,
2022-04-19 19:13:55 +00:00
} )
2022-03-14 13:36:32 +00:00
// Add <NuxtLink>
addComponent ( {
name : 'NuxtLink' ,
2023-03-06 11:33:40 +00:00
priority : 10 , // built-in that we do not expect the user to override
2024-04-05 18:08:32 +00:00
filePath : resolve ( nuxt . options . appDir , 'components/nuxt-link' ) ,
2022-03-14 13:36:32 +00:00
} )
2022-07-07 17:59:55 +00:00
// Add <NuxtLoadingIndicator>
addComponent ( {
name : 'NuxtLoadingIndicator' ,
2023-03-06 11:33:40 +00:00
priority : 10 , // built-in that we do not expect the user to override
2024-04-05 18:08:32 +00:00
filePath : resolve ( nuxt . options . appDir , 'components/nuxt-loading-indicator' ) ,
2022-07-07 17:59:55 +00:00
} )
2024-04-17 15:58:13 +00:00
// Add <NuxtRouteAnnouncer>
addComponent ( {
name : 'NuxtRouteAnnouncer' ,
priority : 10 , // built-in that we do not expect the user to override
filePath : resolve ( nuxt . options . appDir , 'components/nuxt-route-announcer' ) ,
mode : 'client' ,
} )
2023-03-08 21:13:06 +00:00
// Add <NuxtClientFallback>
if ( nuxt . options . experimental . clientFallback ) {
addComponent ( {
name : 'NuxtClientFallback' ,
2023-09-13 21:56:15 +00:00
_raw : true ,
2023-03-08 21:13:06 +00:00
priority : 10 , // built-in that we do not expect the user to override
filePath : resolve ( nuxt . options . appDir , 'components/client-fallback.client' ) ,
2024-04-05 18:08:32 +00:00
mode : 'client' ,
2023-03-08 21:13:06 +00:00
} )
addComponent ( {
name : 'NuxtClientFallback' ,
2023-09-13 21:56:15 +00:00
_raw : true ,
2023-03-08 21:13:06 +00:00
priority : 10 , // built-in that we do not expect the user to override
filePath : resolve ( nuxt . options . appDir , 'components/client-fallback.server' ) ,
2024-04-05 18:08:32 +00:00
mode : 'server' ,
2023-03-08 21:13:06 +00:00
} )
}
2022-11-24 12:24:14 +00:00
// Add <NuxtIsland>
if ( nuxt . options . experimental . componentIslands ) {
addComponent ( {
name : 'NuxtIsland' ,
2023-03-06 11:33:40 +00:00
priority : 10 , // built-in that we do not expect the user to override
2024-04-05 18:08:32 +00:00
filePath : resolve ( nuxt . options . appDir , 'components/nuxt-island' ) ,
2022-11-24 12:24:14 +00:00
} )
2023-08-28 16:46:49 +00:00
2024-03-13 14:39:35 +00:00
if ( ! nuxt . options . ssr && nuxt . options . experimental . componentIslands !== 'auto' ) {
2023-08-28 16:46:49 +00:00
nuxt . options . ssr = true
nuxt . options . nitro . routeRules || = { }
nuxt . options . nitro . routeRules [ '/**' ] = defu ( nuxt . options . nitro . routeRules [ '/**' ] , { ssr : false } )
}
2022-11-24 12:24:14 +00:00
}
2023-10-17 11:07:31 +00:00
// Add stubs for <NuxtImg> and <NuxtPicture>
for ( const name of [ 'NuxtImg' , 'NuxtPicture' ] ) {
addComponent ( {
name ,
export : name ,
priority : - 1 ,
filePath : resolve ( nuxt . options . appDir , 'components/nuxt-stubs' ) ,
// @ts-expect-error TODO: refactor to nuxi
2024-04-05 18:08:32 +00:00
_internal_install : '@nuxt/image' ,
2023-10-17 11:07:31 +00:00
} )
}
2023-09-19 21:31:18 +00:00
// Add prerender payload support
if ( ! nuxt . options . dev && nuxt . options . experimental . payloadExtraction ) {
addPlugin ( resolve ( nuxt . options . appDir , 'plugins/payload.client' ) )
}
2022-10-17 11:15:29 +00:00
// Add experimental cross-origin prefetch support using Speculation Rules API
if ( nuxt . options . experimental . crossOriginPrefetch ) {
addPlugin ( resolve ( nuxt . options . appDir , 'plugins/cross-origin-prefetch.client' ) )
}
2023-02-16 12:43:58 +00:00
// Add experimental page reload support
2023-03-08 12:17:22 +00:00
if ( nuxt . options . experimental . emitRouteChunkError === 'automatic' ) {
2023-02-16 12:43:58 +00:00
addPlugin ( resolve ( nuxt . options . appDir , 'plugins/chunk-reload.client' ) )
}
2023-03-08 12:17:22 +00:00
// Add experimental session restoration support
if ( nuxt . options . experimental . restoreState ) {
addPlugin ( resolve ( nuxt . options . appDir , 'plugins/restore-state.client' ) )
}
2023-02-16 12:43:58 +00:00
2023-04-10 11:33:14 +00:00
// Add experimental automatic view transition api support
if ( nuxt . options . experimental . viewTransition ) {
addPlugin ( resolve ( nuxt . options . appDir , 'plugins/view-transitions.client' ) )
}
// Add experimental support for custom types in JSON payload
2023-04-07 10:34:35 +00:00
if ( nuxt . options . experimental . renderJsonPayloads ) {
2023-04-11 11:58:43 +00:00
nuxt . hooks . hook ( 'modules:done' , ( ) = > {
addPlugin ( resolve ( nuxt . options . appDir , 'plugins/revive-payload.client' ) )
addPlugin ( resolve ( nuxt . options . appDir , 'plugins/revive-payload.server' ) )
2023-04-07 10:34:35 +00:00
} )
}
2022-09-13 10:57:14 +00:00
// Track components used to render for webpack
if ( nuxt . options . builder === '@nuxt/webpack-builder' ) {
addPlugin ( resolve ( nuxt . options . appDir , 'plugins/preload.server' ) )
}
2023-05-15 16:17:39 +00:00
const envMap = {
// defaults from `builder` based on package name
'@nuxt/vite-builder' : 'vite/client' ,
'@nuxt/webpack-builder' : 'webpack/module' ,
// simpler overrides from `typescript.builder` for better DX
2024-04-05 18:08:32 +00:00
'vite' : 'vite/client' ,
'webpack' : 'webpack/module' ,
2023-05-15 16:17:39 +00:00
// default 'merged' builder environment for module authors
2024-04-05 18:08:32 +00:00
'shared' : '@nuxt/schema/builder-env' ,
2023-05-15 16:17:39 +00:00
}
nuxt . hook ( 'prepare:types' , ( { references } ) = > {
// Disable entirely if `typescript.builder` is false
if ( nuxt . options . typescript . builder === false ) { return }
const overrideEnv = nuxt . options . typescript . builder && envMap [ nuxt . options . typescript . builder ]
// If there's no override, infer based on builder. If a custom builder is provided, we disable shared types
const defaultEnv = typeof nuxt . options . builder === 'string' ? envMap [ nuxt . options . builder ] : false
const types = overrideEnv || defaultEnv
if ( types ) { references . push ( { types } ) }
} )
2022-10-15 10:56:15 +00:00
// Add nuxt app debugger
if ( nuxt . options . debug ) {
addPlugin ( resolve ( nuxt . options . appDir , 'plugins/debug' ) )
}
2024-06-07 10:17:37 +00:00
for ( const [ key , options ] of module sToInstall ) {
await installModule ( key , options )
2020-07-02 13:02:35 +00:00
}
2024-04-19 08:48:19 +00:00
// (Re)initialise ignore handler with resolved ignores from modules
nuxt . _ignore = ignore ( nuxt . options . ignoreOptions )
nuxt . _ignore . add ( resolveIgnorePatterns ( ) )
2022-10-27 10:36:37 +00:00
await nuxt . callHook ( 'modules:done' )
2020-07-02 13:02:35 +00:00
2023-12-16 09:12:22 +00:00
if ( nuxt . options . experimental . appManifest ) {
addRouteMiddleware ( {
name : 'manifest-route-rule' ,
path : resolve ( nuxt . options . appDir , 'middleware/manifest-route-rule' ) ,
2024-04-05 18:08:32 +00:00
global : true ,
2023-12-16 09:12:22 +00:00
} )
2024-06-07 15:42:32 +00:00
if ( nuxt . options . experimental . checkOutdatedBuildInterval !== false ) {
addPlugin ( resolve ( nuxt . options . appDir , 'plugins/check-outdated-build.client' ) )
}
2023-12-16 09:12:22 +00:00
}
2023-07-26 09:16:01 +00:00
nuxt . hooks . hook ( 'builder:watch' , ( event , relativePath ) = > {
const path = resolve ( nuxt . options . srcDir , relativePath )
2023-03-09 11:46:08 +00:00
// Local module patterns
if ( watchedPaths . has ( path ) ) {
return nuxt . callHook ( 'restart' , { hard : true } )
}
// User provided patterns
2023-07-30 18:47:29 +00:00
const layerRelativePaths = nuxt . options . _layers . map ( l = > relative ( l . config . srcDir || l . cwd , path ) )
2023-03-09 11:46:08 +00:00
for ( const pattern of nuxt . options . watch ) {
if ( typeof pattern === 'string' ) {
2023-10-10 11:14:55 +00:00
// Test (normalized) strings against absolute path and relative path to any layer `srcDir`
2023-07-30 18:47:29 +00:00
if ( pattern === path || layerRelativePaths . includes ( pattern ) ) { return nuxt . callHook ( 'restart' ) }
2023-03-09 11:46:08 +00:00
continue
}
2023-07-30 18:47:29 +00:00
// Test regular expressions against path to _any_ layer `srcDir`
if ( layerRelativePaths . some ( p = > pattern . test ( p ) ) ) {
return nuxt . callHook ( 'restart' )
}
2023-03-09 11:46:08 +00:00
}
2024-05-02 13:24:31 +00:00
// Restart Nuxt when new `app/` dir is added
if ( event === 'addDir' && path === resolve ( nuxt . options . srcDir , 'app' ) ) {
logger . info ( ` \` ${ path } / \` ${ event === 'addDir' ? 'created' : 'removed' } ` )
return nuxt . callHook ( 'restart' , { hard : true } )
}
2023-03-09 11:46:08 +00:00
// Core Nuxt files: app.vue, error.vue and app.config.ts
const isFileChange = [ 'add' , 'unlink' ] . includes ( event )
2023-05-22 20:25:42 +00:00
if ( isFileChange && RESTART_RE . test ( path ) ) {
2023-09-19 21:26:15 +00:00
logger . info ( ` \` ${ path } \` ${ event === 'add' ? 'created' : 'removed' } ` )
2023-03-09 11:46:08 +00:00
return nuxt . callHook ( 'restart' )
}
} )
2022-06-15 11:52:46 +00:00
// Normalize windows transpile paths added by modules
nuxt . options . build . transpile = nuxt . options . build . transpile . map ( t = > typeof t === 'string' ? normalize ( t ) : t )
2022-06-08 19:49:11 +00:00
addModuleTranspiles ( )
2021-10-06 17:59:35 +00:00
2022-04-07 11:28:04 +00:00
// Init nitro
await initNitro ( nuxt )
2023-06-29 09:14:35 +00:00
// TODO: remove when app manifest support is landed in https://github.com/nuxt/nuxt/pull/21641
// Add prerender payload support
2023-07-18 11:31:35 +00:00
const nitro = useNitro ( )
if ( nitro . options . static && nuxt . options . experimental . payloadExtraction === undefined ) {
2023-09-19 21:26:15 +00:00
logger . warn ( 'Using experimental payload extraction for full-static output. You can opt-out by setting `experimental.payloadExtraction` to `false`.' )
2023-06-29 09:14:35 +00:00
nuxt . options . experimental . payloadExtraction = true
}
2023-07-18 11:31:35 +00:00
nitro . options . replace [ 'process.env.NUXT_PAYLOAD_EXTRACTION' ] = String ( ! ! nuxt . options . experimental . payloadExtraction )
nitro . options . _config . replace ! [ 'process.env.NUXT_PAYLOAD_EXTRACTION' ] = String ( ! ! nuxt . options . experimental . payloadExtraction )
2023-06-29 09:14:35 +00:00
if ( ! nuxt . options . dev && nuxt . options . experimental . payloadExtraction ) {
addPlugin ( resolve ( nuxt . options . appDir , 'plugins/payload.client' ) )
}
2024-05-23 11:35:21 +00:00
// Show compatibility version banner when Nuxt is running with a compatibility version
// that is different from the current major version
if ( ! ( satisfies ( nuxt . _version , nuxt . options . future . compatibilityVersion + '.x' ) ) ) {
console . info ( ` Running with compatibility version \` ${ nuxt . options . future . compatibilityVersion } \` ` )
}
2021-04-02 11:47:01 +00:00
await nuxt . callHook ( 'ready' , nuxt )
}
2020-07-02 13:02:35 +00:00
2021-04-15 19:17:44 +00:00
export async function loadNuxt ( opts : LoadNuxtOptions ) : Promise < Nuxt > {
2021-10-18 09:03:39 +00:00
const options = await loadNuxtConfig ( opts )
2021-03-28 21:12:16 +00:00
2021-11-02 11:27:25 +00:00
// Temporary until finding better placement for each
2021-08-11 20:28:38 +00:00
options . appDir = options . alias [ '#app' ] = resolve ( distDir , 'app' )
2021-03-28 21:12:16 +00:00
options . _majorVersion = 3
2023-04-30 09:06:43 +00:00
2024-06-07 14:24:56 +00:00
// De-duplicate key arrays
for ( const key in options . app . head || { } ) {
options . app . head [ key as 'link' ] = deduplicateArray ( options . app . head [ key as 'link' ] )
}
2023-10-12 15:50:49 +00:00
// Nuxt DevTools only works for Vite
if ( options . builder === '@nuxt/vite-builder' ) {
const isDevToolsEnabled = typeof options . devtools === 'boolean'
? options . devtools
: options . devtools ? . enabled !== false // enabled by default unless explicitly disabled
if ( isDevToolsEnabled ) {
if ( ! options . _modules . some ( m = > m === '@nuxt/devtools' || m === '@nuxt/devtools-edge' ) ) {
options . _modules . push ( '@nuxt/devtools' )
}
2023-04-30 09:06:43 +00:00
}
}
2024-05-03 12:57:28 +00:00
if ( ! options . _modules . some ( m = > m === '@nuxt/scripts' || m === '@nuxt/scripts-nightly' ) ) {
options . imports = defu ( options . imports , {
presets : [ scriptsStubsPreset ] ,
} )
}
2023-07-30 20:25:00 +00:00
// Nuxt Webpack Builder is currently opt-in
if ( options . builder === '@nuxt/webpack-builder' ) {
2023-10-11 10:31:14 +00:00
if ( ! await import ( './features' ) . then ( r = > r . ensurePackageInstalled ( '@nuxt/webpack-builder' , {
rootDir : options.rootDir ,
2024-04-05 18:08:32 +00:00
searchPaths : options.modulesDir ,
2023-10-11 10:31:14 +00:00
} ) ) ) {
2023-07-30 20:25:00 +00:00
logger . warn ( 'Failed to install `@nuxt/webpack-builder`, please install it manually, or change the `builder` option to vite in `nuxt.config`' )
}
}
2023-04-30 09:06:43 +00:00
// Add core modules
2022-06-10 13:33:16 +00:00
options . _modules . push ( pagesModule , metaModule , componentsModule )
2022-08-23 14:22:11 +00:00
options . _modules . push ( [ importsModule , {
2022-06-10 13:33:16 +00:00
transform : {
include : options._layers
2022-06-12 20:12:43 +00:00
. filter ( i = > i . cwd && i . cwd . includes ( 'node_modules' ) )
2024-04-05 18:08:32 +00:00
. map ( i = > new RegExp ( ` (^| \\ /) ${ escapeRE ( i . cwd ! . split ( 'node_modules/' ) . pop ( ) ! ) } ( \\ /| $ )(?!node_modules \\ /) ` ) ) ,
} ,
2022-06-10 13:33:16 +00:00
} ] )
2023-01-23 18:07:21 +00:00
options . _modules . push ( schemaModule )
2022-09-12 20:06:17 +00:00
options . module sDir.push ( resolve ( options . workspaceDir , 'node_modules' ) )
2021-08-11 21:26:47 +00:00
options . module sDir.push ( resolve ( pkgDir , 'node_modules' ) )
2023-06-05 18:23:38 +00:00
options . build . transpile . push (
2024-04-05 18:08:32 +00:00
'std-env' , // we need to statically replace process.env when used in runtime code
2023-06-05 18:23:38 +00:00
)
2021-11-15 10:25:50 +00:00
options . alias [ 'vue-demi' ] = resolve ( options . appDir , 'compat/vue-demi' )
options . alias [ '@vue/composition-api' ] = resolve ( options . appDir , 'compat/capi' )
2022-04-20 18:25:09 +00:00
if ( options . telemetry !== false && ! process . env . NUXT_TELEMETRY_DISABLED ) {
options . _modules . push ( '@nuxt/telemetry' )
}
2021-03-28 21:12:16 +00:00
2024-06-13 17:51:44 +00:00
// Ensure we share key config between Nuxt and Nitro
createPortalProperties ( options . nitro . runtimeConfig , options , [ 'nitro.runtimeConfig' , 'runtimeConfig' ] )
createPortalProperties ( options . nitro . routeRules , options , [ 'nitro.routeRules' , 'routeRules' ] )
// prevent replacement of options.nitro
const nitroOptions = options . nitro
Object . defineProperties ( options , {
nitro : {
configurable : false ,
enumerable : true ,
get : ( ) = > nitroOptions ,
set ( value ) {
Object . assign ( nitroOptions , value )
} ,
} ,
} )
2024-05-09 17:49:35 +00:00
2021-04-02 11:47:01 +00:00
const nuxt = createNuxt ( options )
2024-05-21 08:18:36 +00:00
await Promise . all ( keyDependencies . map ( dependency = > checkDependencyVersion ( dependency , nuxt . _version ) ) )
2023-12-19 20:26:13 +00:00
// We register hooks layer-by-layer so any overrides need to be registered separately
if ( opts . overrides ? . hooks ) {
nuxt . hooks . addHooks ( opts . overrides . hooks )
}
2022-10-15 10:56:15 +00:00
if ( nuxt . options . debug ) {
createDebugger ( nuxt . hooks , { tag : 'nuxt' } )
}
2021-04-15 19:17:44 +00:00
if ( opts . ready !== false ) {
await nuxt . ready ( )
}
2021-04-02 11:47:01 +00:00
2021-03-28 21:12:16 +00:00
return nuxt
}
2023-05-22 20:25:42 +00:00
2024-05-21 08:18:36 +00:00
async function checkDependencyVersion ( name : string , nuxtVersion : string ) : Promise < void > {
const path = await resolvePath ( name ) . catch ( ( ) = > null )
if ( ! path ) { return }
const { version } = await readPackageJSON ( path )
2024-05-21 15:55:22 +00:00
if ( version && gt ( nuxtVersion , version ) ) {
2024-05-21 08:18:36 +00:00
console . warn ( ` [nuxt] Expected \` ${ name } \` to be at least \` ${ nuxtVersion } \` but got \` ${ version } \` . This might lead to unexpected behavior. Check your package.json or refresh your lockfile. ` )
}
}
2024-05-14 17:54:37 +00:00
const RESTART_RE = /^(?:app|error|app\.config)\.(?:js|ts|mjs|jsx|tsx|vue)$/i
2024-06-07 14:24:56 +00:00
function deduplicateArray < T = unknown > ( maybeArray : T ) : T {
if ( ! Array . isArray ( maybeArray ) ) { return maybeArray }
const fresh = [ ]
const hashes = new Set < string > ( )
for ( const item of maybeArray ) {
const _hash = hash ( item )
if ( ! hashes . has ( _hash ) ) {
hashes . add ( _hash )
fresh . push ( item )
}
}
return fresh as T
}
2024-06-13 17:51:44 +00:00
function createPortalProperties ( sourceValue : any , options : NuxtOptions , paths : string [ ] ) {
let sharedValue = sourceValue
for ( const path of paths ) {
const segments = path . split ( '.' )
const key = segments . pop ( ) !
let parent : Record < string , any > = options
while ( segments . length ) {
const key = segments . shift ( ) !
parent = parent [ key ] || ( parent [ key ] = { } )
}
delete parent [ key ]
Object . defineProperties ( parent , {
[ key ] : {
configurable : false ,
enumerable : true ,
get : ( ) = > sharedValue ,
set ( value ) {
sharedValue = value
} ,
} ,
} )
}
}