mirror of https://github.com/nuxt/nuxt.git
Compare commits
16 Commits
3e895291b8
...
b239a8e7dd
Author | SHA1 | Date |
---|---|---|
Nishant Aanjaney Jalan | b239a8e7dd | |
Daniel Roe | 73accb5ac1 | |
Daniel Roe | 097105fb2b | |
Daniel Roe | 33e2d0a5f3 | |
renovate[bot] | 01e00963bc | |
Matej Černý | d970da6da5 | |
renovate[bot] | 61b5ef7850 | |
Matej Černý | 00ad2b8233 | |
Nishant Aanjaney Jalan | cc2846c1a8 | |
Nishant Aanjaney Jalan | a672e38399 | |
Nishant Aanjaney Jalan | 5236e79a3b | |
Nishant Aanjaney Jalan | ee861c2857 | |
Nishant Aanjaney Jalan | 3404589337 | |
Nishant Aanjaney Jalan | 21b2cc736e | |
Nishant Aanjaney Jalan | 90fe70a374 | |
Nishant Aanjaney Jalan | 97de03c48c |
|
@ -8,21 +8,17 @@ Nuxt comes with two composables and a built-in library to perform data-fetching
|
||||||
|
|
||||||
In a nutshell:
|
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 the simplest way to make a network request.
|
||||||
- [`$fetch`](/docs/api/utils/dollarfetch) is great to make network requests based on user interaction.
|
- [`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), combined with `$fetch`, offers more fine-grained control.
|
- [`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.
|
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.
|
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.
|
||||||
|
|
||||||
### 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 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).
|
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**.
|
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
|
### Suspense
|
||||||
|
|
||||||
Nuxt uses Vue’s [`<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.
|
Nuxt uses Vue’s [`<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 Vue’s [`<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.
|
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`
|
## `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]
|
```vue twoslash [app.vue]
|
||||||
<script setup lang="ts">
|
<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"}
|
: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`
|
## `useAsyncData`
|
||||||
|
|
||||||
The `useAsyncData` composable is responsible for wrapping async logic and returning the result once it is resolved.
|
The `useAsyncData` composable is responsible for wrapping async logic and returning the result once it is resolved.
|
||||||
|
|
|
@ -11,18 +11,41 @@ By default, Nuxt uses **universal rendering** to provide better user experience,
|
||||||
|
|
||||||
## Universal Rendering
|
## 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.
|
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.
|
||||||
|
|
||||||
Making a static page interactive in the browser is called "Hydration".
|
|
||||||
|
|
||||||
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.
|
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)
|
![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:**
|
**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.
|
- **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:**
|
**Downsides of server-side rendering:**
|
||||||
|
|
|
@ -166,6 +166,10 @@ export default defineNuxtConfig({
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
::note
|
||||||
|
Any nested directories need to be added first as they are scanned in order.
|
||||||
|
::
|
||||||
|
|
||||||
## npm Packages
|
## 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.
|
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
|
## 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.
|
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.
|
||||||
|
|
|
@ -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
|
## Advanced Usage
|
||||||
|
|
||||||
### Nitro Config
|
### Nitro Config
|
||||||
|
|
|
@ -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).
|
||||||
|
::
|
31
package.json
31
package.json
|
@ -39,9 +39,9 @@
|
||||||
"@nuxt/ui-templates": "workspace:*",
|
"@nuxt/ui-templates": "workspace:*",
|
||||||
"@nuxt/vite-builder": "workspace:*",
|
"@nuxt/vite-builder": "workspace:*",
|
||||||
"@nuxt/webpack-builder": "workspace:*",
|
"@nuxt/webpack-builder": "workspace:*",
|
||||||
"@vue/compiler-core": "3.5.6",
|
"@vue/compiler-core": "3.5.7",
|
||||||
"@vue/compiler-dom": "3.5.6",
|
"@vue/compiler-dom": "3.5.7",
|
||||||
"@vue/shared": "3.5.6",
|
"@vue/shared": "3.5.7",
|
||||||
"@types/node": "20.16.5",
|
"@types/node": "20.16.5",
|
||||||
"c12": "2.0.0-beta.2",
|
"c12": "2.0.0-beta.2",
|
||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
|
@ -51,16 +51,16 @@
|
||||||
"nuxt": "workspace:*",
|
"nuxt": "workspace:*",
|
||||||
"ohash": "1.1.4",
|
"ohash": "1.1.4",
|
||||||
"postcss": "8.4.47",
|
"postcss": "8.4.47",
|
||||||
"rollup": "4.22.2",
|
"rollup": "4.22.4",
|
||||||
"send": ">=0.19.0",
|
"send": ">=0.19.0",
|
||||||
"typescript": "5.6.2",
|
"typescript": "5.6.2",
|
||||||
"ufo": "1.5.4",
|
"ufo": "1.5.4",
|
||||||
"unbuild": "3.0.0-rc.7",
|
"unbuild": "3.0.0-rc.7",
|
||||||
"vite": "5.4.6",
|
"vite": "5.4.7",
|
||||||
"vue": "3.5.6"
|
"vue": "3.5.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "9.10.0",
|
"@eslint/js": "9.11.0",
|
||||||
"@nuxt/eslint-config": "0.5.7",
|
"@nuxt/eslint-config": "0.5.7",
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
"@nuxt/test-utils": "3.14.2",
|
"@nuxt/test-utils": "3.14.2",
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
"cssnano": "7.0.6",
|
"cssnano": "7.0.6",
|
||||||
"destr": "2.0.3",
|
"destr": "2.0.3",
|
||||||
"devalue": "5.0.0",
|
"devalue": "5.0.0",
|
||||||
"eslint": "9.10.0",
|
"eslint": "9.11.0",
|
||||||
"eslint-plugin-no-only-tests": "3.3.0",
|
"eslint-plugin-no-only-tests": "3.3.0",
|
||||||
"eslint-plugin-perfectionist": "3.6.0",
|
"eslint-plugin-perfectionist": "3.6.0",
|
||||||
"eslint-typegen": "0.3.2",
|
"eslint-typegen": "0.3.2",
|
||||||
|
@ -93,9 +93,9 @@
|
||||||
"nuxi": "3.13.2",
|
"nuxi": "3.13.2",
|
||||||
"nuxt": "workspace:*",
|
"nuxt": "workspace:*",
|
||||||
"nuxt-content-twoslash": "0.1.1",
|
"nuxt-content-twoslash": "0.1.1",
|
||||||
"ofetch": "1.3.4",
|
"ofetch": "1.4.0",
|
||||||
"pathe": "1.1.2",
|
"pathe": "1.1.2",
|
||||||
"playwright-core": "1.47.1",
|
"playwright-core": "1.47.2",
|
||||||
"rimraf": "6.0.1",
|
"rimraf": "6.0.1",
|
||||||
"semver": "7.6.3",
|
"semver": "7.6.3",
|
||||||
"sherif": "1.0.0",
|
"sherif": "1.0.0",
|
||||||
|
@ -106,18 +106,13 @@
|
||||||
"ufo": "1.5.4",
|
"ufo": "1.5.4",
|
||||||
"vitest": "2.1.1",
|
"vitest": "2.1.1",
|
||||||
"vitest-environment-nuxt": "1.0.1",
|
"vitest-environment-nuxt": "1.0.1",
|
||||||
"vue": "3.5.6",
|
"vue": "3.5.7",
|
||||||
"vue-router": "4.4.5",
|
"vue-router": "4.4.5",
|
||||||
"vue-tsc": "2.1.6"
|
"vue-tsc": "2.1.6"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.10.0",
|
"packageManager": "pnpm@9.11.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.10.0 || >=18.0.0"
|
"node": "^16.10.0 || >=18.0.0"
|
||||||
},
|
},
|
||||||
"version": "",
|
"version": ""
|
||||||
"pnpm": {
|
|
||||||
"patchedDependencies": {
|
|
||||||
"ofetch@1.3.4": "patches/ofetch@1.3.4.patch"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
"@types/semver": "7.5.8",
|
"@types/semver": "7.5.8",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||||
"unbuild": "3.0.0-rc.7",
|
"unbuild": "3.0.0-rc.7",
|
||||||
"vite": "5.4.6",
|
"vite": "5.4.7",
|
||||||
"vitest": "2.1.1",
|
"vitest": "2.1.1",
|
||||||
"webpack": "5.94.0"
|
"webpack": "5.94.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -79,11 +79,12 @@ export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, n
|
||||||
|
|
||||||
// Import if input is string
|
// Import if input is string
|
||||||
if (typeof nuxtModule === 'string') {
|
if (typeof nuxtModule === 'string') {
|
||||||
const paths = [join(nuxtModule, 'nuxt'), join(nuxtModule, 'module'), nuxtModule]
|
const paths = [join(nuxtModule, 'nuxt'), join(nuxtModule, 'module'), nuxtModule, join(nuxt.options.rootDir, nuxtModule)]
|
||||||
let error: unknown
|
|
||||||
|
for (const parentURL of nuxt.options.modulesDir) {
|
||||||
for (const path of paths) {
|
for (const path of paths) {
|
||||||
try {
|
try {
|
||||||
const src = jiti.esmResolve(path)
|
const src = jiti.esmResolve(path, { parentURL })
|
||||||
nuxtModule = await jiti.import(src) as NuxtModule
|
nuxtModule = await jiti.import(src) as NuxtModule
|
||||||
|
|
||||||
// nuxt-module-builder generates a module.json with metadata including the version
|
// 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'))
|
buildTimeModuleMeta = JSON.parse(await fsp.readFile(moduleMetadataPath, 'utf-8'))
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
} catch (_err: unknown) {
|
} catch (error: unknown) {
|
||||||
error = _err
|
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
|
continue
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (typeof nuxtModule !== 'function' && error) {
|
|
||||||
logger.error(`Error while importing module \`${nuxtModule}\`: ${error}`)
|
logger.error(`Error while importing module \`${nuxtModule}\`: ${error}`)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (typeof nuxtModule !== 'string') { break }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Throw error if input is not a function
|
// Throw error if input is not a function
|
||||||
if (typeof nuxtModule !== 'function') {
|
if (typeof nuxtModule !== 'function') {
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/devalue": "^2.0.2",
|
"@nuxt/devalue": "^2.0.2",
|
||||||
"@nuxt/devtools": "^1.4.2",
|
"@nuxt/devtools": "^1.5.0",
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
"@nuxt/telemetry": "^2.6.0",
|
"@nuxt/telemetry": "^2.6.0",
|
||||||
|
@ -69,7 +69,7 @@
|
||||||
"@unhead/shared": "^1.11.6",
|
"@unhead/shared": "^1.11.6",
|
||||||
"@unhead/ssr": "^1.11.6",
|
"@unhead/ssr": "^1.11.6",
|
||||||
"@unhead/vue": "^1.11.6",
|
"@unhead/vue": "^1.11.6",
|
||||||
"@vue/shared": "^3.5.6",
|
"@vue/shared": "^3.5.7",
|
||||||
"acorn": "8.12.1",
|
"acorn": "8.12.1",
|
||||||
"c12": "^2.0.0-beta.2",
|
"c12": "^2.0.0-beta.2",
|
||||||
"chokidar": "^3.6.0",
|
"chokidar": "^3.6.0",
|
||||||
|
@ -97,7 +97,7 @@
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||||
"nuxi": "^3.13.2",
|
"nuxi": "^3.13.2",
|
||||||
"nypm": "^0.3.11",
|
"nypm": "^0.3.11",
|
||||||
"ofetch": "^1.3.4",
|
"ofetch": "^1.4.0",
|
||||||
"ohash": "^1.1.4",
|
"ohash": "^1.1.4",
|
||||||
"pathe": "^1.1.2",
|
"pathe": "^1.1.2",
|
||||||
"perfect-debounce": "^1.0.0",
|
"perfect-debounce": "^1.0.0",
|
||||||
|
@ -119,7 +119,7 @@
|
||||||
"unplugin-vue-router": "^0.10.8",
|
"unplugin-vue-router": "^0.10.8",
|
||||||
"unstorage": "^1.12.0",
|
"unstorage": "^1.12.0",
|
||||||
"untyped": "^1.4.2",
|
"untyped": "^1.4.2",
|
||||||
"vue": "^3.5.6",
|
"vue": "^3.5.7",
|
||||||
"vue-bundle-renderer": "^2.1.0",
|
"vue-bundle-renderer": "^2.1.0",
|
||||||
"vue-devtools-stub": "^0.1.0",
|
"vue-devtools-stub": "^0.1.0",
|
||||||
"vue-router": "^4.4.5"
|
"vue-router": "^4.4.5"
|
||||||
|
@ -130,9 +130,9 @@
|
||||||
"@parcel/watcher": "2.4.1",
|
"@parcel/watcher": "2.4.1",
|
||||||
"@types/estree": "1.0.6",
|
"@types/estree": "1.0.6",
|
||||||
"@vitejs/plugin-vue": "5.1.4",
|
"@vitejs/plugin-vue": "5.1.4",
|
||||||
"@vue/compiler-sfc": "3.5.6",
|
"@vue/compiler-sfc": "3.5.7",
|
||||||
"unbuild": "3.0.0-rc.7",
|
"unbuild": "3.0.0-rc.7",
|
||||||
"vite": "5.4.6",
|
"vite": "5.4.7",
|
||||||
"vitest": "2.1.1"
|
"vitest": "2.1.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|
|
@ -42,20 +42,20 @@
|
||||||
"@unhead/schema": "1.11.6",
|
"@unhead/schema": "1.11.6",
|
||||||
"@vitejs/plugin-vue": "5.1.4",
|
"@vitejs/plugin-vue": "5.1.4",
|
||||||
"@vitejs/plugin-vue-jsx": "4.0.1",
|
"@vitejs/plugin-vue-jsx": "4.0.1",
|
||||||
"@vue/compiler-core": "3.5.6",
|
"@vue/compiler-core": "3.5.7",
|
||||||
"@vue/compiler-sfc": "3.5.6",
|
"@vue/compiler-sfc": "3.5.7",
|
||||||
"@vue/language-core": "2.1.6",
|
"@vue/language-core": "2.1.6",
|
||||||
"c12": "2.0.0-beta.2",
|
"c12": "2.0.0-beta.2",
|
||||||
"esbuild-loader": "4.2.2",
|
"esbuild-loader": "4.2.2",
|
||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
"ignore": "6.0.2",
|
"ignore": "6.0.2",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||||
"ofetch": "1.3.4",
|
"ofetch": "1.4.0",
|
||||||
"unbuild": "3.0.0-rc.7",
|
"unbuild": "3.0.0-rc.7",
|
||||||
"unctx": "2.3.1",
|
"unctx": "2.3.1",
|
||||||
"unenv": "1.10.0",
|
"unenv": "1.10.0",
|
||||||
"vite": "5.4.6",
|
"vite": "5.4.7",
|
||||||
"vue": "3.5.6",
|
"vue": "3.5.7",
|
||||||
"vue-bundle-renderer": "2.1.0",
|
"vue-bundle-renderer": "2.1.0",
|
||||||
"vue-loader": "17.4.2",
|
"vue-loader": "17.4.2",
|
||||||
"vue-router": "4.4.5",
|
"vue-router": "4.4.5",
|
||||||
|
|
|
@ -30,6 +30,6 @@
|
||||||
"tinyexec": "0.3.0",
|
"tinyexec": "0.3.0",
|
||||||
"tinyglobby": "0.2.6",
|
"tinyglobby": "0.2.6",
|
||||||
"unocss": "0.62.4",
|
"unocss": "0.62.4",
|
||||||
"vite": "5.4.6"
|
"vite": "5.4.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,9 @@
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
"@types/clear": "0.1.4",
|
"@types/clear": "0.1.4",
|
||||||
"@types/estree": "1.0.6",
|
"@types/estree": "1.0.6",
|
||||||
"rollup": "4.22.2",
|
"rollup": "4.22.4",
|
||||||
"unbuild": "3.0.0-rc.7",
|
"unbuild": "3.0.0-rc.7",
|
||||||
"vue": "3.5.6"
|
"vue": "3.5.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
"ufo": "^1.5.4",
|
"ufo": "^1.5.4",
|
||||||
"unenv": "^1.10.0",
|
"unenv": "^1.10.0",
|
||||||
"unplugin": "^1.14.1",
|
"unplugin": "^1.14.1",
|
||||||
"vite": "^5.4.6",
|
"vite": "^5.4.7",
|
||||||
"vite-node": "^2.1.1",
|
"vite-node": "^2.1.1",
|
||||||
"vite-plugin-checker": "^0.8.0",
|
"vite-plugin-checker": "^0.8.0",
|
||||||
"vue-bundle-renderer": "^2.1.0"
|
"vue-bundle-renderer": "^2.1.0"
|
||||||
|
|
|
@ -27,11 +27,16 @@ export async function resolveCSSOptions (nuxt: Nuxt): Promise<ViteConfig['css']>
|
||||||
const pluginOptions = postcssOptions.plugins[pluginName]
|
const pluginOptions = postcssOptions.plugins[pluginName]
|
||||||
if (!pluginOptions) { continue }
|
if (!pluginOptions) { continue }
|
||||||
|
|
||||||
const path = jiti.esmResolve(pluginName)
|
let pluginFn: ((opts: Record<string, any>) => Plugin) | undefined
|
||||||
const pluginFn = (await jiti.import(path)) as (opts: Record<string, any>) => Plugin
|
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') {
|
if (typeof pluginFn === 'function') {
|
||||||
css.postcss.plugins.push(pluginFn(pluginOptions))
|
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.`)
|
console.warn(`[nuxt] could not import postcss plugin \`${pluginName}\`. Please report this as a bug.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ export const viteNodeOptions = JSON.parse(process.env.NUXT_VITE_NODE_OPTIONS ||
|
||||||
|
|
||||||
export const viteNodeFetch = $fetch.create({
|
export const viteNodeFetch = $fetch.create({
|
||||||
baseURL: viteNodeOptions.baseURL,
|
baseURL: viteNodeOptions.baseURL,
|
||||||
// @ts-expect-error https://github.com/node-fetch/node-fetch#custom-agent
|
|
||||||
agent: viteNodeOptions.baseURL.startsWith('https://')
|
agent: viteNodeOptions.baseURL.startsWith('https://')
|
||||||
? new HTTPSAgent({ rejectUnauthorized: false })
|
? new HTTPSAgent({ rejectUnauthorized: false })
|
||||||
: null,
|
: null,
|
||||||
|
|
|
@ -78,9 +78,9 @@
|
||||||
"@types/pify": "5.0.4",
|
"@types/pify": "5.0.4",
|
||||||
"@types/webpack-bundle-analyzer": "4.7.0",
|
"@types/webpack-bundle-analyzer": "4.7.0",
|
||||||
"@types/webpack-hot-middleware": "2.25.9",
|
"@types/webpack-hot-middleware": "2.25.9",
|
||||||
"rollup": "4.22.2",
|
"rollup": "4.22.4",
|
||||||
"unbuild": "3.0.0-rc.7",
|
"unbuild": "3.0.0-rc.7",
|
||||||
"vue": "3.5.6"
|
"vue": "3.5.7"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vue": "^3.3.4"
|
"vue": "^3.3.4"
|
||||||
|
|
|
@ -49,11 +49,16 @@ export async function getPostcssConfig (nuxt: Nuxt) {
|
||||||
const pluginOptions = postcssOptions.plugins[pluginName]
|
const pluginOptions = postcssOptions.plugins[pluginName]
|
||||||
if (!pluginOptions) { continue }
|
if (!pluginOptions) { continue }
|
||||||
|
|
||||||
const path = jiti.esmResolve(pluginName)
|
let pluginFn: ((opts: Record<string, any>) => Plugin) | undefined
|
||||||
const pluginFn = (await jiti.import(path)) as (opts: Record<string, any>) => Plugin
|
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') {
|
if (typeof pluginFn === 'function') {
|
||||||
plugins.push(pluginFn(pluginOptions))
|
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.`)
|
console.warn(`[nuxt] could not import postcss plugin \`${pluginName}\`. Please report this as a bug.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
1205
pnpm-lock.yaml
1205
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -37,10 +37,10 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
|
||||||
const serverDir = join(rootDir, '.output/server')
|
const serverDir = join(rootDir, '.output/server')
|
||||||
|
|
||||||
const serverStats = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
|
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)
|
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
|
const packages = modules.files
|
||||||
.filter(m => m.endsWith('package.json'))
|
.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 serverDir = join(rootDir, '.output-inline/server')
|
||||||
|
|
||||||
const serverStats = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
|
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)
|
const modules = await analyzeSizes(['node_modules/**/*'], serverDir)
|
||||||
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"88.2k"`)
|
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"88.2k"`)
|
||||||
|
|
Loading…
Reference in New Issue