Compare commits

...

9 Commits

17 changed files with 749 additions and 718 deletions

View File

@ -166,6 +166,10 @@ export default defineNuxtConfig({
})
```
::note
Any nested directories need to be added first as they are scanned in order.
::
## npm Packages
If you want to auto-import components from an npm package, you can use [`addComponent`](/docs/api/kit/components#addcomponent) in a [local module](/docs/guide/directory-structure/modules) to register them.
@ -198,10 +202,6 @@ export default defineNuxtModule({
::
::note
Any nested directories need to be added first as they are scanned in order.
::
## Component Extensions
By default, any file with an extension specified in the [extensions key of `nuxt.config.ts`](/docs/api/nuxt-config#extensions) is treated as a component.

View File

@ -347,6 +347,22 @@ export default defineEventHandler((event) => {
})
```
### Forwarding Context & Headers
By default, neither the headers from the incoming request nor the request context are forwarded when
making fetch requests in server routes. You can use `event.$fetch` to forward the request context and headers when making fetch requests in server routes.
```ts [server/api/forward.ts]
export default defineEventHandler((event) => {
return event.$fetch('/api/forwarded')
})
```
::note
Headers that are **not meant to be forwarded** will **not be included** in the request. These headers include, for example:
`transfer-encoding`, `connection`, `keep-alive`, `upgrade`, `expect`, `host`, `accept`
::
## Advanced Usage
### Nitro Config

View File

@ -0,0 +1,52 @@
---
title: 'useRequestFetch'
description: 'Forward the request context and headers for server-side fetch requests with the useRequestFetch composable.'
links:
- label: Source
icon: i-simple-icons-github
to: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/ssr.ts
size: xs
---
You can use `useRequestFetch` to forward the request context and headers when making server-side fetch requests.
When making a client-side fetch request, the browser automatically sends the necessary headers.
However, when making a request during server-side rendering, because the request is made on the server, we need to forward the headers manually.
::note
Headers that are **not meant to be forwarded** will **not be included** in the request. These headers include, for example:
`transfer-encoding`, `connection`, `keep-alive`, `upgrade`, `expect`, `host`, `accept`
::
::tip
The [`useFetch`](/docs/api/composables/use-fetch) composable uses `useRequestFetch` under the hood to automatically forward the request context and headers.
::
::code-group
```vue [pages/index.vue]
<script setup lang="ts">
// This will forward the user's headers to the `/api/foo` event handler
// Result: { cookies: { foo: 'bar' } }
const requestFetch = useRequestFetch()
const { data: forwarded } = await useAsyncData(() => requestFetch('/api/cookies'))
// This will NOT forward anything
// Result: { cookies: {} }
const { data: notForwarded } = await useAsyncData(() => $fetch('/api/cookies'))
</script>
```
```ts [server/api/cookies.ts]
export default defineEventHandler((event) => {
const cookies = parseCookies(event)
return { cookies }
})
```
::
::tip
In the browser during client-side navigation, `useRequestFetch` will behave just like regular [`$fetch`](/docs/api/utils/dollarfetch).
::

View File

@ -39,9 +39,9 @@
"@nuxt/ui-templates": "workspace:*",
"@nuxt/vite-builder": "workspace:*",
"@nuxt/webpack-builder": "workspace:*",
"@vue/compiler-core": "3.5.6",
"@vue/compiler-dom": "3.5.6",
"@vue/shared": "3.5.6",
"@vue/compiler-core": "3.5.7",
"@vue/compiler-dom": "3.5.7",
"@vue/shared": "3.5.7",
"@types/node": "20.16.5",
"c12": "2.0.0-beta.2",
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
@ -51,16 +51,16 @@
"nuxt": "workspace:*",
"ohash": "1.1.4",
"postcss": "8.4.47",
"rollup": "4.22.2",
"rollup": "4.22.4",
"send": ">=0.19.0",
"typescript": "5.6.2",
"ufo": "1.5.4",
"unbuild": "3.0.0-rc.7",
"vite": "5.4.6",
"vue": "3.5.6"
"vite": "5.4.7",
"vue": "3.5.7"
},
"devDependencies": {
"@eslint/js": "9.10.0",
"@eslint/js": "9.11.0",
"@nuxt/eslint-config": "0.5.7",
"@nuxt/kit": "workspace:*",
"@nuxt/test-utils": "3.14.2",
@ -81,7 +81,7 @@
"cssnano": "7.0.6",
"destr": "2.0.3",
"devalue": "5.0.0",
"eslint": "9.10.0",
"eslint": "9.11.0",
"eslint-plugin-no-only-tests": "3.3.0",
"eslint-plugin-perfectionist": "3.6.0",
"eslint-typegen": "0.3.2",
@ -93,9 +93,9 @@
"nuxi": "3.13.2",
"nuxt": "workspace:*",
"nuxt-content-twoslash": "0.1.1",
"ofetch": "1.3.4",
"ofetch": "1.4.0",
"pathe": "1.1.2",
"playwright-core": "1.47.1",
"playwright-core": "1.47.2",
"rimraf": "6.0.1",
"semver": "7.6.3",
"sherif": "1.0.0",
@ -106,18 +106,13 @@
"ufo": "1.5.4",
"vitest": "2.1.1",
"vitest-environment-nuxt": "1.0.1",
"vue": "3.5.6",
"vue": "3.5.7",
"vue-router": "4.4.5",
"vue-tsc": "2.1.6"
},
"packageManager": "pnpm@9.10.0",
"packageManager": "pnpm@9.11.0",
"engines": {
"node": "^16.10.0 || >=18.0.0"
},
"version": "",
"pnpm": {
"patchedDependencies": {
"ofetch@1.3.4": "patches/ofetch@1.3.4.patch"
}
}
"version": ""
}

