Compare commits

...

16 Commits

Author SHA1 Message Date
Nishant Aanjaney Jalan b239a8e7dd
Merge cc2846c1a8 into 73accb5ac1 2024-09-21 16:21:33 +03:00
Daniel Roe 73accb5ac1
fix(kit,vite,webpack): resolve postcss paths from each modules dir (#29096) 2024-09-21 13:37:49 +01:00
Daniel Roe 097105fb2b
fix(kit): try resolving module path from each node_modules dir 2024-09-21 13:37:48 +01:00
Daniel Roe 33e2d0a5f3
fix(kit): handle passing 'bare' relative paths to modules 2024-09-21 13:37:48 +01:00
renovate[bot] 01e00963bc
chore(deps): update all non-major dependencies (main) (#29101) 2024-09-21 12:10:59 +01:00
Matej Černý d970da6da5
docs: add a section about `useRequestFetch` and `event.$fetch` (#29099) 2024-09-21 11:39:54 +01:00
renovate[bot] 61b5ef7850
chore(deps): update all non-major dependencies (main) (#29093) 2024-09-21 09:32:18 +01:00
Matej Černý 00ad2b8233
docs: move custom directories note to the correct place (#29100) 2024-09-21 09:32:00 +01:00
Nishant Aanjaney Jalan cc2846c1a8
docs: revisited updates to data-fetching
Also changed the order of headings to match nutshell pointers
2024-09-17 11:41:41 +05:30
Nishant Aanjaney Jalan a672e38399
Merge branch 'main' into patch-3 2024-09-16 16:57:17 +05:30
Nishant Aanjaney Jalan 5236e79a3b
docs: proofread changes and made minor fixes 2024-09-15 22:47:36 +05:30
Nishant Aanjaney Jalan ee861c2857
docs: added links to directory structure pages 2024-09-15 22:14:02 +05:30
Nishant Aanjaney Jalan 3404589337
docs: update data fetching page to read more ergonomically 2024-09-15 22:07:31 +05:30
Nishant Aanjaney Jalan 21b2cc736e
docs: amended 3.rendering 2024-09-15 21:40:53 +05:30
Nishant Aanjaney Jalan 90fe70a374
docs: added example of what's run on client and server 2024-09-15 20:52:10 +05:30
Nishant Aanjaney Jalan 97de03c48c
docs: improving content of rendering modes page
Rearranged and rephrased the paragraphs to make a story from the initial request to the hydration process.
2024-09-15 15:32:12 +01:00
19 changed files with 833 additions and 755 deletions

View File

@ -8,21 +8,17 @@ Nuxt comes with two composables and a built-in library to perform data-fetching
In a nutshell:
- [`useFetch`](/docs/api/composables/use-fetch) is the most straightforward way to handle data fetching in a component setup function.
- [`$fetch`](/docs/api/utils/dollarfetch) is great to make network requests based on user interaction.
- [`useAsyncData`](/docs/api/composables/use-async-data), combined with `$fetch`, offers more fine-grained control.
- [`$fetch`](/docs/api/utils/dollarfetch) is the simplest way to make a network request.
- [`useFetch`](/docs/api/composables/use-fetch) is wrapper around `$fetch` that fetches data only once in [universal rendering](/docs/guide/concepts/rendering#universal-rendering).
- [`useAsyncData`](/docs/api/composables/use-async-data) is similar to `useFetch` but offers more fine-grained control.
Both `useFetch` and `useAsyncData` share a common set of options and patterns that we will detail in the last sections.
Before that, it's imperative to know why these composables exist in the first place.
## The need for `useFetch` and `useAsyncData`
## Why use specific composables for data fetching?
Nuxt is a framework which can run isomorphic (or universal) code in both server and client environments. If the [`$fetch` function](/docs/api/utils/dollarfetch) is used to perform data fetching in the setup function of a Vue component, this may cause data to be fetched twice, once on the server (to render the HTML) and once again on the client (when the HTML is hydrated). This invites the application to cause hydration issues and other unpredictable behaviours.
Nuxt is a framework which can run isomorphic (or universal) code in both server and client environments. If the [`$fetch` function](/docs/api/utils/dollarfetch) is used to perform data fetching in the setup function of a Vue component, this may cause data to be fetched twice, once on the server (to render the HTML) and once again on the client (when the HTML is hydrated). This is why Nuxt offers specific data fetching composables so data is fetched only once.
### Network calls duplication
The [`useFetch`](/docs/api/composables/use-fetch) and [`useAsyncData`](/docs/api/composables/use-async-data) composables ensure that once an API call is made on the server, the data is properly forwarded to the client in the payload.
The [`useFetch`](/docs/api/composables/use-fetch) and [`useAsyncData`](/docs/api/composables/use-async-data) composables solves this problem by ensuring that once an API call is made on the server, the data is properly forwarded to the client in the payload.
The payload is a JavaScript object accessible through [`useNuxtApp().payload`](/docs/api/composables/use-nuxt-app#payload). It is used on the client to avoid refetching the same data when the code is executed in the browser [during hydration](/docs/guide/concepts/rendering#universal-rendering).
@ -30,6 +26,34 @@ The payload is a JavaScript object accessible through [`useNuxtApp().payload`](/
Use the [Nuxt DevTools](https://devtools.nuxt.com) to inspect this data in the **Payload tab**.
::
```vue [app.vue]
<script setup lang="ts">
const { data } = await useFetch('/api/data')
async function handleFormSubmit() {
const res = await $fetch('/api/submit', {
method: 'POST',
body: {
// My form data
}
})
}
</script>
<template>
<div v-if="data == null">
No data
</div>
<div v-else>
<form @submit="handleFormSubmit">
<!-- form input tags -->
</form>
</div>
</template>
```
In the example above, `useFetch` would make sure that the request would occur in the server and is properly forwarded to the browser. `$fetch` has no such mechanism and is a better option to use when the request is solely made from the browser.
### Suspense
Nuxt uses Vues [`<Suspense>`](https://vuejs.org/guide/built-ins/suspense) component under the hood to prevent navigation before every async data is available to the view. The data fetching composables can help you leverage this feature and use what suits best on a per-calls basis.
@ -38,9 +62,35 @@ Nuxt uses Vues [`<Suspense>`](https://vuejs.org/guide/built-ins/suspense) com
You can add the [`<NuxtLoadingIndicator>`](/docs/api/components/nuxt-loading-indicator) to add a progress bar between page navigations.
::
## `$fetch`
Nuxt includes the [ofetch](https://github.com/unjs/ofetch) library, and is auto-imported as the `$fetch` alias globally across your application.
```vue twoslash [pages/todos.vue]
<script setup lang="ts">
async function addTodo() {
const todo = await $fetch('/api/todos', {
method: 'POST',
body: {
// My todo data
}
})
}
</script>
```
::warning
Beware that using only `$fetch` will not provide [network calls de-duplication and navigation prevention](#the-need-for-usefetch-and-useasyncdata). :br
It is recommended to use `$fetch` for client-side interactions (event based) or combined with [`useAsyncData`](#useasyncdata) when fetching the initial component data.
::
::read-more{to="/docs/api/utils/dollarfetch"}
Read more about `$fetch`.
::
## `useFetch`
The [`useFetch`](/docs/api/composables/use-fetch) composable is the most straightforward way to perform data fetching.
The [`useFetch`](/docs/api/composables/use-fetch) composable uses `$fetch` under-the-hood to make SSR-safe network calls in the setup function.
```vue twoslash [app.vue]
<script setup lang="ts">
@ -62,32 +112,6 @@ Watch the video from Alexander Lichter to avoid using `useFetch` the wrong way!
:link-example{to="/docs/examples/features/data-fetching"}
## `$fetch`
Nuxt includes the [ofetch](https://github.com/unjs/ofetch) library, and is auto-imported as the `$fetch` alias globally across your application. It's what `useFetch` uses behind the scenes.
```vue twoslash [pages/todos.vue]
<script setup lang="ts">
async function addTodo() {
const todo = await $fetch('/api/todos', {
method: 'POST',
body: {
// My todo data
}
})
}
</script>
```
::warning
Beware that using only `$fetch` will not provide [network calls de-duplication and navigation prevention](#why-use-specific-composables-for-data-fetching). :br
It is recommended to use `$fetch` for client-side interactions (event based) or combined with [`useAsyncData`](#useasyncdata) when fetching the initial component data.
::
::read-more{to="/docs/api/utils/dollarfetch"}
Read more about `$fetch`.
::
## `useAsyncData`
The `useAsyncData` composable is responsible for wrapping async logic and returning the result once it is resolved.

View File

@ -11,18 +11,41 @@ By default, Nuxt uses **universal rendering** to provide better user experience,
## Universal Rendering
When the browser requests a URL with universal (server-side + client-side) rendering enabled, the server returns a fully rendered HTML page to the browser. Whether the page has been generated in advance and cached or is rendered on the fly, at some point, Nuxt has run the JavaScript (Vue.js) code in a server environment, producing an HTML document. Users immediately get the content of our application, contrary to client-side rendering. This step is similar to traditional **server-side rendering** performed by PHP or Ruby applications.
This step is similar to traditional **server-side rendering** performed by PHP or Ruby applications. When the browser requests a URL with universal rendering enabled, Nuxt runs the JavaScript (Vue.js) code in a server environment and returns a fully rendered HTML page to the browser. Nuxt may also return a fully rendered HTML page from a cache if the page was generated in advance. Users immediately get the entirety of the initial content of the application, contrary to client-side rendering.
To not lose the benefits of the client-side rendering method, such as dynamic interfaces and pages transitions, the Client (browser) loads the JavaScript code that runs on the Server in the background once the HTML document has been downloaded. The browser interprets it again (hence **Universal rendering**) and Vue.js takes control of the document and enables interactivity.
Making a static page interactive in the browser is called "Hydration".
Once the HTML document has been downloaded, The browser interprets this and Vue.js takes control of the document. The same JavaScript code that once ran on the server runs on the client (browser) **again** in the background now enabling interactivity (hence **Universal rendering**) by binding its listeners to the HTML. This is called **Hydration**. When hydration is complete, the page can enjoy benefits such as dynamic interfaces and page transitions.
Universal rendering allows a Nuxt application to provide quick page load times while preserving the benefits of client-side rendering. Furthermore, as the content is already present in the HTML document, crawlers can index it without overhead.
![Users can access the static content when the HTML document is loaded. Hydration then allows page's interactivity](/assets/docs/concepts/rendering/ssr.svg)
**What's server-rendered and what's client-rendered?**
It is normal to ask which parts of a Vue file runs on the server and/or the client in universal rendering mode.
```vue [app.vue]
<script setup lang="ts">
const counter = ref(0); // executes in server and client environments
const handleClick = () => {
counter.value++; // executes only in a client environment
};
</script>
<template>
<div>
<p>Count: {{ counter }}</p>
<button @click="handleClick">Increment</button>
</div>
</template>
```
On the initial request, the `counter` ref is initialized in the server since it is rendered inside the `<p>` tag. The contents of `handleClick` is never executed here. During hydration in the browser, the `counter` ref is re-initialized. The `handleClick` finally binds itself to the button; Therefore it is reasonable to deduce that the body of `handleClick` will always run in a browser environment.
[Middlewares](/docs/2.guide/2.directory-structure/1.middleware.md) and [pages](/docs/2.guide/2.directory-structure/1.pages.md) run in the server and on the client during hydration. [Plugins](/docs/2.guide/2.directory-structure/1.plugins.md) can be rendered on the server or client or both. [Components](/docs/2.guide/2.directory-structure/1.components.md) can be forced to run on the client only as well. [Composables](/docs/2.guide/2.directory-structure/1.composables.md) and [utilities](/docs/2.guide/2.directory-structure/1.utils.md) are rendered based on the context of their usage.
**Benefits of server-side rendering:**
- **Performance**: Users can get immediate access to the page's content because browsers can display static content much faster than JavaScript-generated content. At the same time, Nuxt preserves the interactivity of a web application when the hydration process happens.
- **Performance**: Users can get immediate access to the page's content because browsers can display static content much faster than JavaScript-generated content. At the same time, Nuxt preserves the interactivity of a web application during the hydration process.
- **Search Engine Optimization**: Universal rendering delivers the entire HTML content of the page to the browser as a classic server application. Web crawlers can directly index the page's content, which makes Universal rendering a great choice for any content that you want to index quickly.
**Downsides of server-side rendering:**

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

@ -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,27 +79,30 @@ 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
for (const path of paths) {
try {
const src = jiti.esmResolve(path)
nuxtModule = await jiti.import(src) as NuxtModule
const paths = [join(nuxtModule, 'nuxt'), join(nuxtModule, 'module'), nuxtModule, join(nuxt.options.rootDir, nuxtModule)]
// nuxt-module-builder generates a module.json with metadata including the version
const moduleMetadataPath = join(dirname(src), 'module.json')
if (existsSync(moduleMetadataPath)) {
buildTimeModuleMeta = JSON.parse(await fsp.readFile(moduleMetadataPath, 'utf-8'))
for (const parentURL of nuxt.options.modulesDir) {
for (const path of paths) {
try {
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
const moduleMetadataPath = join(dirname(src), 'module.json')
if (existsSync(moduleMetadataPath)) {
buildTimeModuleMeta = JSON.parse(await fsp.readFile(moduleMetadataPath, 'utf-8'))
}
break
} 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
}
logger.error(`Error while importing module \`${nuxtModule}\`: ${error}`)
throw error
}
break
} catch (_err: unknown) {
error = _err
continue
}
}
if (typeof nuxtModule !== 'function' && error) {
logger.error(`Error while importing module \`${nuxtModule}\`: ${error}`)
throw error
if (typeof nuxtModule !== 'string') { break }
}
}

View File

@ -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

@ -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

@ -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
if (typeof pluginFn === 'function') {
css.postcss.plugins.push(pluginFn(pluginOptions))
} else {
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))
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

@ -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
if (typeof pluginFn === 'function') {
plugins.push(pluginFn(pluginOptions))
} else {
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))
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"`)