+ expect(treeshaken).not.toContain('ClientImport')
+ expect(treeshaken).toContain('import { Glob } from \'#components\'')
- // treeshake .client slot
- expect(treeshaken).not.toContain('ByeBye')
- // don't treeshake variables that has the same name as .client components
- expect(treeshaken).toContain('NotDotClientComponent')
- expect(treeshaken).not.toContain('(DotClientComponent')
+ // treeshake .client slot
+ expect(treeshaken).not.toContain('ByeBye')
+ // don't treeshake variables that has the same name as .client components
+ expect(treeshaken).toContain('NotDotClientComponent')
+ expect(treeshaken).not.toContain('(DotClientComponent')
- expect(treeshaken).not.toContain('AutoImportedComponent')
- expect(treeshaken).toContain('AutoImportedNotTreeShakenComponent')
+ expect(treeshaken).not.toContain('AutoImportedComponent')
+ expect(treeshaken).toContain('AutoImportedNotTreeShakenComponent')
- expect(treeshaken).not.toContain('Both')
- expect(treeshaken).not.toContain('AreTreeshaken')
+ expect(treeshaken).not.toContain('Both')
+ expect(treeshaken).not.toContain('AreTreeshaken')
- if (state.options.isProduction === false) {
- // treeshake at inlined template
- expect(treeshaken).not.toContain('ssrRenderComponent($setup["HelloWorld"]')
- expect(treeshaken).toContain('ssrRenderComponent($setup["Glob"]')
- } else {
- // treeshake unref
- expect(treeshaken).not.toContain('ssrRenderComponent(_unref(HelloWorld')
- expect(treeshaken).toContain('ssrRenderComponent(_unref(Glob')
- }
- expect(treeshaken.replace(/data-v-\w{8}/g, 'data-v-one-hash').replace(/scoped=\w{8}/g, 'scoped=one-hash')).toMatchSnapshot()
- })
- }
+ if (state.options.isProduction === false) {
+ // treeshake at inlined template
+ expect(treeshaken).not.toContain('ssrRenderComponent($setup["HelloWorld"]')
+ expect(treeshaken).toContain('ssrRenderComponent($setup["Glob"]')
+ } else {
+ // treeshake unref
+ expect(treeshaken).not.toContain('ssrRenderComponent(_unref(HelloWorld')
+ expect(treeshaken).toContain('ssrRenderComponent(_unref(Glob')
+ }
+ expect(treeshaken.replace(/data-v-\w{8}/g, 'data-v-one-hash').replace(/scoped=\w{8}/g, 'scoped=one-hash')).toMatchSnapshot()
+ })
it('should not treeshake reused component #26137', async () => {
const treeshaken = await treeshake(`import { resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode } from "vue"
import { ssrRenderComponent as _ssrRenderComponent, ssrRenderAttrs as _ssrRenderAttrs } from "vue/server-renderer"
-
+
export function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_AppIcon = _resolveComponent("AppIcon")
const _component_ClientOnly = _resolveComponent("ClientOnly")
-
+
_push(\`\`)
_push(_ssrRenderComponent(_component_AppIcon, { name: "caret-left" }, null, _parent))
_push(_ssrRenderComponent(_component_ClientOnly, null, {
diff --git a/packages/nuxt/types.d.mts b/packages/nuxt/types.d.mts
index b1b7ec3047..f027f93b6a 100644
--- a/packages/nuxt/types.d.mts
+++ b/packages/nuxt/types.d.mts
@@ -1,4 +1,5 @@
-///
+///
+///
import type { DefineNuxtConfig } from 'nuxt/config'
import type { RuntimeConfig, SchemaDefinition } from 'nuxt/schema'
@@ -14,7 +15,28 @@ declare global {
}
// Note: Keep in sync with packages/nuxt/src/core/templates.ts
-declare module 'nitropack' {
+declare module 'nitro/types' {
+ interface NitroRuntimeConfigApp {
+ buildAssetsDir: string
+ cdnURL: string
+ }
+ interface NitroRuntimeConfig extends RuntimeConfig {}
+ interface NitroRouteConfig {
+ ssr?: boolean
+ experimentalNoScripts?: boolean
+ }
+ interface NitroRouteRules {
+ ssr?: boolean
+ experimentalNoScripts?: boolean
+ appMiddleware?: Record
+ }
+ interface NitroRuntimeHooks {
+ 'dev:ssr-logs': (ctx: { logs: LogObject[], path: string }) => void | Promise
+ 'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event }) => void | Promise
+ 'render:island': (islandResponse: NuxtIslandResponse, context: { event: H3Event, islandContext: NuxtIslandContext }) => void | Promise
+ }
+}
+declare module 'nitropack/types' {
interface NitroRuntimeConfigApp {
buildAssetsDir: string
cdnURL: string
diff --git a/packages/nuxt/types.d.ts b/packages/nuxt/types.d.ts
index 1733bdec62..2ecb9dc72e 100644
--- a/packages/nuxt/types.d.ts
+++ b/packages/nuxt/types.d.ts
@@ -1,4 +1,6 @@
-///
+///
+///
+
import type { DefineNuxtConfig } from 'nuxt/config'
import type { RuntimeConfig, SchemaDefinition } from 'nuxt/schema'
import type { H3Event } from 'h3'
@@ -13,7 +15,28 @@ declare global {
}
// Note: Keep in sync with packages/nuxt/src/core/templates.ts
-declare module 'nitropack' {
+declare module 'nitro/types' {
+ interface NitroRuntimeConfigApp {
+ buildAssetsDir: string
+ cdnURL: string
+ }
+ interface NitroRuntimeConfig extends RuntimeConfig {}
+ interface NitroRouteConfig {
+ ssr?: boolean
+ experimentalNoScripts?: boolean
+ }
+ interface NitroRouteRules {
+ ssr?: boolean
+ experimentalNoScripts?: boolean
+ appMiddleware?: Record
+ }
+ interface NitroRuntimeHooks {
+ 'dev:ssr-logs': (ctx: { logs: LogObject[], path: string }) => void | Promise
+ 'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event }) => void | Promise
+ 'render:island': (islandResponse: NuxtIslandResponse, context: { event: H3Event, islandContext: NuxtIslandContext }) => void | Promise
+ }
+}
+declare module 'nitropack/types' {
interface NitroRuntimeConfigApp {
buildAssetsDir: string
cdnURL: string
diff --git a/packages/schema/build.config.ts b/packages/schema/build.config.ts
index e7179a174f..22f17cbef8 100644
--- a/packages/schema/build.config.ts
+++ b/packages/schema/build.config.ts
@@ -23,6 +23,8 @@ export default defineBuildConfig({
externals: [
// Type imports
'#app/components/nuxt-link',
+ 'cssnano',
+ 'autoprefixer',
'ofetch',
'vue-router',
'@nuxt/telemetry',
@@ -31,6 +33,7 @@ export default defineBuildConfig({
'vue',
'unctx',
'hookable',
+ 'nitro',
'nitropack',
'webpack',
'webpack-bundle-analyzer',
diff --git a/packages/schema/package.json b/packages/schema/package.json
index 1bc0147c36..b2c75ee42a 100644
--- a/packages/schema/package.json
+++ b/packages/schema/package.json
@@ -39,28 +39,28 @@
"@types/file-loader": "5.0.4",
"@types/pug": "2.0.10",
"@types/sass-loader": "8.0.8",
- "@unhead/schema": "1.9.13",
- "@vitejs/plugin-vue": "5.0.4",
+ "@unhead/schema": "1.9.14",
+ "@vitejs/plugin-vue": "5.0.5",
"@vitejs/plugin-vue-jsx": "4.0.0",
- "@vue/compiler-core": "3.4.29",
- "@vue/compiler-sfc": "3.4.29",
- "@vue/language-core": "2.0.21",
- "c12": "1.11.1",
- "esbuild": "0.21.5",
+ "@vue/compiler-core": "3.4.31",
+ "@vue/compiler-sfc": "3.4.31",
+ "@vue/language-core": "2.0.24",
+ "c12": "2.0.0-beta.1",
+ "esbuild": "0.23.0",
"esbuild-loader": "4.2.0",
- "h3": "1.11.1",
+ "h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
"ignore": "5.3.1",
- "nitropack": "2.9.6",
+ "nitro": "npm:nitro-nightly@3.0.0-beta-28659787.859de2d6",
"ofetch": "1.3.4",
- "unbuild": "latest",
+ "unbuild": "3.0.0-rc.6",
"unctx": "2.3.1",
"unenv": "1.9.0",
- "vite": "5.3.1",
- "vue": "3.4.29",
+ "vite": "5.3.3",
+ "vue": "3.4.31",
"vue-bundle-renderer": "2.1.0",
"vue-loader": "17.4.2",
- "vue-router": "4.3.3",
- "webpack": "5.92.0",
+ "vue-router": "4.4.0",
+ "webpack": "5.92.1",
"webpack-dev-middleware": "7.2.1"
},
"dependencies": {
@@ -69,7 +69,7 @@
"defu": "^6.1.4",
"hookable": "^5.5.3",
"pathe": "^1.1.2",
- "pkg-types": "^1.1.1",
+ "pkg-types": "^1.1.3",
"scule": "^1.3.0",
"std-env": "^3.7.0",
"ufo": "^1.5.3",
diff --git a/packages/schema/src/config/adhoc.ts b/packages/schema/src/config/adhoc.ts
index b6a849b853..4328909a32 100644
--- a/packages/schema/src/config/adhoc.ts
+++ b/packages/schema/src/config/adhoc.ts
@@ -23,7 +23,7 @@ export default defineUntypedSchema({
/**
* Configure how Nuxt auto-imports composables into your application.
- * @see [Nuxt 3 documentation](https://nuxt.com/docs/guide/directory-structure/composables)
+ * @see [Nuxt documentation](https://nuxt.com/docs/guide/directory-structure/composables)
* @type {typeof import('../src/types/imports').ImportsOptions}
*/
imports: {
diff --git a/packages/schema/src/config/common.ts b/packages/schema/src/config/common.ts
index 152a76a3ca..5940f1c2a9 100644
--- a/packages/schema/src/config/common.ts
+++ b/packages/schema/src/config/common.ts
@@ -1,7 +1,7 @@
import { existsSync } from 'node:fs'
import { readdir } from 'node:fs/promises'
import { defineUntypedSchema } from 'untyped'
-import { join, relative, resolve } from 'pathe'
+import { basename, join, relative, resolve } from 'pathe'
import { isDebug, isDevelopment, isTest } from 'std-env'
import { defu } from 'defu'
import { findWorkspaceDir } from 'pkg-types'
@@ -301,7 +301,8 @@ export default defineUntypedSchema({
$resolve: async (val: string | undefined, get) => {
const isV4 = (await get('future') as Record).compatibilityVersion === 4
if (isV4) {
- return resolve(await get('srcDir') as string, val || '.')
+ const [srcDir, rootDir] = await Promise.all([get('srcDir') as Promise, get('rootDir') as Promise])
+ return resolve(await get('srcDir') as string, val || (srcDir === rootDir ? 'app' : '.'))
}
return val || 'app'
},
@@ -419,8 +420,8 @@ export default defineUntypedSchema({
'@': srcDir,
'~~': rootDir,
'@@': rootDir,
- [assetsDir]: join(srcDir, assetsDir),
- [publicDir]: join(srcDir, publicDir),
+ [basename(assetsDir)]: join(srcDir, assetsDir),
+ [basename(publicDir)]: resolve(srcDir, publicDir),
...val,
}
},
diff --git a/packages/schema/src/config/experimental.ts b/packages/schema/src/config/experimental.ts
index d33390a0be..83fbc5177f 100644
--- a/packages/schema/src/config/experimental.ts
+++ b/packages/schema/src/config/experimental.ts
@@ -7,45 +7,12 @@ export default defineUntypedSchema({
*/
future: {
/**
- * Enable early access to Nuxt v4 features or flags.
+ * Enable early access to future features or flags.
*
- * Setting `compatibilityVersion` to `4` changes defaults throughout your
- * Nuxt configuration, but you can granularly re-enable Nuxt v3 behaviour
- * when testing (see example). Please file issues if so, so that we can
- * address in Nuxt or in the ecosystem.
- *
- * @example
- * ```ts
- * export default defineNuxtConfig({
- * future: {
- * compatibilityVersion: 4,
- * },
- * // To re-enable _all_ Nuxt v3 behaviour, set the following options:
- * srcDir: '.',
- * dir: {
- * app: 'app'
- * },
- * experimental: {
- * compileTemplate: true,
- * templateUtils: true,
- * relativeWatchPaths: true,
- * resetAsyncDataToUndefined: true,
- * defaults: {
- * useAsyncData: {
- * deep: true
- * }
- * }
- * },
- * unhead: {
- * renderSSRHeadOptions: {
- * omitLineBreaks: false
- * }
- * }
- * })
- * ```
- * @type {3 | 4}
+ * It is currently not configurable but may be in future.
+ * @type {4}
*/
- compatibilityVersion: 3,
+ compatibilityVersion: 4,
/**
* This enables early access to the experimental multi-app support.
* @see [Nuxt Issue #21635](https://github.com/nuxt/nuxt/issues/21635)
@@ -146,21 +113,11 @@ export default defineUntypedSchema({
externalVue: true,
/**
- * Tree shakes contents of client-only components from server bundle.
- * @see [Nuxt PR #5750](https://github.com/nuxt/framework/pull/5750)
- * @deprecated This option will no longer be configurable in Nuxt v4
+ * Enable accessing `appConfig` from server routes.
+ *
+ * @deprecated This option is not recommended.
*/
- treeshakeClientOnly: {
- async $resolve (val, get) {
- const isV4 = ((await get('future') as Record).compatibilityVersion === 4)
- if (isV4 && val === false) {
- console.warn('Enabling `experimental.treeshakeClientOnly` in v4 compatibility mode as it will no longer be configurable in Nuxt v4.')
- return true
- }
- return val ?? true
- },
- },
-
+ serverAppConfig: false,
/**
* Emit `app:chunkError` hook when there is an error loading vite/webpack
* chunks.
@@ -265,55 +222,6 @@ export default defineUntypedSchema({
},
},
- /**
- * Config schema support
- * @see [Nuxt Issue #15592](https://github.com/nuxt/nuxt/issues/15592)
- * @deprecated This option will no longer be configurable in Nuxt v4
- */
- configSchema: {
- async $resolve (val, get) {
- const isV4 = ((await get('future') as Record).compatibilityVersion === 4)
- if (isV4 && val === false) {
- console.warn('Enabling `experimental.configSchema` in v4 compatibility mode as it will no longer be configurable in Nuxt v4.')
- return true
- }
- return val ?? true
- },
- },
-
- /**
- * Whether or not to add a compatibility layer for modules, plugins or user code relying on the old
- * `@vueuse/head` API.
- *
- * This is disabled to reduce the client-side bundle by ~0.5kb.
- * @deprecated This feature will be removed in Nuxt v4.
- */
- polyfillVueUseHead: {
- async $resolve (val, get) {
- const isV4 = ((await get('future') as Record).compatibilityVersion === 4)
- if (isV4 && val === true) {
- console.warn('Disabling `experimental.polyfillVueUseHead` in v4 compatibility mode as it will no longer be configurable in Nuxt v4.')
- return false
- }
- return val ?? false
- },
- },
-
- /**
- * Allow disabling Nuxt SSR responses by setting the `x-nuxt-no-ssr` header.
- * @deprecated This feature will be removed in Nuxt v4.
- */
- respectNoSSRHeader: {
- async $resolve (val, get) {
- const isV4 = ((await get('future') as Record).compatibilityVersion === 4)
- if (isV4 && val === true) {
- console.warn('Disabling `experimental.respectNoSSRHeader` in v4 compatibility mode as it will no longer be configurable in Nuxt v4.')
- return false
- }
- return val ?? false
- },
- },
-
/** Resolve `~`, `~~`, `@` and `@@` aliases located within layers with respect to their layer source and root directories. */
localLayerAliases: true,
@@ -336,8 +244,9 @@ export default defineUntypedSchema({
/**
* Set an alternative watcher that will be used as the watching service for Nuxt.
*
- * Nuxt uses 'chokidar-granular' by default, which will ignore top-level directories
- * (like `node_modules` and `.git`) that are excluded from watching.
+ * Nuxt uses 'chokidar-granular' if your source directory is the same as your root
+ * directory . This will ignore top-level directories (like `node_modules` and `.git`)
+ * that are excluded from watching.
*
* You can set this instead to `parcel` to use `@parcel/watcher`, which may improve
* performance in large projects or on Windows platforms.
@@ -347,7 +256,18 @@ export default defineUntypedSchema({
* @see [Parcel watcher](https://github.com/parcel-bundler/watcher)
* @type {'chokidar' | 'parcel' | 'chokidar-granular'}
*/
- watcher: 'chokidar-granular',
+ watcher: {
+ $resolve: async (val, get) => {
+ if (val) {
+ return val
+ }
+ const [srcDir, rootDir] = await Promise.all([get('srcDir'), get('rootDir')]) as [string, string]
+ if (srcDir === rootDir) {
+ return 'chokidar-granular'
+ }
+ return 'chokidar'
+ },
+ },
/**
* Enable native async context to be accessible for nested composables
@@ -436,23 +356,7 @@ export default defineUntypedSchema({
* Options that apply to `useAsyncData` (and also therefore `useFetch`)
*/
useAsyncData: {
- /** @type {'undefined' | 'null'} */
- value: {
- async $resolve (val, get) {
- return val ?? ((await get('future') as Record).compatibilityVersion === 4 ? 'undefined' : 'null')
- },
- },
- /** @type {'undefined' | 'null'} */
- errorValue: {
- async $resolve (val, get) {
- return val ?? ((await get('future') as Record).compatibilityVersion === 4 ? 'undefined' : 'null')
- },
- },
- deep: {
- async $resolve (val, get) {
- return val ?? !((await get('future') as Record).compatibilityVersion === 4)
- },
- },
+ deep: false,
},
/** @type {Pick} */
useFetch: {},
@@ -474,50 +378,11 @@ export default defineUntypedSchema({
clientNodeCompat: false,
/**
- * Whether to use `lodash.template` to compile Nuxt templates.
+ * Wait for a single animation frame before navigation, which gives an opportunity
+ * for the browser to repaint, acknowledging user interaction.
*
- * This flag will be removed with the release of v4 and exists only for
- * advance testing within Nuxt v3.12+ or in [the nightly release channel](/docs/guide/going-further/nightly-release-channel).
+ * It can reduce INP when navigating on prerendered routes.
*/
- compileTemplate: {
- async $resolve (val, get) {
- return val ?? ((await get('future') as Record).compatibilityVersion !== 4)
- },
- },
-
- /**
- * Whether to provide a legacy `templateUtils` object (with `serialize`,
- * `importName` and `importSources`) when compiling Nuxt templates.
- *
- * This flag will be removed with the release of v4 and exists only for
- * advance testing within Nuxt v3.12+ or in [the nightly release channel](/docs/guide/going-further/nightly-release-channel).
- */
- templateUtils: {
- async $resolve (val, get) {
- return val ?? ((await get('future') as Record).compatibilityVersion !== 4)
- },
- },
-
- /**
- * Whether to provide relative paths in the `builder:watch` hook.
- *
- * This flag will be removed with the release of v4 and exists only for
- * advance testing within Nuxt v3.12+ or in [the nightly release channel](/docs/guide/going-further/nightly-release-channel).
- */
- relativeWatchPaths: {
- async $resolve (val, get) {
- return val ?? ((await get('future') as Record).compatibilityVersion !== 4)
- },
- },
-
- /**
- * Whether `clear` and `clearNuxtData` should reset async data to its _default_ value or update
- * it to `null`/`undefined`.
- */
- resetAsyncDataToUndefined: {
- async $resolve (val, get) {
- return val ?? ((await get('future') as Record).compatibilityVersion !== 4)
- },
- },
+ navigationRepaint: true,
},
})
diff --git a/packages/schema/src/config/internal.ts b/packages/schema/src/config/internal.ts
index 346beff214..77c79bd5bc 100644
--- a/packages/schema/src/config/internal.ts
+++ b/packages/schema/src/config/internal.ts
@@ -2,7 +2,7 @@ import { defineUntypedSchema } from 'untyped'
export default defineUntypedSchema({
/** @private */
- _majorVersion: 3,
+ _majorVersion: 4,
/** @private */
_legacyGenerate: false,
/** @private */
diff --git a/packages/schema/src/config/nitro.ts b/packages/schema/src/config/nitro.ts
index f937bb2f20..855fec69a4 100644
--- a/packages/schema/src/config/nitro.ts
+++ b/packages/schema/src/config/nitro.ts
@@ -5,7 +5,7 @@ export default defineUntypedSchema({
/**
* Configuration for Nitro.
* @see https://nitro.unjs.io/config/
- * @type {typeof import('nitropack')['NitroConfig']}
+ * @type {typeof import('nitro/types')['NitroConfig']}
*/
nitro: {
runtimeConfig: {
@@ -38,7 +38,7 @@ export default defineUntypedSchema({
* Global route options applied to matching server routes.
* @experimental This is an experimental feature and API may change in the future.
* @see https://nitro.unjs.io/config/#routerules
- * @type {typeof import('nitropack')['NitroConfig']['routeRules']}
+ * @type {typeof import('nitro/types')['NitroConfig']['routeRules']}
*/
routeRules: {},
@@ -61,14 +61,14 @@ export default defineUntypedSchema({
* { route: '/path/foo/**:name', handler: '~/server/foohandler.ts' }
* ]
* ```
- * @type {typeof import('nitropack')['NitroEventHandler'][]}
+ * @type {typeof import('nitro/types')['NitroEventHandler'][]}
*/
serverHandlers: [],
/**
* Nitro development-only server handlers.
* @see https://nitro.unjs.io/guide/routing
- * @type {typeof import('nitropack')['NitroDevEventHandler'][]}
+ * @type {typeof import('nitro/types')['NitroDevEventHandler'][]}
*/
devServerHandlers: [],
})
diff --git a/packages/schema/src/config/postcss.ts b/packages/schema/src/config/postcss.ts
index 83694cfd18..56f02c1227 100644
--- a/packages/schema/src/config/postcss.ts
+++ b/packages/schema/src/config/postcss.ts
@@ -1,12 +1,45 @@
import { defineUntypedSchema } from 'untyped'
+const ensureItemIsLast = (item: string) => (arr: string[]) => {
+ const index = arr.indexOf(item)
+ if (index !== -1) {
+ arr.splice(index, 1)
+ arr.push(item)
+ }
+ return arr
+}
+
+const orderPresets = {
+ cssnanoLast: ensureItemIsLast('cssnano'),
+ autoprefixerLast: ensureItemIsLast('autoprefixer'),
+ autoprefixerAndCssnanoLast (names: string[]) {
+ return orderPresets.cssnanoLast(orderPresets.autoprefixerLast(names))
+ },
+}
+
export default defineUntypedSchema({
postcss: {
+ /**
+ * A strategy for ordering PostCSS plugins.
+ *
+ * @type {'cssnanoLast' | 'autoprefixerLast' | 'autoprefixerAndCssnanoLast' | string[] | ((names: string[]) => string[])}
+ */
+ order: {
+ $resolve: (val: string | string[] | ((plugins: string[]) => string[])): string[] | ((plugins: string[]) => string[]) => {
+ if (typeof val === 'string') {
+ if (!(val in orderPresets)) {
+ throw new Error(`[nuxt] Unknown PostCSS order preset: ${val}`)
+ }
+ return orderPresets[val as keyof typeof orderPresets]
+ }
+ return val ?? orderPresets.autoprefixerAndCssnanoLast
+ },
+ },
/**
* Options for configuring PostCSS plugins.
*
* https://postcss.org/
- * @type {Record & { autoprefixer?: any; cssnano?: any }}
+ * @type {Record | false> & { autoprefixer?: typeof import('autoprefixer').Options; cssnano?: typeof import('cssnano').Options }}
*/
plugins: {
/**
diff --git a/packages/schema/src/config/typescript.ts b/packages/schema/src/config/typescript.ts
index 7dd1c26368..eb6ff76108 100644
--- a/packages/schema/src/config/typescript.ts
+++ b/packages/schema/src/config/typescript.ts
@@ -35,7 +35,7 @@ export default defineUntypedSchema({
$resolve: (val) => {
const defaults = [
// Nitro auto-imported/augmented dependencies
- 'nitropack',
+ 'nitro/types',
'defu',
'h3',
'consola',
@@ -48,6 +48,8 @@ export default defineUntypedSchema({
'@vue/compiler-sfc',
'@vue/runtime-dom',
'vue-router',
+ 'vue-router/auto-routes',
+ 'unplugin-vue-router/client',
'@nuxt/schema',
'nuxt',
]
diff --git a/packages/schema/src/index.ts b/packages/schema/src/index.ts
index eebdb6e56b..0eb5c1588f 100644
--- a/packages/schema/src/index.ts
+++ b/packages/schema/src/index.ts
@@ -1,13 +1,13 @@
// Types
-export * from './types/compatibility'
-export * from './types/components'
-export * from './types/config'
-export * from './types/hooks'
-export * from './types/imports'
-export * from './types/head'
-export * from './types/module'
-export * from './types/nuxt'
-export * from './types/router'
+export type { NuxtCompatibility, NuxtCompatibilityIssue, NuxtCompatibilityIssues } from './types/compatibility'
+export type { Component, ComponentMeta, ComponentsDir, ComponentsOptions, ScanDir } from './types/components'
+export type { AppConfig, AppConfigInput, CustomAppConfig, NuxtAppConfig, NuxtBuilder, NuxtConfig, NuxtConfigLayer, NuxtOptions, PublicRuntimeConfig, RuntimeConfig, RuntimeValue, SchemaDefinition, UpperSnakeCase, ViteConfig } from './types/config'
+export type { GenerateAppOptions, HookResult, ImportPresetWithDeprecation, NuxtAnalyzeMeta, NuxtHookName, NuxtHooks, NuxtLayout, NuxtMiddleware, NuxtPage, TSReference, VueTSConfig, WatchEvent } from './types/hooks'
+export type { ImportsOptions } from './types/imports'
+export type { AppHeadMetaObject, MetaObject, MetaObjectRaw, HeadAugmentations } from './types/head'
+export type { ModuleDefinition, ModuleMeta, ModuleOptions, ModuleSetupInstallResult, ModuleSetupReturn, NuxtModule, ResolvedModuleOptions } from './types/module'
+export type { Nuxt, NuxtApp, NuxtPlugin, NuxtPluginTemplate, NuxtTemplate, NuxtTypeTemplate, ResolvedNuxtTemplate } from './types/nuxt'
+export type { RouterConfig, RouterConfigSerializable, RouterOptions } from './types/router'
// Schema
export { default as NuxtConfigSchema } from './config/index'
diff --git a/packages/schema/src/types/config.ts b/packages/schema/src/types/config.ts
index 44a1390734..c89e261fe1 100644
--- a/packages/schema/src/types/config.ts
+++ b/packages/schema/src/types/config.ts
@@ -3,7 +3,7 @@ import type { ServerOptions as ViteServerOptions, UserConfig as ViteUserConfig }
import type { Options as VuePluginOptions } from '@vitejs/plugin-vue'
import type { Options as VueJsxPluginOptions } from '@vitejs/plugin-vue-jsx'
import type { SchemaDefinition } from 'untyped'
-import type { NitroRuntimeConfig, NitroRuntimeConfigApp } from 'nitropack'
+import type { NitroRuntimeConfig, NitroRuntimeConfigApp } from 'nitro/types'
import type { SnakeCase } from 'scule'
import type { ConfigSchema } from '../../schema/config'
import type { Nuxt } from './nuxt'
@@ -75,9 +75,10 @@ export interface NuxtBuilder {
}
// Normalized Nuxt options available as `nuxt.options.*`
-export interface NuxtOptions extends Omit {
+export interface NuxtOptions extends Omit {
sourcemap: Required>
builder: '@nuxt/vite-builder' | '@nuxt/webpack-builder' | NuxtBuilder
+ postcss: Omit & { order: Exclude }
webpack: ConfigSchema['webpack'] & {
$client: ConfigSchema['webpack']
$server: ConfigSchema['webpack']
@@ -101,12 +102,6 @@ export interface ViteConfig extends Omit {
*/
vueJsx?: VueJsxPluginOptions
- /**
- * Bundler for dev time server-side rendering.
- * @default 'vite-node'
- */
- devBundler?: 'vite-node' | 'legacy'
-
/**
* Warmup vite entrypoint caches on dev startup.
*/
diff --git a/packages/schema/src/types/hooks.ts b/packages/schema/src/types/hooks.ts
index 1c4fde5fad..34b5528960 100644
--- a/packages/schema/src/types/hooks.ts
+++ b/packages/schema/src/types/hooks.ts
@@ -6,7 +6,7 @@ import type { Manifest } from 'vue-bundle-renderer'
import type { EventHandler } from 'h3'
import type { Import, InlinePreset, Unimport } from 'unimport'
import type { Compiler, Configuration, Stats } from 'webpack'
-import type { Nitro, NitroConfig } from 'nitropack'
+import type { Nitro, NitroConfig } from 'nitro/types'
import type { Schema, SchemaDefinition } from 'untyped'
import type { RouteLocationRaw } from 'vue-router'
import type { VueCompilerOptions } from '@vue/language-core'
diff --git a/packages/schema/src/types/module.ts b/packages/schema/src/types/module.ts
index 9b92d6a93e..1879678923 100644
--- a/packages/schema/src/types/module.ts
+++ b/packages/schema/src/types/module.ts
@@ -1,3 +1,4 @@
+import type { Defu } from 'defu'
import type { NuxtHooks } from './hooks'
import type { Nuxt } from './nuxt'
import type { NuxtCompatibility } from './compatibility'
@@ -26,8 +27,7 @@ export interface ModuleMeta {
/** The options received. */
export type ModuleOptions = Record
-/** Optional result for nuxt modules */
-export interface ModuleSetupReturn {
+export type ModuleSetupInstallResult = {
/**
* Timing information for the initial setup
*/
@@ -39,19 +39,62 @@ export interface ModuleSetupReturn {
}
type Awaitable = T | Promise
-type _ModuleSetupReturn = Awaitable
-/** Input module passed to defineNuxtModule. */
-export interface ModuleDefinition {
+type Prettify = {
+ [K in keyof T]: T[K];
+} & {}
+
+export type ModuleSetupReturn = Awaitable
+
+export type ResolvedModuleOptions<
+ TOptions extends ModuleOptions,
+ TOptionsDefaults extends Partial,
+> =
+ Prettify<
+ Defu<
+ Partial,
+ [Partial, TOptionsDefaults]
+ >
+ >
+
+/** Module definition passed to 'defineNuxtModule(...)' or 'defineNuxtModule().with(...)'. */
+export interface ModuleDefinition<
+ TOptions extends ModuleOptions,
+ TOptionsDefaults extends Partial,
+ TWith extends boolean,
+> {
meta?: ModuleMeta
- defaults?: T | ((nuxt: Nuxt) => T)
- schema?: T
+ defaults?: TOptionsDefaults | ((nuxt: Nuxt) => TOptionsDefaults)
+ schema?: TOptions
hooks?: Partial
- setup?: (this: void, resolvedOptions: T, nuxt: Nuxt) => _ModuleSetupReturn
+ setup?: (
+ this: void,
+ resolvedOptions: TWith extends true
+ ? ResolvedModuleOptions
+ : TOptions,
+ nuxt: Nuxt
+ ) => ModuleSetupReturn
}
-export interface NuxtModule {
- (this: void, inlineOptions: T, nuxt: Nuxt): _ModuleSetupReturn
- getOptions?: (inlineOptions?: T, nuxt?: Nuxt) => Promise
+export interface NuxtModule<
+ TOptions extends ModuleOptions = ModuleOptions,
+ TOptionsDefaults extends Partial = Partial,
+ TWith extends boolean = false,
+> {
+ (
+ this: void,
+ resolvedOptions: TWith extends true
+ ? ResolvedModuleOptions
+ : TOptions,
+ nuxt: Nuxt
+ ): ModuleSetupReturn
+ getOptions?: (
+ inlineOptions?: Partial,
+ nuxt?: Nuxt
+ ) => Promise<
+ TWith extends true
+ ? ResolvedModuleOptions
+ : TOptions
+ >
getMeta?: () => Promise
}
diff --git a/packages/schema/src/types/router.ts b/packages/schema/src/types/router.ts
index 76abc6acb8..c7cb0ce850 100644
--- a/packages/schema/src/types/router.ts
+++ b/packages/schema/src/types/router.ts
@@ -2,7 +2,7 @@ import type { RouterHistory, RouterOptions as _RouterOptions } from 'vue-router'
export type RouterOptions = Partial> & {
history?: (baseURL?: string) => RouterHistory
- routes?: (_routes: _RouterOptions['routes']) => _RouterOptions['routes']
+ routes?: (_routes: _RouterOptions['routes']) => _RouterOptions['routes'] | Promise<_RouterOptions['routes']>
hashMode?: boolean
scrollBehaviorType?: 'smooth' | 'auto'
}
diff --git a/packages/schema/test/folder-structure.spec.ts b/packages/schema/test/folder-structure.spec.ts
index 1f59c8f908..d9cb857f2c 100644
--- a/packages/schema/test/folder-structure.spec.ts
+++ b/packages/schema/test/folder-structure.spec.ts
@@ -11,13 +11,13 @@ describe('nuxt folder structure', () => {
expect(getDirs(result as unknown as NuxtOptions)).toMatchInlineSnapshot(`
{
"dir": {
- "app": "app",
- "modules": "modules",
- "public": "public",
+ "app": "/app",
+ "modules": "/modules",
+ "public": "/public",
},
"rootDir": "",
"serverDir": "/server",
- "srcDir": "",
+ "srcDir": "/app",
"workspaceDir": "",
}
`)
@@ -28,12 +28,12 @@ describe('nuxt folder structure', () => {
expect(getDirs(result as unknown as NuxtOptions)).toMatchInlineSnapshot(`
{
"dir": {
- "app": "app",
- "modules": "modules",
- "public": "public",
+ "app": "/test/src",
+ "modules": "/test/modules",
+ "public": "/test/public",
},
"rootDir": "/test",
- "serverDir": "/test/src/server",
+ "serverDir": "/test/server",
"srcDir": "/test/src",
"workspaceDir": "/test",
}
diff --git a/packages/ui-templates/lib/dev.ts b/packages/ui-templates/lib/dev.ts
index 75e431f66c..8c2624dbfe 100644
--- a/packages/ui-templates/lib/dev.ts
+++ b/packages/ui-templates/lib/dev.ts
@@ -1,9 +1,10 @@
+import { runInNewContext } from 'node:vm'
import { join, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { promises as fsp } from 'node:fs'
import type { Plugin } from 'vite'
-import { template } from 'lodash-es'
import genericMessages from '../templates/messages.json'
+import { version } from '../../nuxt/package.json'
const templatesRoot = fileURLToPath(new URL('..', import.meta.url))
@@ -15,6 +16,8 @@ export const DevRenderingPlugin = () => {
async transformIndexHtml (html: string, context) {
const page = context.originalUrl || '/'
+ if (page.endsWith('.png')) { return }
+
if (page === '/') {
const templateNames = await fsp.readdir(r('templates'))
const serializedData = JSON.stringify({ templateNames })
@@ -25,11 +28,20 @@ export const DevRenderingPlugin = () => {
const messages = JSON.parse(await fsp.readFile(r(page, 'messages.json'), 'utf-8'))
- return template(contents, {
- interpolate: /\{\{\{?([\s\S]+?)\}?\}\}/g,
- })({
- messages: { ...genericMessages, ...messages },
- })
+ const chunks = contents.split(/\{{2,3}[^{}]+\}{2,3}/g)
+ let templateString = chunks.shift()
+ for (const expression of contents.matchAll(/\{{2,3}([^{}]+)\}{2,3}/g)) {
+ const value = runInNewContext(expression[1].trim(), {
+ version,
+ messages: { ...genericMessages, ...messages },
+ })
+ templateString += `${value}${chunks.shift()}`
+ }
+ if (chunks.length > 0) {
+ templateString += chunks.join('')
+ }
+
+ return templateString
},
}
}
diff --git a/packages/ui-templates/lib/render.ts b/packages/ui-templates/lib/render.ts
index c9d5986c4f..ea444ed059 100644
--- a/packages/ui-templates/lib/render.ts
+++ b/packages/ui-templates/lib/render.ts
@@ -10,6 +10,7 @@ import htmlMinifier from 'html-minifier'
import { globby } from 'globby'
import { camelCase } from 'scule'
+import { version } from '../../nuxt/package.json'
import genericMessages from '../templates/messages.json'
const r = (path: string) => fileURLToPath(new URL(join('..', path), import.meta.url))
@@ -89,6 +90,8 @@ export const RenderPlugin = () => {
html = html.replace('