View File

@ -1,6 +1,6 @@
{
"name": "@nuxt/kit",
"version": "3.12.2",
"version": "4.0.0",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/nuxt.git",
@ -52,7 +52,7 @@
"@types/semver": "7.5.8",
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
"unbuild": "3.0.0-rc.7",
"vite": "5.4.6",
"vite": "5.4.7",
"vitest": "2.1.1",
"webpack": "5.94.0"
},

View File

@ -79,11 +79,12 @@ export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, n
// Import if input is string
if (typeof nuxtModule === 'string') {
const paths = [join(nuxtModule, 'nuxt'), join(nuxtModule, 'module'), nuxtModule]
let error: unknown
const paths = [join(nuxtModule, 'nuxt'), join(nuxtModule, 'module'), nuxtModule, join(nuxt.options.rootDir, nuxtModule)]
for (const parentURL of nuxt.options.modulesDir) {
for (const path of paths) {
try {
const src = jiti.esmResolve(path)
const src = jiti.esmResolve(path, { parentURL })
nuxtModule = await jiti.import(src) as NuxtModule
// nuxt-module-builder generates a module.json with metadata including the version
@ -92,16 +93,18 @@ export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, n
buildTimeModuleMeta = JSON.parse(await fsp.readFile(moduleMetadataPath, 'utf-8'))
}
break
} catch (_err: unknown) {
error = _err
} catch (error: unknown) {
const code = (error as Error & { code?: string }).code
if (code === 'MODULE_NOT_FOUND' || code === 'ERR_PACKAGE_PATH_NOT_EXPORTED' || code === 'ERR_MODULE_NOT_FOUND' || code === 'ERR_UNSUPPORTED_DIR_IMPORT') {
continue
}
}
if (typeof nuxtModule !== 'function' && error) {
logger.error(`Error while importing module \`${nuxtModule}\`: ${error}`)
throw error
}
}
if (typeof nuxtModule !== 'string') { break }
}
}
// Throw error if input is not a function
if (typeof nuxtModule !== 'function') {

View File

@ -1,6 +1,6 @@
{
"name": "nuxt",
"version": "3.12.2",
"version": "4.0.0",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/nuxt.git",
@ -60,7 +60,7 @@
},
"dependencies": {
"@nuxt/devalue": "^2.0.2",
"@nuxt/devtools": "^1.4.2",
"@nuxt/devtools": "^1.5.0",
"@nuxt/kit": "workspace:*",
"@nuxt/schema": "workspace:*",
"@nuxt/telemetry": "^2.6.0",
@ -69,7 +69,7 @@
"@unhead/shared": "^1.11.6",
"@unhead/ssr": "^1.11.6",
"@unhead/vue": "^1.11.6",
"@vue/shared": "^3.5.6",
"@vue/shared": "^3.5.7",
"acorn": "8.12.1",
"c12": "^2.0.0-beta.2",
"chokidar": "^3.6.0",
@ -97,7 +97,7 @@
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
"nuxi": "^3.13.2",
"nypm": "^0.3.11",
"ofetch": "^1.3.4",
"ofetch": "^1.4.0",
"ohash": "^1.1.4",
"pathe": "^1.1.2",
"perfect-debounce": "^1.0.0",
@ -119,7 +119,7 @@
"unplugin-vue-router": "^0.10.8",
"unstorage": "^1.12.0",
"untyped": "^1.4.2",
"vue": "^3.5.6",
"vue": "^3.5.7",
"vue-bundle-renderer": "^2.1.0",
"vue-devtools-stub": "^0.1.0",
"vue-router": "^4.4.5"
@ -130,9 +130,9 @@
"@parcel/watcher": "2.4.1",
"@types/estree": "1.0.6",
"@vitejs/plugin-vue": "5.1.4",
"@vue/compiler-sfc": "3.5.6",
"@vue/compiler-sfc": "3.5.7",
"unbuild": "3.0.0-rc.7",
"vite": "5.4.6",
"vite": "5.4.7",
"vitest": "2.1.1"
},
"peerDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@nuxt/schema",
"version": "3.12.2",
"version": "4.0.0",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/nuxt.git",
@ -42,20 +42,20 @@
"@unhead/schema": "1.11.6",
"@vitejs/plugin-vue": "5.1.4",
"@vitejs/plugin-vue-jsx": "4.0.1",
"@vue/compiler-core": "3.5.6",
"@vue/compiler-sfc": "3.5.6",
"@vue/compiler-core": "3.5.7",
"@vue/compiler-sfc": "3.5.7",
"@vue/language-core": "2.1.6",
"c12": "2.0.0-beta.2",
"esbuild-loader": "4.2.2",
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
"ignore": "6.0.2",
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
"ofetch": "1.3.4",
"ofetch": "1.4.0",
"unbuild": "3.0.0-rc.7",
"unctx": "2.3.1",
"unenv": "1.10.0",
"vite": "5.4.6",
"vue": "3.5.6",
"vite": "5.4.7",
"vue": "3.5.7",
"vue-bundle-renderer": "2.1.0",
"vue-loader": "17.4.2",
"vue-router": "4.4.5",

View File

@ -30,6 +30,6 @@
"tinyexec": "0.3.0",
"tinyglobby": "0.2.6",
"unocss": "0.62.4",
"vite": "5.4.6"
"vite": "5.4.7"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@nuxt/vite-builder",
"version": "3.12.2",
"version": "4.0.0",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/nuxt.git",
@ -27,9 +27,9 @@
"@nuxt/schema": "workspace:*",
"@types/clear": "0.1.4",
"@types/estree": "1.0.6",
"rollup": "4.22.2",
"rollup": "4.22.4",
"unbuild": "3.0.0-rc.7",
"vue": "3.5.6"
"vue": "3.5.7"
},
"dependencies": {
"@nuxt/kit": "workspace:*",
@ -62,7 +62,7 @@
"ufo": "^1.5.4",
"unenv": "^1.10.0",
"unplugin": "^1.14.1",
"vite": "^5.4.6",
"vite": "^5.4.7",
"vite-node": "^2.1.1",
"vite-plugin-checker": "^0.8.0",
"vue-bundle-renderer": "^2.1.0"

View File

@ -27,11 +27,16 @@ export async function resolveCSSOptions (nuxt: Nuxt): Promise<ViteConfig['css']>
const pluginOptions = postcssOptions.plugins[pluginName]
if (!pluginOptions) { continue }
const path = jiti.esmResolve(pluginName)
const pluginFn = (await jiti.import(path)) as (opts: Record<string, any>) => Plugin
let pluginFn: ((opts: Record<string, any>) => Plugin) | undefined
for (const parentURL of nuxt.options.modulesDir) {
pluginFn = await jiti.import(pluginName, { parentURL, try: true }) as (opts: Record<string, any>) => Plugin
if (typeof pluginFn === 'function') {
css.postcss.plugins.push(pluginFn(pluginOptions))
} else {
break
}
}
if (typeof pluginFn !== 'function') {
console.warn(`[nuxt] could not import postcss plugin \`${pluginName}\`. Please report this as a bug.`)
}
}

View File

@ -7,7 +7,6 @@ export const viteNodeOptions = JSON.parse(process.env.NUXT_VITE_NODE_OPTIONS ||
export const viteNodeFetch = $fetch.create({
baseURL: viteNodeOptions.baseURL,
// @ts-expect-error https://github.com/node-fetch/node-fetch#custom-agent
agent: viteNodeOptions.baseURL.startsWith('https://')
? new HTTPSAgent({ rejectUnauthorized: false })
: null,

View File

@ -1,6 +1,6 @@
{
"name": "@nuxt/webpack-builder",
"version": "3.12.2",
"version": "4.0.0",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/nuxt.git",
@ -78,9 +78,9 @@
"@types/pify": "5.0.4",
"@types/webpack-bundle-analyzer": "4.7.0",
"@types/webpack-hot-middleware": "2.25.9",
"rollup": "4.22.2",
"rollup": "4.22.4",
"unbuild": "3.0.0-rc.7",
"vue": "3.5.6"
"vue": "3.5.7"
},
"peerDependencies": {
"vue": "^3.3.4"

View File

@ -49,11 +49,16 @@ export async function getPostcssConfig (nuxt: Nuxt) {
const pluginOptions = postcssOptions.plugins[pluginName]
if (!pluginOptions) { continue }
const path = jiti.esmResolve(pluginName)
const pluginFn = (await jiti.import(path)) as (opts: Record<string, any>) => Plugin
let pluginFn: ((opts: Record<string, any>) => Plugin) | undefined
for (const parentURL of nuxt.options.modulesDir) {
pluginFn = await jiti.import(pluginName, { parentURL, try: true }) as (opts: Record<string, any>) => Plugin
if (typeof pluginFn === 'function') {
plugins.push(pluginFn(pluginOptions))
} else {
break
}
}
if (typeof pluginFn !== 'function') {
console.warn(`[nuxt] could not import postcss plugin \`${pluginName}\`. Please report this as a bug.`)
}
}

View File

@ -1,33 +0,0 @@
diff --git a/dist/node.d.cts b/dist/node.d.cts
index d3a39ff53717d267ff4581af714533ff7229799c..4e3db1f3d6defb7b0c40d11589c0ff6cb8391ad5 100644
--- a/dist/node.d.cts
+++ b/dist/node.d.cts
@@ -1,5 +1,5 @@
import { $ as $Fetch } from './shared/ofetch.8459ad38.cjs';
-export { F as FetchError, c as createFetch, a as createFetchError } from './shared/ofetch.8459ad38.cjs';
+export { C as CreateFetchOptions, g as Fetch, b as FetchContext, F as FetchError, d as FetchOptions, h as FetchRequest, f as FetchResponse, G as GlobalOptions, I as IFetchError, M as MappedResponseType, R as ResponseMap, e as ResponseType, S as SearchParameters, c as createFetch, a as createFetchError } from './shared/ofetch.8459ad38.mjs';
declare function createNodeFetch(): (input: RequestInfo, init?: RequestInit) => any;
declare const fetch: typeof globalThis.fetch;
diff --git a/dist/node.d.mts b/dist/node.d.mts
index 3d8b330375ce60178c05292179ec8bac764ae516..bdcc322bd8554fc7e61d5d9760cb9991560560eb 100644
--- a/dist/node.d.mts
+++ b/dist/node.d.mts
@@ -1,5 +1,5 @@
import { $ as $Fetch } from './shared/ofetch.8459ad38.mjs';
-export { F as FetchError, c as createFetch, a as createFetchError } from './shared/ofetch.8459ad38.mjs';
+export { C as CreateFetchOptions, g as Fetch, b as FetchContext, F as FetchError, d as FetchOptions, h as FetchRequest, f as FetchResponse, G as GlobalOptions, I as IFetchError, M as MappedResponseType, R as ResponseMap, e as ResponseType, S as SearchParameters, c as createFetch, a as createFetchError } from './shared/ofetch.8459ad38.mjs';
declare function createNodeFetch(): (input: RequestInfo, init?: RequestInit) => any;
declare const fetch: typeof globalThis.fetch;
diff --git a/dist/node.d.ts b/dist/node.d.ts
index 6a5419d1939000a15958b362f44bf49fb1800207..4b319d2c3051e966274268670e243c5f99e2904d 100644
--- a/dist/node.d.ts
+++ b/dist/node.d.ts
@@ -1,5 +1,5 @@
import { $ as $Fetch } from './shared/ofetch.8459ad38.js';
-export { F as FetchError, c as createFetch, a as createFetchError } from './shared/ofetch.8459ad38.js';
+export { C as CreateFetchOptions, g as Fetch, b as FetchContext, F as FetchError, d as FetchOptions, h as FetchRequest, f as FetchResponse, G as GlobalOptions, I as IFetchError, M as MappedResponseType, R as ResponseMap, e as ResponseType, S as SearchParameters, c as createFetch, a as createFetchError } from './shared/ofetch.8459ad38.mjs';
declare function createNodeFetch(): (input: RequestInfo, init?: RequestInit) => any;
declare const fetch: typeof globalThis.fetch;

File diff suppressed because it is too large Load Diff

View File

@ -37,10 +37,10 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
const serverDir = join(rootDir, '.output/server')
const serverStats = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"207k"`)
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"208k"`)
const modules = await analyzeSizes(['node_modules/**/*'], serverDir)
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"1387k"`)
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"1388k"`)
const packages = modules.files
.filter(m => m.endsWith('package.json'))
@ -78,7 +78,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
const serverDir = join(rootDir, '.output-inline/server')
const serverStats = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"555k"`)
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"557k"`)
const modules = await analyzeSizes(['node_modules/**/*'], serverDir)
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"88.2k"`)