Merge branch 'main' into patch-21

This commit is contained in:
Michael Brevard 2024-06-27 17:40:56 +03:00 committed by GitHub
commit 97fd06e4ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
127 changed files with 2447 additions and 2647 deletions

View File

@ -30,4 +30,4 @@ jobs:
- name: Lint (docs)
run: pnpm lint:docs:fix
- uses: autofix-ci/action@ea32e3a12414e6d3183163c3424a7d7a8631ad84
- uses: autofix-ci/action@dd55f44df8f7cdb7a6bf74c78677eb8acd40cd0a

View File

@ -52,4 +52,4 @@ jobs:
- name: Lint (code)
run: pnpm lint:fix
- uses: autofix-ci/action@ea32e3a12414e6d3183163c3424a7d7a8631ad84
- uses: autofix-ci/action@dd55f44df8f7cdb7a6bf74c78677eb8acd40cd0a

View File

@ -41,7 +41,9 @@ jobs:
head_sha="$(echo "$pr" | jq -r .head.sha)"
updated_at="$(echo "$pr" | jq -r .updated_at)"
if [[ $(date -d $updated_at) > $(date -d $COMMENT_AT) ]]; exit 1; fi
if [[ $(date -d "$updated_at" +%s) -gt $(date -d "$COMMENT_AT" +%s) ]]; then
exit 1
fi
echo "head_sha=$head_sha" >> $GITHUB_OUTPUT
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

View File

@ -12,7 +12,7 @@ on:
schedule:
- cron: '20 7 * * 2'
push:
branches: ["main", "3.x"]
branches: ["main"]
# Declare default permissions as read only.
permissions: read-all

View File

@ -11,10 +11,26 @@ navigation.icon: i-ph-arrow-circle-up-duotone
To upgrade Nuxt to the [latest release](https://github.com/nuxt/nuxt/releases), use the `nuxi upgrade` command.
```bash [Terminal]
::code-group
```bash [npm]
npx nuxi upgrade
```
```bash [yarn]
yarn dlx nuxi upgrade
```
```bash [pnpm]
pnpm dlx nuxi upgrade
```
```bash [bun]
bunx nuxi upgrade
```
::
### Nightly Release Channel
To use the latest Nuxt build and test features before their release, read about the [nightly release channel](/docs/guide/going-further/nightly-release-channel) guide.
@ -355,6 +371,25 @@ However, if you are a module author using the `builder:watch` hook and wishing t
})
```
#### Removal of `window.__NUXT__` object
##### What Changed
We are removing the global `window.__NUXT__` object after the app finishes hydration.
##### Reasons for Change
This opens the way to multi-app patterns ([#21635](https://github.com/nuxt/nuxt/issues/21635)) and enables us to focus on a single way to access Nuxt app data - `useNuxtApp()`.
##### Migration Steps
The data is still available, but can be accessed with `useNuxtApp().payload`:
```diff
- console.log(window.__NUXT__)
+ console.log(useNuxtApp().payload)
```
#### Directory index scanning
🚦 **Impact Level**: Medium

View File

@ -37,10 +37,14 @@ Open a terminal (if you're using [Visual Studio Code](https://code.visualstudio.
::code-group
```bash [npx]
```bash [npm]
npx nuxi@latest init <project-name>
```
```bash [yarn]
yarn dlx nuxi@latest init <project-name>
```
```bash [pnpm]
pnpm dlx nuxi@latest init <project-name>
```

View File

@ -77,10 +77,26 @@ h1 {
You can also reference stylesheets that are distributed through npm. Let's use the popular `animate.css` library as an example.
```bash [Terminal]
::code-group
```bash [npm]
npm install animate.css
```
```bash [yarn]
yarn add animate.css
```
```bash [pnpm]
pnpm install animate.css
```
```bash [bun]
bun install animate.css
```
::
Then you can reference it directly in your pages, layouts and components:
```vue [app.vue]

View File

@ -14,10 +14,26 @@ Use the [`nuxi generate` command](/docs/api/commands/generate) to build and pre-
This will build your site, stand up a nuxt instance, and, by default, prerender the root page `/` along with any of your site's pages it links to, any of your site's pages they link to, and so on.
```bash [Terminal]
::code-group
```bash [npm]
npx nuxi generate
```
```bash [yarn]
yarn dlx nuxi generate
```
```bash [pnpm]
pnpm dlx nuxi generate
```
```bash [bun]
bunx nuxi generate
```
::
You can now deploy the `.output/public` directory to any static hosting service or preview it locally with `npx serve .output/public`.
Working of the Nitro crawler:

View File

@ -33,6 +33,10 @@ npx nuxi dev --dotenv .env.local
When updating `.env` in development mode, the Nuxt instance is automatically restarted to apply new values to the `process.env`.
::important
In your application code, you should use [Runtime Config](https://nuxt.com/docs/guide/going-further/runtime-config) instead of plain env variables.
::
## Production
**After your server is built**, you are responsible for setting environment variables when you run the server.

View File

@ -90,7 +90,7 @@ declare module '#app' {
}
}
declare module 'nitropack' {
declare module 'nitro/types' {
interface NitroRuntimeHooks {
'your-nitro-hook': () => void;
}

View File

@ -583,7 +583,7 @@ export default defineNuxtModule({
interface MyModuleNitroRules {
myModule?: { foo: 'bar' }
}
declare module 'nitropack' {
declare module 'nitro/types' {
interface NitroRouteRules extends MyModuleNitroRules {}
interface NitroRouteConfig extends MyModuleNitroRules {}
}

View File

@ -70,7 +70,7 @@ const { data: modules } = await useAsyncData('modules', () => $api('/modules'))
Wrapping with [`useAsyncData`](/docs/api/composables/use-async-data) **avoid double data fetching when doing server-side rendering** (server & client on hydration).
::
## Custom `useFetch`
## Custom `useFetch`/`useAsyncData`
Now that `$api` has the logic we want, let's create a `useAPI` composable to replace the usage of `useAsyncData` + `$api`:
@ -96,6 +96,10 @@ const { data: modules } = await useAPI('/modules')
</script>
```
::note
This example demonstrates how to use a custom `useFetch`, but the same structure is identical for a custom `useAsyncData`.
::
::callout{icon="i-simple-icons-youtube" color="red" to="https://www.youtube.com/watch?v=jXH8Tr-exhI"}
Watch a video about custom `$fetch` and Repository Pattern in Nuxt.
::

View File

@ -25,6 +25,10 @@ const { data, pending, error, refresh, clear } = await useAsyncData(
</script>
```
::warning
If you're using a custom useAsyncData wrapper, do not await it in the composable, as that can cause unexpected behavior. Please follow [this recipe](/docs/guide/recipes/custom-usefetch#custom-usefetch) for more information on how to make a custom async data fetcher.
::
::note
`data`, `pending`, `status` and `error` are Vue refs and they should be accessed with `.value` when used within the `<script setup>`, while `refresh`/`execute` and `clear` are plain functions.
::

View File

@ -25,6 +25,10 @@ const { data, pending, error, refresh, clear } = await useFetch('/api/modules',
</script>
```
::warning
If you're using a custom useFetch wrapper, do not await it in the composable, as that can cause unexpected behavior. Please follow [this recipe](/docs/guide/recipes/custom-usefetch#custom-usefetch) for more information on how to make a custom async data fetcher.
::
::note
`data`, `pending`, `status` and `error` are Vue refs and they should be accessed with `.value` when used within the `<script setup>`, while `refresh`/`execute` and `clear` are plain functions..
::

View File

@ -32,10 +32,6 @@ We'll do our best to follow our [internal issue decision making flowchart](https
### Send a Pull Request
::Tip
On windows, you need to clone the repository with `git clone -c core.symlinks=true https://github.com/nuxt/nuxt.git` to make symlinks work.
::
We always welcome pull requests! ❤️
#### Before You Start

View File

@ -148,17 +148,6 @@ export default createConfigForNuxt({
],
},
],
'import/order': [
'error',
{
pathGroups: [
{
group: 'external',
pattern: '#vue-router',
},
],
},
],
'jsdoc/check-tag-names': [
'error',
{

View File

@ -34,6 +34,9 @@
"typecheck:docs": "DOCS_TYPECHECK=true pnpm nuxi prepare && nuxt-content-twoslash verify --content-dir docs"
},
"resolutions": {
"nitro": "npm:nitro-nightly@3.0.0-beta-28648657.9a717203",
"typescript": "5.5.2",
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
"@nuxt/kit": "workspace:*",
"@nuxt/schema": "workspace:*",
"@nuxt/ui-templates": "workspace:*",
@ -43,7 +46,7 @@
"nuxt": "workspace:*",
"rollup": "^4.18.0",
"vite": "5.3.1",
"vue": "3.4.29"
"vue": "3.4.30"
},
"devDependencies": {
"@eslint/js": "9.5.0",
@ -53,8 +56,7 @@
"@nuxt/webpack-builder": "workspace:*",
"@testing-library/vue": "8.1.0",
"@types/eslint__js": "8.42.3",
"@types/fs-extra": "11.0.4",
"@types/node": "20.14.7",
"@types/node": "20.14.9",
"@types/semver": "7.5.8",
"@unhead/schema": "1.9.14",
"@vitejs/plugin-vue": "5.0.4",
@ -68,20 +70,19 @@
"eslint-plugin-no-only-tests": "3.1.0",
"eslint-plugin-perfectionist": "2.11.0",
"eslint-typegen": "0.2.4",
"execa": "9.2.0",
"fs-extra": "11.2.0",
"execa": "9.3.0",
"globby": "14.0.1",
"h3": "1.12.0",
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
"happy-dom": "14.12.3",
"jiti": "1.21.6",
"markdownlint-cli": "0.41.0",
"nitropack": "2.9.6",
"nitro": "npm:nitro-nightly@3.0.0-beta-28648657.9a717203",
"nuxi": "3.12.0",
"nuxt": "workspace:*",
"nuxt-content-twoslash": "0.0.10",
"ofetch": "1.3.4",
"pathe": "1.1.2",
"playwright-core": "1.44.1",
"playwright-core": "1.45.0",
"rimraf": "5.0.7",
"semver": "7.6.2",
"std-env": "3.7.0",
@ -89,13 +90,18 @@
"ufo": "1.5.3",
"vitest": "1.6.0",
"vitest-environment-nuxt": "1.0.0",
"vue": "3.4.29",
"vue-router": "4.3.3",
"vue-tsc": "2.0.21"
"vue": "3.4.30",
"vue-router": "4.4.0",
"vue-tsc": "2.0.22"
},
"packageManager": "pnpm@9.4.0",
"engines": {
"node": "^16.10.0 || >=18.0.0"
},
"version": ""
"version": "",
"pnpm": {
"patchedDependencies": {
"ofetch@1.3.4": "patches/ofetch@1.3.4.patch"
}
}
}

View File

@ -8,6 +8,7 @@ export default defineBuildConfig({
externals: [
'@nuxt/schema',
'nitropack',
'nitro',
'webpack',
'vite',
'h3',

View File

@ -49,7 +49,7 @@
"devDependencies": {
"@types/hash-sum": "1.0.2",
"@types/semver": "7.5.8",
"nitropack": "2.9.6",
"nitro": "npm:nitro-nightly@3.0.0-beta-28648657.9a717203",
"unbuild": "latest",
"vite": "5.3.1",
"vitest": "1.6.0",

View File

@ -55,7 +55,7 @@ export async function addComponent (opts: AddComponentOptions) {
nuxt.hook('components:extend', (components: Component[]) => {
const existingComponentIndex = components.findIndex(c => (c.pascalName === component.pascalName || c.kebabName === component.kebabName) && c.mode === component.mode)
if (existingComponentIndex !== -1) {
const existingComponent = components[existingComponentIndex]
const existingComponent = components[existingComponentIndex]!
const existingPriority = existingComponent.priority ?? 0
const newPriority = component.priority ?? 0

View File

@ -50,7 +50,7 @@ export function resolveIgnorePatterns (relativePath?: string): string[] {
// Map ignore patterns based on if they start with * or !*
return ignorePatterns.map((p) => {
const [_, negation = '', pattern] = p.match(NEGATION_RE) || []
if (pattern[0] === '*') {
if (pattern && pattern[0] === '*') {
return p
}
return negation + relative(relativePath, resolve(nuxt.options.rootDir, pattern || p))
@ -73,7 +73,7 @@ export function resolveGroupSyntax (group: string): string[] {
groups = groups.flatMap((group) => {
const [head, ...tail] = group.split('{')
if (tail.length) {
const [body, ...rest] = tail.join('{').split('}')
const [body = '', ...rest] = tail.join('{').split('}')
return body.split(',').map(part => `${head}${part}${rest.join('')}`)
}

View File

@ -13,7 +13,7 @@ export function addLayout (this: any, template: NuxtTemplate | string, name?: st
// Nuxt 3 adds layouts on app
nuxt.hook('app:templates', (app) => {
if (layoutName in app.layouts) {
const relativePath = relative(nuxt.options.srcDir, app.layouts[layoutName].file)
const relativePath = relative(nuxt.options.srcDir, app.layouts[layoutName]!.file)
return logger.warn(
`Not overriding \`${layoutName}\` (provided by \`~/${relativePath}\`) with \`${src || filename}\`.`,
)

View File

@ -1,6 +1,7 @@
import { pathToFileURL } from 'node:url'
import { readPackageJSON, resolvePackageJSON } from 'pkg-types'
import type { Nuxt } from '@nuxt/schema'
import { resolve } from 'pathe'
import { importModule, tryImportModule } from '../internal/esm'
import type { LoadNuxtConfigOptions } from './config'
@ -10,64 +11,29 @@ export interface LoadNuxtOptions extends LoadNuxtConfigOptions {
/** Use lazy initialization of nuxt if set to false */
ready?: boolean
/** @deprecated Use cwd option */
rootDir?: LoadNuxtConfigOptions['cwd']
/** @deprecated use overrides option */
config?: LoadNuxtConfigOptions['overrides']
}
export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
// Backward compatibility
opts.cwd = opts.cwd || opts.rootDir
opts.overrides = opts.overrides || opts.config || {}
opts.cwd = resolve(opts.cwd || (opts as any).rootDir /* backwards compat */ || '.')
opts.overrides = opts.overrides || (opts as any).config as {} /* backwards compat */ || {}
// Apply dev as config override
opts.overrides.dev = !!opts.dev
const nearestNuxtPkg = await Promise.all(['nuxt-nightly', 'nuxt3', 'nuxt', 'nuxt-edge']
const nearestNuxtPkg = await Promise.all(['nuxt-nightly', 'nuxt']
.map(pkg => resolvePackageJSON(pkg, { url: opts.cwd }).catch(() => null)))
.then(r => (r.filter(Boolean) as string[]).sort((a, b) => b.length - a.length)[0])
if (!nearestNuxtPkg) {
throw new Error(`Cannot find any nuxt version from ${opts.cwd}`)
}
const pkg = await readPackageJSON(nearestNuxtPkg)
const majorVersion = pkg.version ? Number.parseInt(pkg.version.split('.')[0]) : ''
const rootDir = pathToFileURL(opts.cwd || process.cwd()).href
const rootDir = pathToFileURL(opts.cwd!).href
// Nuxt 3
if (majorVersion === 3) {
const { loadNuxt } = await importModule((pkg as any)._name || pkg.name, rootDir)
const nuxt = await loadNuxt(opts)
return nuxt
}
// Nuxt 2
const { loadNuxt } = await tryImportModule('nuxt-edge', rootDir) || await importModule('nuxt', rootDir)
const nuxt = await loadNuxt({
rootDir: opts.cwd,
for: opts.dev ? 'dev' : 'build',
configOverrides: opts.overrides,
ready: opts.ready,
envConfig: opts.dotenv, // TODO: Backward format conversion
})
// Mock new hookable methods
nuxt.removeHook ||= nuxt.clearHook.bind(nuxt)
nuxt.removeAllHooks ||= nuxt.clearHooks.bind(nuxt)
nuxt.hookOnce ||= (name: string, fn: (...args: any[]) => any, ...hookArgs: any[]) => {
const unsub = nuxt.hook(name, (...args: any[]) => {
unsub()
return fn(...args)
}, ...hookArgs)
return unsub
}
// https://github.com/nuxt/nuxt/tree/main/packages/kit/src/module/define.ts#L111-L113
nuxt.hooks ||= nuxt
return nuxt as Nuxt
const { loadNuxt } = await importModule((pkg as any)._name || pkg.name, rootDir)
const nuxt = await loadNuxt(opts)
return nuxt
}
export async function buildNuxt (nuxt: Nuxt): Promise<any> {

View File

@ -1,4 +1,4 @@
import type { Nitro, NitroDevEventHandler, NitroEventHandler } from 'nitropack'
import type { Nitro, NitroDevEventHandler, NitroEventHandler } from 'nitro/types'
import type { Import } from 'unimport'
import { normalize } from 'pathe'
import { useNuxt } from './context'
@ -12,7 +12,7 @@ function normalizeHandlerMethod (handler: NitroEventHandler) {
// retrieve method from handler file name
const [, method = undefined] = handler.handler.match(/\.(get|head|patch|post|put|delete|connect|options|trace)(\.\w+)*$/) || []
return {
method,
method: method as 'get' | 'head' | 'patch' | 'post' | 'put' | 'delete' | 'connect' | 'options' | 'trace' | undefined,
...handler,
handler: normalize(handler.handler),
}

View File

@ -1,5 +1,5 @@
import type { NuxtHooks, NuxtMiddleware } from '@nuxt/schema'
import type { NitroRouteConfig } from 'nitropack'
import type { NitroRouteConfig } from 'nitro/types'
import { defu } from 'defu'
import { useNuxt } from './context'
import { logger } from './logger'
@ -44,7 +44,7 @@ export function addRouteMiddleware (input: NuxtMiddleware | NuxtMiddleware[], op
for (const middleware of middlewares) {
const find = app.middleware.findIndex(item => item.name === middleware.name)
if (find >= 0) {
const foundPath = app.middleware[find].path
const foundPath = app.middleware[find]!.path
if (foundPath === middleware.path) { continue }
if (options.override === true) {
app.middleware[find] = { ...middleware }

View File

@ -174,7 +174,7 @@ export async function resolveNuxtModule (base: string, paths: string[]): Promise
for (const path of paths) {
if (path.startsWith(base)) {
resolved.push(path.split('/index.ts')[0])
resolved.push(path.split('/index.ts')[0]!)
} else {
const resolvedPath = await resolver.resolvePath(path)
resolved.push(resolvedPath.slice(0, resolvedPath.lastIndexOf(path) + path.length))

View File

@ -228,10 +228,10 @@ export async function _generateTypes (nuxt: Nuxt) {
if (excludedAlias.some(re => re.test(alias))) {
continue
}
let absolutePath = resolve(basePath, aliases[alias])
let absolutePath = resolve(basePath, aliases[alias]!)
let stats = await fsp.stat(absolutePath).catch(() => null /* file does not exist */)
if (!stats) {
const resolvedModule = await tryResolveModule(aliases[alias], nuxt.options.modulesDir)
const resolvedModule = await tryResolveModule(aliases[alias]!, nuxt.options.modulesDir)
if (resolvedModule) {
absolutePath = resolvedModule
stats = await fsp.stat(resolvedModule).catch(() => null)
@ -251,7 +251,7 @@ export async function _generateTypes (nuxt: Nuxt) {
// remove extension
? relativePath.replace(/\b\.\w+$/g, '')
// non-existent file probably shouldn't be resolved
: aliases[alias]
: aliases[alias]!
tsConfig.compilerOptions.paths[alias] = [path]
@ -334,7 +334,7 @@ function renderAttrs (obj: Record<string, string>) {
return attrs.join(' ')
}
function renderAttr (key: string, value: string) {
function renderAttr (key: string, value?: string) {
return value ? `${key}="${value}"` : ''
}

View File

@ -60,7 +60,7 @@
},
"dependencies": {
"@nuxt/devalue": "^2.0.2",
"@nuxt/devtools": "^1.3.3",
"@nuxt/devtools": "^1.3.6",
"@nuxt/kit": "workspace:*",
"@nuxt/schema": "workspace:*",
"@nuxt/telemetry": "^2.5.4",
@ -68,7 +68,7 @@
"@unhead/dom": "^1.9.14",
"@unhead/ssr": "^1.9.14",
"@unhead/vue": "^1.9.14",
"@vue/shared": "^3.4.29",
"@vue/shared": "^3.4.30",
"acorn": "8.12.0",
"c12": "^1.11.1",
"chokidar": "^3.6.0",
@ -79,9 +79,8 @@
"esbuild": "^0.21.5",
"escape-string-regexp": "^5.0.0",
"estree-walker": "^3.0.3",
"fs-extra": "^11.2.0",
"globby": "^14.0.1",
"h3": "^1.12.0",
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
"hookable": "^5.5.3",
"ignore": "^5.3.1",
"jiti": "^1.21.6",
@ -89,7 +88,7 @@
"knitwork": "^1.1.0",
"magic-string": "^0.30.10",
"mlly": "^1.7.1",
"nitropack": "^2.9.6",
"nitro": "npm:nitro-nightly@3.0.0-beta-28648657.9a717203",
"nuxi": "^3.12.0",
"nypm": "^0.3.8",
"ofetch": "^1.3.4",
@ -109,22 +108,21 @@
"unenv": "^1.9.0",
"unimport": "^3.7.2",
"unplugin": "^1.10.1",
"unplugin-vue-router": "^0.7.0",
"unplugin-vue-router": "^0.10.0",
"unstorage": "^1.10.2",
"untyped": "^1.4.2",
"vue": "^3.4.29",
"vue": "^3.4.30",
"vue-bundle-renderer": "^2.1.0",
"vue-devtools-stub": "^0.1.0",
"vue-router": "^4.3.3"
"vue-router": "^4.4.0"
},
"devDependencies": {
"@nuxt/scripts": "0.5.1",
"@nuxt/ui-templates": "1.3.4",
"@parcel/watcher": "2.4.1",
"@types/estree": "1.0.5",
"@types/fs-extra": "11.0.4",
"@vitejs/plugin-vue": "5.0.4",
"@vue/compiler-sfc": "3.4.29",
"@vue/compiler-sfc": "3.4.30",
"unbuild": "latest",
"vite": "5.3.1",
"vitest": "1.6.0"

View File

@ -7,7 +7,7 @@ import type {
VNodeProps,
} from 'vue'
import { computed, defineComponent, h, inject, onBeforeUnmount, onMounted, provide, ref, resolveComponent } from 'vue'
import type { RouteLocation, RouteLocationRaw, Router, RouterLink, RouterLinkProps, useLink } from '#vue-router'
import type { RouteLocation, RouteLocationRaw, Router, RouterLink, RouterLinkProps, useLink } from 'vue-router'
import { hasProtocol, joinURL, parseQuery, withTrailingSlash, withoutTrailingSlash } from 'ufo'
import { preloadRouteComponents } from '../composables/preload'
import { onNuxtReady } from '../composables/ready'
@ -168,8 +168,10 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
if (!to.value || isAbsoluteUrl.value) { return to.value as string }
if (isExternal.value) {
const path = typeof to.value === 'object' ? resolveRouteObject(to.value) : to.value
return resolveTrailingSlashBehavior(path, router.resolve /* will not be called */) as string
const path = typeof to.value === 'object' && 'path' in to.value ? resolveRouteObject(to.value) : to.value
// separately resolve route objects with a 'name' property and without 'path'
const href = typeof path === 'object' ? router.resolve(path).href : path
return resolveTrailingSlashBehavior(href, router.resolve /* will not be called */) as string
}
if (typeof to.value === 'object') {

View File

@ -1,6 +1,6 @@
import { defineComponent, h, nextTick, onMounted, provide, shallowReactive } from 'vue'
import type { Ref, VNode } from 'vue'
import type { RouteLocation, RouteLocationNormalizedLoaded } from '#vue-router'
import type { RouteLocationNormalizedLoaded } from 'vue-router'
import { PageRouteSymbol } from './injections'
export const RouteProvider = defineComponent({
@ -23,7 +23,7 @@ export const RouteProvider = defineComponent({
const previousRoute = props.route
// Provide a reactive route within the page
const route = {} as RouteLocation
const route = {} as RouteLocationNormalizedLoaded
for (const key in props.route) {
Object.defineProperty(route, key, {
get: () => previousKey === props.renderKey ? props.route[key as keyof RouteLocationNormalizedLoaded] : previousRoute[key as keyof RouteLocationNormalizedLoaded],

View File

@ -2,7 +2,7 @@ import { h } from 'vue'
import type { Component, RendererNode } from 'vue'
// eslint-disable-next-line
import { isString, isPromise, isArray, isObject } from '@vue/shared'
import type { RouteLocationNormalized } from '#vue-router'
import type { RouteLocationNormalized } from 'vue-router'
// @ts-expect-error virtual file
import { START_LOCATION } from '#build/pages'

View File

@ -1,5 +1,5 @@
import type { FetchError, FetchOptions } from 'ofetch'
import type { NitroFetchRequest, TypedInternalResponse, AvailableRouterMethod as _AvailableRouterMethod } from 'nitropack'
import type { NitroFetchRequest, TypedInternalResponse, AvailableRouterMethod as _AvailableRouterMethod } from 'nitro/types'
import type { MaybeRef, Ref } from 'vue'
import { computed, reactive, toValue } from 'vue'
import { hash } from 'ohash'

View File

@ -1,5 +1,5 @@
import type { Component } from 'vue'
import type { RouteLocationRaw, Router } from '#vue-router'
import type { RouteLocationRaw, Router } from 'vue-router'
import { useNuxtApp } from '../nuxt'
import { toArray } from '../utils'
import { useRouter } from './router'

View File

@ -1,7 +1,7 @@
import type { Ref } from 'vue'
import { getCurrentScope, onScopeDispose, ref } from 'vue'
import { injectHead } from '@unhead/vue'
import { useNuxtApp } from '#app'
import { useNuxtApp } from '../nuxt'
export type Politeness = 'assertive' | 'polite' | 'off'

View File

@ -1,6 +1,6 @@
import { getCurrentInstance, hasInjectionContext, inject, onScopeDispose } from 'vue'
import type { Ref } from 'vue'
import type { NavigationFailure, NavigationGuard, RouteLocationNormalized, RouteLocationRaw, Router, useRoute as _useRoute, useRouter as _useRouter } from '#vue-router'
import type { NavigationFailure, NavigationGuard, RouteLocationNormalized, RouteLocationRaw, Router, useRoute as _useRoute, useRouter as _useRouter } from 'vue-router'
import { sanitizeStatusCode } from 'h3'
import { hasProtocol, isScriptProtocol, joinURL, withQuery } from 'ufo'
@ -135,7 +135,8 @@ export const navigateTo = (to: RouteLocationRaw | undefined | null, options?: Na
return Promise.resolve()
}
const isExternal = options?.external || hasProtocol(toPath, { acceptRelative: true })
const isExternalHost = hasProtocol(toPath, { acceptRelative: true })
const isExternal = options?.external || isExternalHost
if (isExternal) {
if (!options?.external) {
throw new Error('Navigating to an external URL is not allowed by default. Use `navigateTo(url, { external: true })`.')
@ -166,10 +167,12 @@ export const navigateTo = (to: RouteLocationRaw | undefined | null, options?: Na
// TODO: consider deprecating in favour of `app:rendered` and removing
await nuxtApp.callHook('app:redirected')
const encodedLoc = location.replace(/"/g, '%22')
const encodedHeader = encodeURL(location, isExternalHost)
nuxtApp.ssrContext!._renderResponse = {
statusCode: sanitizeStatusCode(options?.redirectCode || 302, 302),
body: `<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=${encodedLoc}"></head></html>`,
headers: { location: encodeURI(location) },
headers: { location: encodedHeader },
}
return response
}
@ -259,3 +262,17 @@ export const setPageLayout = (layout: unknown extends PageMeta['layout'] ? strin
export function resolveRouteObject (to: Exclude<RouteLocationRaw, string>) {
return withQuery(to.path || '', to.query || {}) + (to.hash || '')
}
/**
* @internal
*/
export function encodeURL (location: string, isExternalHost = false) {
const url = new URL(location, 'http://localhost')
if (!isExternalHost) {
return url.pathname + url.search + url.hash
}
if (location.startsWith('//')) {
return url.toString().replace(url.protocol, '')
}
return url.toString()
}

View File

@ -1,13 +1,13 @@
import { effectScope, getCurrentInstance, getCurrentScope, hasInjectionContext, reactive, shallowReactive } from 'vue'
import type { App, EffectScope, Ref, VNode, onErrorCaptured } from 'vue'
import type { RouteLocationNormalizedLoaded } from '#vue-router'
import type { RouteLocationNormalizedLoaded } from 'vue-router'
import type { HookCallback, Hookable } from 'hookable'
import { createHooks } from 'hookable'
import { getContext } from 'unctx'
import type { SSRContext, createRenderer } from 'vue-bundle-renderer/runtime'
import type { EventHandlerRequest, H3Event } from 'h3'
import type { AppConfig, AppConfigInput, RuntimeConfig } from 'nuxt/schema'
import type { RenderResponse } from 'nitropack'
import type { RenderResponse } from 'nitro/types'
import type { LogObject } from 'consola'
import type { MergeHead, VueHeadClient } from '@unhead/vue'

View File

@ -49,7 +49,6 @@ export default defineNuxtPlugin({
definePayloadReviver(reviver, revivers[reviver as keyof typeof revivers])
}
Object.assign(nuxtApp.payload, await nuxtApp.runWithContext(getNuxtClientPayload))
// For backwards compatibility - TODO: remove later
window.__NUXT__ = nuxtApp.payload
delete window.__NUXT__
},
})

View File

@ -226,6 +226,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>({
})
}
// @ts-expect-error vue-router types diverge from our Route type above
nuxtApp._route = route
// Handle middleware

View File

@ -43,10 +43,11 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => {
const s = new MagicString(code)
const nuxt = tryUseNuxt()
// replace `_resolveComponent("...")` to direct import
s.replace(/(?<=[ (])_?resolveComponent\(\s*["'](lazy-|Lazy)?(Idle|Visible|idle-|visible-|Event|event-)?([^'"]*)["'][^)]*\)/g, (full: string, lazy: string, modifier: string, name: string) => {
s.replace(/(?<=[ (])_?resolveComponent\(\s*["'](lazy-|Lazy(?=[A-Z]))?(Idle|Visible|idle-|visible-|Event|event-)?([^'"]*)["'][^)]*\)/g, (full: string, lazy: string, modifier: string, name: string) => {
const normalComponent = findComponent(components, name, options.mode)
const modifierComponent = !normalComponent && modifier ? findComponent(components, modifier + name, options.mode) : null
const component = normalComponent || modifierComponent
if (component) {
// @ts-expect-error TODO: refactor to nuxi
if (component._internal_install && nuxt?.options.test === false) {

View File

@ -96,6 +96,10 @@ export async function scanComponents (dirs: ComponentsDir[], srcDir: string): Pr
const componentNameSegments = resolveComponentNameSegments(fileName.replace(/["']/g, ''), prefixParts)
const pascalName = pascalCase(componentNameSegments)
if (LAZY_COMPONENT_NAME_REGEX.test(pascalName)) {
logger.warn(`The component \`${pascalName}\` (in \`${filePath}\`) is using the reserved "Lazy" prefix used for dynamic imports, which may cause it to break at runtime.`)
}
if (resolvedNames.has(pascalName + suffix) || resolvedNames.has(pascalName)) {
warnAboutDuplicateComponent(pascalName, filePath, resolvedNames.get(pascalName) || resolvedNames.get(pascalName + suffix)!)
continue
@ -166,3 +170,5 @@ function warnAboutDuplicateComponent (componentName: string, filePath: string, d
`\n - ${duplicatePath}`,
)
}
const LAZY_COMPONENT_NAME_REGEX = /^Lazy(?=[A-Z])/

View File

@ -112,7 +112,7 @@ export const componentsTypeTemplate = {
]
})
const islandType = 'type IslandComponent<T extends DefineComponent> = T & DefineComponent<{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, SlotsType<{ fallback: { error: unknown } }>>'
const islandType = 'type IslandComponent<T extends DefineComponent> = T & DefineComponent<{}, {refresh: () => Promise<void>}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, SlotsType<{ fallback: { error: unknown } }>>'
return `
import type { DefineComponent, SlotsType } from 'vue'
${nuxt.options.experimental.componentIslands ? islandType : ''}

View File

@ -4,12 +4,11 @@ import { cpus } from 'node:os'
import { join, relative, resolve } from 'pathe'
import { createRouter as createRadixRouter, exportMatcher, toRouteMatcher } from 'radix3'
import { joinURL, withTrailingSlash } from 'ufo'
import { build, copyPublicAssets, createDevServer, createNitro, prepare, prerender, scanHandlers, writeTypes } from 'nitropack'
import type { Nitro, NitroConfig, NitroOptions } from 'nitropack'
import { build, copyPublicAssets, createDevServer, createNitro, prepare, prerender, writeTypes } from 'nitro'
import type { Nitro, NitroConfig, NitroOptions } from 'nitro/types'
import { findPath, logger, resolveAlias, resolveIgnorePatterns, resolveNuxtModule, resolvePath } from '@nuxt/kit'
import escapeRE from 'escape-string-regexp'
import { defu } from 'defu'
import fsExtra from 'fs-extra'
import { dynamicEventHandler } from 'h3'
import { isWindows } from 'std-env'
import type { Nuxt, NuxtOptions } from 'nuxt/schema'
@ -96,22 +95,18 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
: false,
scanDirs: nuxt.options._layers.map(layer => (layer.config.serverDir || layer.config.srcDir) && resolve(layer.cwd, layer.config.serverDir || resolve(layer.config.srcDir, 'server'))).filter(Boolean),
renderer: resolve(distDir, 'core/runtime/nitro/renderer'),
errorHandler: resolve(distDir, 'core/runtime/nitro/error'),
nodeModulesDirs: nuxt.options.modulesDir,
handlers: nuxt.options.serverHandlers,
devHandlers: [],
baseURL: nuxt.options.app.baseURL,
virtual: {
'#internal/nuxt.config.mjs': () => nuxt.vfs['#build/nuxt.config'],
'#internal/nuxt/app-config': () => nuxt.vfs['#build/app.config'].replace(/\/\*\* client \*\*\/[\s\S]*\/\*\* client-end \*\*\//, ''),
'#spa-template': async () => `export const template = ${JSON.stringify(await spaLoadingTemplate(nuxt))}`,
},
routeRules: {
'/__nuxt_error': { cache: false },
},
appConfig: nuxt.options.appConfig,
appConfigFiles: nuxt.options._layers.map(
layer => resolve(layer.config.srcDir, 'app.config'),
),
typescript: {
strict: true,
generateTsConfig: true,
@ -214,6 +209,19 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
logLevel: logLevelMapReverse[nuxt.options.logLevel],
} satisfies NitroConfig)
if (nuxt.options.experimental.serverAppConfig && nitroConfig.imports) {
nitroConfig.imports.imports ||= []
nitroConfig.imports.imports.push({
name: 'useAppConfig',
from: resolve(distDir, 'core/runtime/nitro/app-config'),
})
}
// add error handler
if (!nitroConfig.errorHandler && (nuxt.options.dev || !nuxt.options.experimental.noVueServer)) {
nitroConfig.errorHandler = resolve(distDir, 'core/runtime/nitro/error')
}
// Resolve user-provided paths
nitroConfig.srcDir = resolve(nuxt.options.rootDir, nuxt.options.srcDir, nitroConfig.srcDir!)
nitroConfig.ignore = [...(nitroConfig.ignore || []), ...resolveIgnorePatterns(nitroConfig.srcDir), `!${join(nuxt.options.buildDir, 'dist/client', nuxt.options.app.buildAssetsDir, '**/*')}`]
@ -359,7 +367,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
await nuxt.callHook('nitro:config', nitroConfig)
// TODO: extract to shared utility?
const excludedAlias = [/^@vue\/.*$/, '#imports', '#vue-router', 'vue-demi', /^#app/]
const excludedAlias = [/^@vue\/.*$/, '#imports', 'vue-demi', /^#app/]
const basePath = nitroConfig.typescript!.tsConfig!.compilerOptions?.baseUrl ? resolve(nuxt.options.buildDir, nitroConfig.typescript!.tsConfig!.compilerOptions?.baseUrl) : nuxt.options.buildDir
const aliases = nitroConfig.alias!
const tsConfig = nitroConfig.typescript!.tsConfig!
@ -384,7 +392,6 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
// Init nitro
const nitro = await createNitro(nitroConfig, {
// @ts-expect-error this will be valid in a future version of Nitro
compatibilityDate: nuxt.options.compatibilityDate,
})
@ -464,21 +471,22 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
if (!nuxt.options.dev && nuxt.options.experimental.noVueServer) {
nitro.hooks.hook('rollup:before', (nitro) => {
if (nitro.options.preset === 'nitro-prerender') { return }
if (nitro.options.preset === 'nitro-prerender') {
nitro.options.errorHandler = resolve(distDir, 'core/runtime/nitro/error')
return
}
const nuxtErrorHandler = nitro.options.handlers.findIndex(h => h.route === '/__nuxt_error')
if (nuxtErrorHandler >= 0) {
nitro.options.handlers.splice(nuxtErrorHandler, 1)
}
nitro.options.renderer = undefined
nitro.options.errorHandler = '#internal/nitro/error'
})
}
// Add typed route responses
nuxt.hook('prepare:types', async (opts) => {
if (!nuxt.options.dev) {
await scanHandlers(nitro)
await writeTypes(nitro)
}
// Exclude nitro output dir from typescript
@ -534,7 +542,6 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
// nuxt dev
if (nuxt.options.dev) {
nuxt.hook('webpack:compile', ({ compiler }) => { compiler.outputFileSystem = { ...fsExtra, join } as any })
nuxt.hook('webpack:compiled', () => { nuxt.server.reload() })
nuxt.hook('vite:compiled', () => { nuxt.server.reload() })

View File

@ -1,3 +1,5 @@
import { existsSync } from 'node:fs'
import { rm } from 'node:fs/promises'
import { dirname, join, normalize, relative, resolve } from 'pathe'
import { createDebugger, createHooks } from 'hookable'
import ignore from 'ignore'
@ -10,7 +12,6 @@ import { readPackageJSON, resolvePackageJSON } from 'pkg-types'
import { hash } from 'ohash'
import escapeRE from 'escape-string-regexp'
import fse from 'fs-extra'
import { withTrailingSlash, withoutLeadingSlash } from 'ufo'
import defu from 'defu'
@ -61,6 +62,7 @@ export function createNuxt (options: NuxtOptions): Nuxt {
const nightlies = {
'nitropack': 'nitropack-nightly',
'nitro': 'nitro-nightly',
'h3': 'h3-nightly',
'nuxt': 'nuxt-nightly',
'@nuxt/schema': '@nuxt/schema-nightly',
@ -159,7 +161,7 @@ async function initNuxt (nuxt: Nuxt) {
for (const layer of nuxt.options._layers) {
const declaration = join(layer.cwd, 'index.d.ts')
if (fse.existsSync(declaration)) {
if (existsSync(declaration)) {
opts.references.push({ path: declaration })
}
}
@ -277,7 +279,7 @@ async function initNuxt (nuxt: Nuxt) {
nuxt.hook('build:manifest', async (manifest) => {
for (const file in manifest) {
if (manifest[file].resourceType === 'script') {
await fse.rm(resolve(nuxt.options.buildDir, 'dist/client', withoutLeadingSlash(nuxt.options.app.buildAssetsDir), manifest[file].file), { force: true })
await rm(resolve(nuxt.options.buildDir, 'dist/client', withoutLeadingSlash(nuxt.options.app.buildAssetsDir), manifest[file].file), { force: true })
manifest[file].file = ''
}
}
@ -305,7 +307,7 @@ async function initNuxt (nuxt: Nuxt) {
for (const _mod of nuxt.options.modules) {
const mod = Array.isArray(_mod) ? _mod[0] : _mod
if (typeof mod !== 'string') { continue }
const modPath = await resolvePath(resolveAlias(mod))
const modPath = await resolvePath(resolveAlias(mod), { fallbackToOriginal: true })
specifiedModules.add(modPath)
}
@ -706,9 +708,9 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
}
async function checkDependencyVersion (name: string, nuxtVersion: string): Promise<void> {
const path = await resolvePath(name).catch(() => null)
const path = await resolvePath(name, { fallbackToOriginal: true }).catch(() => null)
if (!path) { return }
if (!path || path === name) { return }
const { version } = await readPackageJSON(path)
if (version && gt(nuxtVersion, version)) {

View File

@ -34,7 +34,7 @@ export const nuxtImportProtections = (nuxt: { options: NuxtOptions }, options: {
])
}
for (const i of [/(^|node_modules\/)@nuxt\/(kit|test-utils)/, /(^|node_modules\/)nuxi/, /(^|node_modules\/)nuxt\/(config|kit|schema)/, 'nitropack']) {
for (const i of [/(^|node_modules\/)@nuxt\/(kit|test-utils)/, /(^|node_modules\/)nuxi/, /(^|node_modules\/)nitro(?:pack)?(?:-nightly)?(?:$|\/)(?!(?:dist\/)?runtime|types)/, /(^|node_modules\/)nuxt\/(config|kit|schema)/]) {
patterns.push([i, 'This module cannot be imported' + (options.isNitro ? ' in server runtime.' : ' in the Vue part of your app.')])
}

View File

@ -0,0 +1,38 @@
import type { H3Event } from 'h3'
import { klona } from 'klona'
// @ts-expect-error virtual file
import _inlineAppConfig from '#internal/nuxt/app-config'
// App config
const _sharedAppConfig = _deepFreeze(klona(_inlineAppConfig))
export function useAppConfig (event?: H3Event) {
// Backwards compatibility with ambient context
if (!event) {
return _sharedAppConfig
}
if (!event.context.nuxt) {
event.context.nuxt = {}
}
// Reuse cached app config from event context
if (event.context.nuxt.appConfig) {
return event.context.nuxt.appConfig
}
// Prepare app config for event context
const appConfig = klona(_inlineAppConfig)
event.context.nuxt.appConfig = appConfig
return appConfig
}
// --- Utils ---
function _deepFreeze (object: Record<string, any>) {
const propNames = Object.getOwnPropertyNames(object)
for (const name of propNames) {
const value = object[name]
if (value && typeof value === 'object') {
_deepFreeze(value)
}
}
return Object.freeze(object)
}

View File

@ -7,7 +7,7 @@ import { withTrailingSlash } from 'ufo'
import { getContext } from 'unctx'
import { isVNode } from 'vue'
import type { NitroApp } from '#internal/nitro/app'
import type { NitroApp } from 'nitro/types'
// @ts-expect-error virtual file
import { rootDir } from '#internal/dev-server-logs-options'

View File

@ -1,10 +1,8 @@
import { joinURL, withQuery } from 'ufo'
import type { NitroErrorHandler } from 'nitropack'
import type { H3Error } from 'h3'
import { getRequestHeaders, send, setResponseHeader, setResponseStatus } from 'h3'
import { useRuntimeConfig } from '#internal/nitro'
import { useNitroApp } from '#internal/nitro/app'
import { isJsonRequest, normalizeError } from '#internal/nitro/utils'
import type { NitroErrorHandler } from 'nitro/types'
import type { H3Error, H3Event } from 'h3'
import { getRequestHeader, getRequestHeaders, send, setResponseHeader, setResponseStatus } from 'h3'
import { useNitroApp, useRuntimeConfig } from 'nitro/runtime'
import type { NuxtPayload } from '#app'
export default <NitroErrorHandler> async function errorhandler (error: H3Error, event) {
@ -86,3 +84,65 @@ export default <NitroErrorHandler> async function errorhandler (error: H3Error,
return send(event, html)
}
/**
* Nitro internal functions extracted from https://github.com/unjs/nitro/blob/main/src/runtime/internal/utils.ts
*/
function isJsonRequest (event: H3Event) {
// If the client specifically requests HTML, then avoid classifying as JSON.
if (hasReqHeader(event, 'accept', 'text/html')) {
return false
}
return (
hasReqHeader(event, 'accept', 'application/json') ||
hasReqHeader(event, 'user-agent', 'curl/') ||
hasReqHeader(event, 'user-agent', 'httpie/') ||
hasReqHeader(event, 'sec-fetch-mode', 'cors') ||
event.path.startsWith('/api/') ||
event.path.endsWith('.json')
)
}
function hasReqHeader (event: H3Event, name: string, includes: string) {
const value = getRequestHeader(event, name)
return (
value && typeof value === 'string' && value.toLowerCase().includes(includes)
)
}
function normalizeError (error: any) {
// temp fix for https://github.com/unjs/nitro/issues/759
// TODO: investigate vercel-edge not using unenv pollyfill
const cwd = typeof process.cwd === 'function' ? process.cwd() : '/'
const stack = ((error.stack as string) || '')
.split('\n')
.splice(1)
.filter(line => line.includes('at '))
.map((line) => {
const text = line
.replace(cwd + '/', './')
.replace('webpack:/', '')
.replace('file://', '')
.trim()
return {
text,
internal:
(line.includes('node_modules') && !line.includes('.cache')) ||
line.includes('internal') ||
line.includes('new Promise'),
}
})
const statusCode = error.statusCode || 500
const statusMessage =
error.statusMessage ?? (statusCode === 404 ? 'Not Found' : '')
const message = error.message || error.toString()
return {
stack,
statusCode,
statusMessage,
message,
}
}

View File

@ -1,5 +1,5 @@
import { joinRelativeURL } from 'ufo'
import { useRuntimeConfig } from '#internal/nitro'
import { useRuntimeConfig } from 'nitro/runtime'
export function baseURL (): string {
// TODO: support passing event to `useRuntimeConfig`

View File

@ -6,7 +6,7 @@ import {
getRequestDependencies,
renderResourceHeaders,
} from 'vue-bundle-renderer/runtime'
import type { RenderResponse } from 'nitropack'
import type { RenderResponse } from 'nitro/types'
import type { Manifest } from 'vite'
import type { H3Event } from 'h3'
import { appendResponseHeader, createError, getQuery, getResponseStatus, getResponseStatusText, readBody, writeEarlyHints } from 'h3'
@ -21,8 +21,7 @@ import type { HeadEntryOptions } from '@unhead/schema'
import type { Link, Script, Style } from '@unhead/vue'
import { createServerHead } from '@unhead/vue'
import { defineRenderHandler, getRouteRules, useRuntimeConfig, useStorage } from '#internal/nitro'
import { useNitroApp } from '#internal/nitro/app'
import { defineRenderHandler, getRouteRules, useNitroApp, useRuntimeConfig, useStorage } from 'nitro/runtime'
// @ts-expect-error virtual file
import unheadPlugins from '#internal/unhead-plugins.mjs'

View File

@ -239,7 +239,28 @@ import type { H3Event } from 'h3'
import type { LogObject } from 'consola'
import type { NuxtIslandContext, NuxtIslandResponse, NuxtRenderHTMLContext } from 'nuxt/app'
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<string, boolean>
}
interface NitroRuntimeHooks {
'dev:ssr-logs': (ctx: { logs: LogObject[], path: string }) => void | Promise<void>
'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event }) => void | Promise<void>
'render:island': (islandResponse: NuxtIslandResponse, context: { event: H3Event, islandContext: NuxtIslandContext }) => void | Promise<void>
}
}
declare module 'nitropack/types' {
interface NitroRuntimeConfigApp {
buildAssetsDir: string
cdnURL: string
@ -267,7 +288,7 @@ declare module 'nitropack' {
export const clientConfigTemplate: NuxtTemplate = {
filename: 'nitro.client.mjs',
getContents: () => `
export const useRuntimeConfig = () => window?.__NUXT__?.config || {}
export const useRuntimeConfig = () => window?.__NUXT__?.config || window?.useNuxtApp?.().payload?.config || {}
`,
}
@ -315,17 +336,19 @@ export const appConfigTemplate: NuxtTemplate = {
write: true,
getContents ({ app, nuxt }) {
return `
import { updateAppConfig } from '#app/config'
import { defuFn } from 'defu'
const inlineConfig = ${JSON.stringify(nuxt.options.appConfig, null, 2)}
/** client **/
// Vite - webpack is handled directly in #app/config
if (import.meta.hot) {
if (import.meta.dev && !import.meta.nitro && import.meta.hot) {
const { updateAppConfig } = await import('#app/config')
import.meta.hot.accept((newModule) => {
updateAppConfig(newModule.default)
})
}
/** client-end **/
${app.configs.map((id: string, index: number) => `import ${`cfg${index}`} from ${JSON.stringify(id)}`).join('\n')}
@ -339,7 +362,7 @@ export const publicPathTemplate: NuxtTemplate = {
getContents ({ nuxt }) {
return [
'import { joinRelativeURL } from \'ufo\'',
!nuxt.options.dev && 'import { useRuntimeConfig } from \'#internal/nitro\'',
!nuxt.options.dev && 'import { useRuntimeConfig } from \'nitro/runtime\'',
nuxt.options.dev
? `const appConfig = ${JSON.stringify(nuxt.options.app)}`

View File

@ -8,7 +8,7 @@ import { createRoutesContext } from 'unplugin-vue-router'
import { resolveOptions } from 'unplugin-vue-router/options'
import type { EditableTreeNode, Options as TypedRouterOptions } from 'unplugin-vue-router'
import type { NitroRouteConfig } from 'nitropack'
import type { NitroRouteConfig } from 'nitro/types'
import { defu } from 'defu'
import { distDir } from '../dirs'
import { normalizeRoutes, resolvePagesRoutes, resolveRoutePaths } from './utils'
@ -105,14 +105,6 @@ export default defineNuxtModule({
}
})
// adds support for #vue-router alias (used for types) with and without pages integration
addTypeTemplate({
filename: 'vue-router-stub.d.ts',
getContents: () => `export * from '${useExperimentalTypedPages ? 'vue-router/auto' : 'vue-router'}'`,
})
nuxt.options.alias['#vue-router'] = join(nuxt.options.buildDir, 'vue-router-stub')
if (!nuxt.options.pages) {
addPlugin(resolve(distDir, 'app/plugins/router'))
addTemplate({
@ -125,7 +117,12 @@ export default defineNuxtModule({
addTypeTemplate({
filename: 'types/middleware.d.ts',
getContents: () => [
'declare module \'nitropack\' {',
'declare module \'nitropack/types\' {',
' interface NitroRouteConfig {',
' appMiddleware?: string | string[] | Record<string, boolean>',
' }',
'}',
'declare module \'nitro/types\' {',
' interface NitroRouteConfig {',
' appMiddleware?: string | string[] | Record<string, boolean>',
' }',
@ -141,12 +138,6 @@ export default defineNuxtModule({
return
}
addTemplate({
filename: 'vue-router-stub.mjs',
// TODO: use `vue-router/auto` when we have support for page metadata
getContents: () => 'export * from \'vue-router\';',
})
if (useExperimentalTypedPages) {
const declarationFile = './types/typed-router.d.ts'
@ -189,6 +180,7 @@ export default defineNuxtModule({
nuxt.hook('prepare:types', ({ references }) => {
// This file will be generated by unplugin-vue-router
references.push({ path: declarationFile })
references.push({ types: 'unplugin-vue-router/client' })
})
const context = createRoutesContext(resolveOptions(options))
@ -215,15 +207,7 @@ export default defineNuxtModule({
// Add $router types
nuxt.hook('prepare:types', ({ references }) => {
references.push({ types: useExperimentalTypedPages ? 'vue-router/auto' : 'vue-router' })
})
// Add vue-router route guard imports
nuxt.hook('imports:sources', (sources) => {
const routerImports = sources.find(s => s.from === '#app/composables/router' && s.imports.includes('onBeforeRouteLeave'))
if (routerImports) {
routerImports.from = '#vue-router'
}
references.push({ types: useExperimentalTypedPages ? 'vue-router/auto-routes' : 'vue-router' })
})
// Regenerate templates when adding or removing pages
@ -278,7 +262,7 @@ export default defineNuxtModule({
nuxt.hook('imports:extend', (imports) => {
imports.push(
{ name: 'definePageMeta', as: 'definePageMeta', from: resolve(runtimeDir, 'composables') },
{ name: 'useLink', as: 'useLink', from: '#vue-router' },
{ name: 'useLink', as: 'useLink', from: 'vue-router' },
)
if (nuxt.options.experimental.inlineRouteRules) {
imports.push({ name: 'defineRouteRules', as: 'defineRouteRules', from: resolve(runtimeDir, 'composables') })
@ -463,7 +447,12 @@ export default defineNuxtModule({
' middleware?: MiddlewareKey | NavigationGuard | Array<MiddlewareKey | NavigationGuard>',
' }',
'}',
'declare module \'nitropack\' {',
'declare module \'nitropack/types\' {',
' interface NitroRouteConfig {',
' appMiddleware?: MiddlewareKey | MiddlewareKey[] | Record<MiddlewareKey, boolean>',
' }',
'}',
'declare module \'nitro/types\' {',
' interface NitroRouteConfig {',
' appMiddleware?: MiddlewareKey | MiddlewareKey[] | Record<MiddlewareKey, boolean>',
' }',

View File

@ -5,7 +5,7 @@ import { walk } from 'estree-walker'
import { transform } from 'esbuild'
import { parse } from 'acorn'
import type { NuxtPage } from '@nuxt/schema'
import type { NitroRouteConfig } from 'nitropack'
import type { NitroRouteConfig } from 'nitro/types'
import { normalize } from 'pathe'
import { extractScriptContent, pathToNitroGlob } from './utils'

View File

@ -1,8 +1,8 @@
import type { KeepAliveProps, TransitionProps, UnwrapRef } from 'vue'
import { getCurrentInstance } from 'vue'
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteRecordRedirectOption } from '#vue-router'
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteRecordRedirectOption } from 'vue-router'
import { useRoute } from 'vue-router'
import type { NitroRouteConfig } from 'nitropack'
import type { NitroRouteConfig } from 'nitro/types'
import { useNuxtApp } from '#app/nuxt'
import type { NuxtError } from '#app'

View File

@ -1,8 +1,8 @@
import { Fragment, Suspense, Transition, defineComponent, h, inject, nextTick, ref, watch } from 'vue'
import type { KeepAliveProps, TransitionProps, VNode } from 'vue'
import { RouterView } from '#vue-router'
import { RouterView } from 'vue-router'
import { defu } from 'defu'
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded } from '#vue-router'
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded } from 'vue-router'
import { generateRouteKey, toArray, wrapInKeepAlive } from './utils'
import type { RouterViewSlotProps } from './utils'

View File

@ -1,13 +1,7 @@
import { isReadonly, reactive, shallowReactive, shallowRef } from 'vue'
import type { Ref } from 'vue'
import type { RouteLocation, Router, RouterScrollBehavior } from '#vue-router'
import {
START_LOCATION,
createMemoryHistory,
createRouter,
createWebHashHistory,
createWebHistory,
} from '#vue-router'
import type { RouteLocation, RouteLocationNormalizedLoaded, Router, RouterScrollBehavior } from 'vue-router'
import { START_LOCATION, createMemoryHistory, createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
import { createError } from 'h3'
import { isEqual, withoutBase } from 'ufo'
@ -124,7 +118,7 @@ const plugin: Plugin<{ router: Router }> = defineNuxtPlugin({
})
// https://github.com/vuejs/router/blob/main/packages/router/src/router.ts#L1225-L1233
const route = {} as RouteLocation
const route = {} as RouteLocationNormalizedLoaded
for (const key in _route.value) {
Object.defineProperty(route, key, {
get: () => _route.value[key as keyof RouteLocation],

View File

@ -1,4 +1,4 @@
import type { RouteLocationNormalized, RouterScrollBehavior } from '#vue-router'
import type { RouteLocationNormalized, RouterScrollBehavior } from 'vue-router'
import type { RouterConfig } from 'nuxt/schema'
import { useNuxtApp } from '#app/nuxt'
import { isChangingPage } from '#app/components/utils'

View File

@ -1,5 +1,5 @@
import { KeepAlive, h } from 'vue'
import type { RouteLocationMatched, RouteLocationNormalizedLoaded, RouterView } from '#vue-router'
import type { RouteLocationMatched, RouteLocationNormalizedLoaded, RouterView } from 'vue-router'
type InstanceOf<T> = T extends new (...args: any[]) => infer R ? R : never
type RouterViewSlot = Exclude<InstanceOf<typeof RouterView>['$slots']['default'], undefined>

View File

@ -256,13 +256,19 @@ export async function getRouteMeta (contents: string, absolutePath: string): Pro
extractedMeta[key] = property.value.value
}
const extraneousMetaKeys = pageMetaArgument.properties
.filter(property => property.type === 'Property' && property.key.type === 'Identifier' && !(extractionKeys as unknown as string[]).includes(property.key.name))
// @ts-expect-error inferred types have been filtered out
.map(property => property.key.name)
if (extraneousMetaKeys.length) {
dynamicProperties.add('meta')
for (const property of pageMetaArgument.properties) {
if (property.type !== 'Property') {
continue
}
const isIdentifierOrLiteral = property.key.type === 'Literal' || property.key.type === 'Identifier'
if (!isIdentifierOrLiteral) {
continue
}
const name = property.key.type === 'Identifier' ? property.key.name : String(property.value)
if (!(extractionKeys as unknown as string[]).includes(name)) {
dynamicProperties.add('meta')
break
}
}
if (dynamicProperties.size) {

View File

@ -56,8 +56,9 @@ describe('dependency mismatch', () => {
cwd: repoRoot,
})
// @nuxt/kit is explicitly installed in repo root but @nuxt/schema isn't, so we only
// get warnings about @nuxt/schema
expect(console.warn).toHaveBeenCalledWith(`[nuxt] Expected \`@nuxt/kit\` to be at least \`${version}\` but got \`3.0.0\`. This might lead to unexpected behavior. Check your package.json or refresh your lockfile.`)
expect(console.warn).toHaveBeenCalledWith(`[nuxt] Expected \`@nuxt/schema\` to be at least \`${version}\` but got \`3.0.0\`. This might lead to unexpected behavior. Check your package.json or refresh your lockfile.`)
vi.mocked(readPackageJSON).mockRestore()
await nuxt.close()

View File

@ -30,17 +30,16 @@ vi.mock('../src/app/composables/router', () => ({
return withQuery(to.path || '', to.query || {}) + (to.hash || '')
},
useRouter: () => ({
resolve: (route: string | RouteLocation & { to?: string }): Partial<RouteLocation> & { href?: string } => {
resolve: (route: string | RouteLocation): Partial<RouteLocation> & { href: string } => {
if (typeof route === 'string') {
return { href: route, path: route }
return { path: route, href: route }
}
return {
path: route.path || `/${route.name?.toString()}`,
query: route.query || undefined,
hash: route.hash || undefined,
href: route.path || `/${route.name?.toString()}`,
}
return route.to
? { href: route.to }
: {
path: route.path || `/${route.name?.toString()}` || undefined,
query: route.query || undefined,
hash: route.hash || undefined,
}
},
}),
}))
@ -133,6 +132,10 @@ describe('nuxt-link:propsOrAttributes', () => {
expect(nuxtLink({ to: { path: '/to' }, external: true }).props.href).toBe('/to')
})
it('resolves route location object with name', () => {
expect(nuxtLink({ to: { name: 'to' }, external: true }).props.href).toBe('/to')
})
it('applies trailing slash behaviour', () => {
expect(nuxtLink({ to: { path: '/to' }, external: true }, { trailingSlash: 'append' }).props.href).toBe('/to/')
expect(nuxtLink({ to: '/to', external: true }, { trailingSlash: 'append' }).props.href).toBe('/to/')
@ -225,6 +228,7 @@ describe('nuxt-link:propsOrAttributes', () => {
it('forwards `to` prop', () => {
expect(nuxtLink({ to: '/to' }).props.to).toBe('/to')
expect(nuxtLink({ to: { path: '/to' } }).props.to).toEqual({ path: '/to' })
expect(nuxtLink({ to: { name: 'to' } }).props.to).toEqual({ name: 'to' })
})
})

View File

@ -75,6 +75,28 @@ describe('page metadata', () => {
}
`)
})
it('should extract serialisable metadata all quoted', async () => {
const meta = await getRouteMeta(`
<script setup>
definePageMeta({
"otherValue": {
foo: 'bar',
},
})
</script>
`, filePath)
expect(meta).toMatchInlineSnapshot(`
{
"meta": {
"__nuxt_dynamic_meta_key": Set {
"meta",
},
},
}
`)
})
})
describe('normalizeRoutes', () => {

View File

@ -1,4 +1,4 @@
/// <reference types="nitropack" />
/// <reference types="nitro/types" />
import type { DefineNuxtConfig } from 'nuxt/config'
import type { RuntimeConfig, SchemaDefinition } from 'nuxt/schema'
@ -14,7 +14,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<string, boolean>
}
interface NitroRuntimeHooks {
'dev:ssr-logs': (ctx: { logs: LogObject[], path: string }) => void | Promise<void>
'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event }) => void | Promise<void>
'render:island': (islandResponse: NuxtIslandResponse, context: { event: H3Event, islandContext: NuxtIslandContext }) => void | Promise<void>
}
}
declare module 'nitropack/types' {
interface NitroRuntimeConfigApp {
buildAssetsDir: string
cdnURL: string

View File

@ -1,4 +1,4 @@
/// <reference types="nitropack" />
/// <reference types="nitro/types" />
import type { DefineNuxtConfig } from 'nuxt/config'
import type { RuntimeConfig, SchemaDefinition } from 'nuxt/schema'
import type { H3Event } from 'h3'
@ -13,7 +13,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<string, boolean>
}
interface NitroRuntimeHooks {
'dev:ssr-logs': (ctx: { logs: LogObject[], path: string }) => void | Promise<void>
'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event }) => void | Promise<void>
'render:island': (islandResponse: NuxtIslandResponse, context: { event: H3Event, islandContext: NuxtIslandContext }) => void | Promise<void>
}
}
declare module 'nitropack/types' {
interface NitroRuntimeConfigApp {
buildAssetsDir: string
cdnURL: string

View File

@ -31,6 +31,7 @@ export default defineBuildConfig({
'vue',
'unctx',
'hookable',
'nitro',
'nitropack',
'webpack',
'webpack-bundle-analyzer',

View File

@ -42,23 +42,23 @@
"@unhead/schema": "1.9.14",
"@vitejs/plugin-vue": "5.0.4",
"@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",
"@vue/compiler-core": "3.4.30",
"@vue/compiler-sfc": "3.4.30",
"@vue/language-core": "2.0.22",
"c12": "1.11.1",
"esbuild-loader": "4.2.0",
"h3": "1.12.0",
"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-28648657.9a717203",
"ofetch": "1.3.4",
"unbuild": "latest",
"unctx": "2.3.1",
"unenv": "1.9.0",
"vite": "5.3.1",
"vue": "3.4.29",
"vue": "3.4.30",
"vue-bundle-renderer": "2.1.0",
"vue-loader": "17.4.2",
"vue-router": "4.3.3",
"vue-router": "4.4.0",
"webpack": "5.92.1",
"webpack-dev-middleware": "7.2.1"
},

View File

@ -106,6 +106,12 @@ export default defineUntypedSchema({
*/
externalVue: true,
/**
* Enable accessing `appConfig` from server routes.
*
* @deprecated This option is not recommended.
*/
serverAppConfig: false,
/**
* Emit `app:chunkError` hook when there is an error loading vite/webpack
* chunks.

View File

@ -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: [],
})

View File

@ -34,7 +34,7 @@ export default defineUntypedSchema({
$resolve: (val) => {
const defaults = [
// Nitro auto-imported/augmented dependencies
'nitropack',
'nitro/types',
'defu',
'h3',
'consola',
@ -47,7 +47,8 @@ export default defineUntypedSchema({
'@vue/compiler-sfc',
'@vue/runtime-dom',
'vue-router',
'vue-router/auto',
'vue-router/auto-routes',
'unplugin-vue-router/client',
'@nuxt/schema',
'nuxt',
]

View File

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

View File

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

View File

@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url'
import { promises as fsp } from 'node:fs'
import type { Plugin } from 'vite'
import genericMessages from '../templates/messages.json'
import { version } from '../../nuxt/package.json'
const templatesRoot = fileURLToPath(new URL('..', import.meta.url))
@ -31,6 +32,7 @@ export const DevRenderingPlugin = () => {
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()}`

View File

@ -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('</body></html>', '')
}
html = html.replace(/\{\{ version \}\}/g, version)
// Load messages
const messages = JSON.parse(readFileSync(r(`templates/${templateName}/messages.json`), 'utf-8'))
@ -136,9 +139,14 @@ export const RenderPlugin = () => {
}
return lastChar || ''
}).replace(/@media[^{]*\{\}/g, '')
const inlineScripts = Array.from(html.matchAll(/<script>([\s\S]*?)<\/script>/g))
.map(block => block[1])
.filter(i => !i.includes('const t=document.createElement("link")'))
const inlineScripts: string[] = []
for (const [_, i] of html.matchAll(/<script>([\s\S]*?)<\/script>/g)) {
if (i && !i.includes('const t=document.createElement("link")')) {
inlineScripts.push(i)
}
}
const props = genObjectFromRawEntries(Object.entries({ ...genericMessages, ...messages }).map(([key, value]) => [key, {
type: typeof value === 'string' ? 'String' : typeof value === 'number' ? 'Number' : typeof value === 'boolean' ? 'Boolean' : 'undefined',
default: JSON.stringify(value),

View File

@ -21,8 +21,8 @@
"devDependencies": {
"@types/html-minifier": "4.0.5",
"@unocss/reset": "0.61.0",
"critters": "0.0.22",
"execa": "9.2.0",
"critters": "0.0.24",
"execa": "9.3.0",
"globby": "14.0.1",
"html-minifier": "4.0.0",
"jiti": "1.21.6",

View File

@ -5,70 +5,14 @@
<meta charset="utf-8"/>
<meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0" name="viewport"/>
<script type="module" src="/styles.ts"></script>
<style>
.spotlight {
background: linear-gradient(45deg, #00DC82 0%, #36E4DA 50%, #0047E1 100%);
filter: blur(20vh);
height: 40vh;
bottom: -30vh;
}
.gradient-border {
position: relative;
border-radius: 0.5rem;
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
}
@media (prefers-color-scheme: light) {
.gradient-border {
background-color: rgba(255, 255, 255, 0.3);
}
.gradient-border::before {
background: linear-gradient(90deg, #e2e2e2 0%, #e2e2e2 25%, #00DC82 50%, #36E4DA 75%, #0047E1 100%);
}
}
@media (prefers-color-scheme: dark) {
.gradient-border {
background-color: rgba(20, 20, 20, 0.3);
}
.gradient-border::before {
background: linear-gradient(90deg, #303030 0%, #303030 25%, #00DC82 50%, #36E4DA 75%, #0047E1 100%);
}
}
.gradient-border::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 0.5rem;
padding: 2px;
width: 100%;
background-size: 400% auto;
opacity: 0.5;
transition: background-position 0.3s ease-in-out, opacity 0.2s ease-in-out;
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
.gradient-border:hover::before {
background-position: -50% 0;
opacity: 1;
}
</style>
</head>
<body class="font-sans antialiased bg-white dark:bg-black text-black dark:text-white grid min-h-screen place-content-center overflow-hidden ">
<div class="fixed left-0 right-0 spotlight z-10"></div>
<div class="max-w-520px text-center z-20">
<h1 class="text-8xl sm:text-10xl font-medium mb-8">{{ messages.statusCode }}</h1>
<p class="text-xl px-8 sm:px-0 sm:text-4xl font-light mb-16 leading-tight">{{ messages.description }}</p>
<body class="font-sans antialiased bg-white dark:bg-[#020420] text-[#020420] tracking-wide dark:text-white grid min-h-screen place-content-center overflow-hidden ">
<div class="max-w-520px text-center">
<h1 class="text-[80px] sm:text-[110px] font-semibold mb-4 tabular-nums leading-none">{{ messages.statusCode }}</h1>
<h2 class="text-2xl font-semibold sm:text-3xl mb-2">{{ messages.statusMessage }}</h2>
<p class="text-[#64748B] text-md mb-4 px-2">{{ messages.description }}</p>
<div class="w-full flex items-center justify-center">
<a href="/" class="gradient-border text-md sm:text-xl py-2 px-4 sm:py-3 sm:px-6 cursor-pointer">
<a href="/" class="underline underline-offset-3 text-sm font-medium hover:text-[#00DC82]">
{{ messages.backHome }}
</a>
</div>

View File

@ -1,6 +1,6 @@
{
"statusCode": 404,
"statusMessage": "Not Found",
"statusMessage": "Page not found",
"description": "Sorry, the page you are looking for could not be found.",
"backHome": "Go back home"
}

View File

@ -5,18 +5,12 @@
<meta charset="utf-8"/>
<meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0" name="viewport"/>
<script type="module" src="/styles.ts"></script>
<style>
.spotlight {
background: linear-gradient(45deg, #00DC82 0%, #36E4DA 50%, #0047E1 100%);
filter: blur(20vh);
}
</style>
</head>
<body class="font-sans antialiased bg-white dark:bg-black text-black dark:text-white grid min-h-screen place-content-center overflow-hidden ">
<div class="fixed -bottom-1/2 left-0 right-0 h-1/2 spotlight"></div>
<body class="font-sans antialiased bg-white dark:bg-[#020420] text-[#020420] tracking-wide dark:text-white grid min-h-screen place-content-center overflow-hidden ">
<div class="max-w-520px text-center">
<h1 class="text-8xl sm:text-10xl font-medium mb-8">{{ messages.statusCode }}</h1>
<p class="text-xl px-8 sm:px-0 sm:text-4xl font-light mb-16 leading-tight">{{ messages.description }}</p>
<h1 class="text-[80px] sm:text-[110px] font-semibold mb-4 tabular-nums leading-none">{{ messages.statusCode }}</h1>
<h2 class="text-2xl font-semibold sm:text-3xl mb-2">{{ messages.statusMessage }}</h2>
<p class="text-[#64748B] text-md mb-4 px-2">{{ messages.description }}</p>
</div>
</body>
</html>

View File

@ -1,5 +1,6 @@
{
"statusCode": 500,
"statusMessage": "Server error",
"description": "This page is temporarily unavailable."
"statusMessage": "Internal server error",
"description": "This page is temporarily unavailable.",
"refresh": "Refresh this page"
}

View File

@ -5,21 +5,12 @@
<meta charset="utf-8"/>
<meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0" name="viewport"/>
<script type="module" src="/styles.ts"></script>
<style>
.spotlight {
background: linear-gradient(45deg, #00DC82 0%, #36E4DA 50%, #0047E1 100%);
opacity: 0.8;
filter: blur(30vh);
height: 60vh;
bottom: -40vh;
}
</style>
</head>
<body class="font-sans antialiased bg-white px-10 pt-14 dark:bg-black text-black dark:text-white min-h-screen flex flex-col">
<div class="fixed left-0 right-0 spotlight"></div>
<h1 class="text-6xl sm:text-8xl font-medium mb-6">{{ messages.statusCode }}</h1>
<p class="text-xl sm:text-2xl font-light mb-8 leading-tight">{{ messages.description }}</p>
<div class="bg-white rounded-t-md bg-black/5 dark:bg-white/10 flex-1 overflow-y-auto h-auto">
<body class="font-sans antialiased bg-white px-10 pt-12 dark:bg-[#020420] text-black dark:text-white min-h-screen flex flex-col">
<h1 class="text-6xl sm:text-8xl font-medium mb-4">{{ messages.statusCode }}</h1>
<p class="text-xl sm:text-2xl mb-8 font-light leading-tight">{{ messages.description }}</p>
<a href="https://nuxt.com/docs/getting-started/error-handling?utm_source=nuxt-error-dev-page" target="_blank" class="absolute top-6 sm:right-6 mx-auto inline-block hover:underline underline-offset-3 text-sm font-medium hover:text-[#00DC82]">Customize this page</a>
<div class="rounded-t-md border border-b-0 border-black/5 bg-gray-50/50 dark:border-white/10 dark:bg-white/5 flex-1 overflow-y-auto h-auto">
<pre class="text-xl font-light leading-tight z-10 p-8">{{{ messages.stack }}}</pre>
</div>
</body>

View File

@ -1,6 +1,6 @@
{
"statusCode": 500,
"statusMessage": "Server error",
"description": "An error occurred in the application and the page could not be served. If you are the application owner, check your server logs for details.",
"description": "An error occurred in the application and the page could not be served.",
"stack": ""
}

View File

@ -7,59 +7,32 @@
<script type="module" src="/styles.ts"></script>
<style>
.nuxt-loader-bar {
background: repeating-linear-gradient(to right, #36E4DA 0%, #1DE0B1 25%, #00DC82 50%, #1DE0B1 75%, #36E4DA 100%);
height: 100px;
background-size: 200% auto;
background-position: 0 0;
animation: gradient 2s infinite;
animation-fill-mode: forwards;
animation-timing-function: linear;
background: #00DC82;
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 5px;
height: 3px;
}
.visual-effects .nuxt-loader-bar {
height: 100px;
bottom: -50px;
left: -50px;
right: -50px;
filter: blur(100px);
.triangle-loading {
position: absolute;
}
.visual-effects .mouse-gradient {
background: repeating-linear-gradient(to right, #00DC82 0%, #1DE0B1 50%, #36E4DA 100%);
filter: blur(100px);
opacity: 0.5;
.triangle-loading>path {
fill: none;
stroke-width: 4px;
stroke-linecap: round;
stroke-linejoin: round;
/* Stroke-dasharray property */
stroke-dasharray: 128;
stroke-dashoffset: 128;
animation: nuxt-loading-move 3s linear infinite;
}
#animation-toggle {
position: fixed;
padding: 10px;
top: 0;
right: 0;
transition: opacity 0.4s ease-in;
opacity: 0;
.nuxt-logo:hover .triangle-loading>path {
animation-play-state: paused;
}
#animation-toggle:hover {
opacity: 0.8;
}
@keyframes gradient {
0% {
background-position: 0 0;
}
@keyframes nuxt-loading-move {
100% {
background-position: -200% 0;
}
}
@keyframes width {
0% {
width: 100%;
}
50% {
width: 0;
}
100% {
width: 100%;
stroke-dashoffset: -128;
}
}
@media (prefers-color-scheme: dark) {
@ -67,106 +40,25 @@
color: white;
color-scheme: dark;
}
.nuxt-loader-bar {
opacity: 0.5;
}
}
</style>
</head>
<body class="visual-effects relative overflow-hidden min-h-screen bg-white dark:bg-black flex flex-col justify-center items-center text-center">
<div id="mouseLight" class="absolute top-0 rounded-full mouse-gradient transition-opacity h-[200px] w-[200px]"></div>
<a href="https://nuxt.com" target="_blank" rel="noopener" class="nuxt-logo z-20">
<svg id="nuxtImg" xmlns="http://www.w3.org/2000/svg" width="214" height="53" fill="none" viewBox="0 0 800 200">
<path fill="#00DC82" d="M168.303 200h111.522c3.543 0 7.022-.924 10.09-2.679A20.086 20.086 0 0 0 297.3 190a19.855 19.855 0 0 0 2.7-10.001 19.858 19.858 0 0 0-2.709-9.998L222.396 41.429a20.09 20.09 0 0 0-7.384-7.32 20.313 20.313 0 0 0-10.088-2.679c-3.541 0-7.02.925-10.087 2.68a20.082 20.082 0 0 0-7.384 7.32l-19.15 32.896L130.86 9.998a20.086 20.086 0 0 0-7.387-7.32A20.322 20.322 0 0 0 113.384 0c-3.542 0-7.022.924-10.09 2.679a20.091 20.091 0 0 0-7.387 7.319L2.709 170A19.853 19.853 0 0 0 0 179.999c-.002 3.511.93 6.96 2.7 10.001a20.091 20.091 0 0 0 7.385 7.321A20.322 20.322 0 0 0 20.175 200h70.004c27.737 0 48.192-12.075 62.266-35.633l34.171-58.652 18.303-31.389 54.93 94.285h-73.233L168.303 200Zm-79.265-31.421-48.854-.011 73.232-125.706 36.541 62.853-24.466 42.01c-9.347 15.285-19.965 20.854-36.453 20.854Z" />
<path fill="currentColor" d="M377 200a4 4 0 0 0 4-4v-93s5.244 8.286 15 25l38.707 66.961c1.789 3.119 5.084 5.039 8.649 5.039H470V50h-27a4 4 0 0 0-4 4v94l-17-30-36.588-62.98c-1.792-3.108-5.081-5.02-8.639-5.02H350v150h27ZM676.203 143.857 710.551 92h-25.73a9.972 9.972 0 0 0-8.333 4.522L660.757 120.5l-15.731-23.978A9.972 9.972 0 0 0 636.693 92h-25.527l34.348 51.643L608.524 200h24.966a9.969 9.969 0 0 0 8.29-4.458l19.18-28.756 18.981 28.72a9.968 9.968 0 0 0 8.313 4.494h24.736l-36.787-56.143ZM724.598 92h19.714V60.071h28.251V92H800v24.857h-27.437V159.5c0 10.5 5.284 15.429 14.43 15.429H800V200h-16.869c-23.576 0-38.819-14.143-38.819-39.214v-43.929h-19.714V92ZM590 92h-15c-3.489 0-6.218.145-8.5 2.523-2.282 2.246-2.5 3.63-2.5 7.066v52.486c0 8.058-.376 12.962-4 16.925-3.624 3.831-8.619 5-16 5-7.247 0-12.376-1.169-16-5-3.624-3.963-4-8.867-4-16.925v-52.486c0-3.435-.218-4.82-2.5-7.066C519.218 92.145 516.489 92 513 92h-15v62.422c0 14.004 3.892 25.101 11.676 33.292C517.594 195.905 529.103 200 544 200c14.897 0 26.204-4.095 34.123-12.286 7.918-8.191 11.877-19.288 11.877-33.292V92Z" />
</svg>
<body class="font-sans antialiased relative overflow-hidden min-h-screen bg-white dark:bg-[#020420] text-[#020420] dark:text-white flex flex-col justify-center items-center text-center">
<a href="https://nuxt.com/?utm_source=nuxt-loading-screen" target="_blank" rel="noopener" class="nuxt-logo group flex items-end gap-4" id="nuxtImg">
<div class="relative">
<svg class="triangle-loading text-[#00DC82]/80 group-hover:text-[#00DC82]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 37 25" fill="none" width="80">
<path stroke="currentColor" d="M24.236 22.006h10.742L25.563 5.822l-8.979 14.31a4 4 0 0 1-3.388 1.874H2.978l11.631-20 5.897 10.567" />
</svg>
<svg class="dark:text-gray-200 dark:group-hover:text-white text-[#020420]/80 group-hover:text-[#020420]" xmlns="http://www.w3.org/2000/svg" width="214" height="53" fill="none" viewBox="0 0 800 200">
<path fill="currentColor" d="M377 200a4 4 0 0 0 4-4v-93s5.244 8.286 15 25l38.707 66.961c1.789 3.119 5.084 5.039 8.649 5.039H470V50h-27a4 4 0 0 0-4 4v94l-17-30-36.588-62.98c-1.792-3.108-5.081-5.02-8.639-5.02H350v150h27ZM676.203 143.857 710.551 92h-25.73a9.972 9.972 0 0 0-8.333 4.522L660.757 120.5l-15.731-23.978A9.972 9.972 0 0 0 636.693 92h-25.527l34.348 51.643L608.524 200h24.966a9.969 9.969 0 0 0 8.29-4.458l19.18-28.756 18.981 28.72a9.968 9.968 0 0 0 8.313 4.494h24.736l-36.787-56.143ZM724.598 92h19.714V60.071h28.251V92H800v24.857h-27.437V159.5c0 10.5 5.284 15.429 14.43 15.429H800V200h-16.869c-23.576 0-38.819-14.143-38.819-39.214v-43.929h-19.714V92ZM590 92h-15c-3.489 0-6.218.145-8.5 2.523-2.282 2.246-2.5 3.63-2.5 7.066v52.486c0 8.058-.376 12.962-4 16.925-3.624 3.831-8.619 5-16 5-7.247 0-12.376-1.169-16-5-3.624-3.963-4-8.867-4-16.925v-52.486c0-3.435-.218-4.82-2.5-7.066C519.218 92.145 516.489 92 513 92h-15v62.422c0 14.004 3.892 25.101 11.676 33.292C517.594 195.905 529.103 200 544 200c14.897 0 26.204-4.095 34.123-12.286 7.918-8.191 11.877-19.288 11.877-33.292V92Z" />
</svg>
</div>
<span class="inline-block font-mono leading-none text-[#00DC82] group-hover:border-[#00DC42] text-[16px] font-semibold border-[#00DC42]/50 bg-[#00DC42]/10 group-hover:bg-[#00DC42]/15 px-2.5 py-1.5 border rounded">{{ version }}</span>
</a>
<button id="animation-toggle">Animation Enabled</button>
<div class="nuxt-loader-bar"></div>
<script>
const ANIMATION_KEY = 'nuxt-loading-enable-animation'
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
let isLowPerformance = checkIsLowPerformance()
let enableAnimation = localStorage.getItem(ANIMATION_KEY) === 'false'
? false
: localStorage.getItem(ANIMATION_KEY) === 'true'
? true
: !isLowPerformance
const mouseLight = window.document.getElementById('mouseLight')
const nuxtImg = window.document.getElementById('nuxtImg')
const animationToggle = window.document.getElementById('animation-toggle')
const body = window.document.body
let bodyRect
function checkIsLowPerformance() {
return window.matchMedia('(prefers-reduced-motion: reduce)').matches
|| navigator.hardwareConcurrency < 2
|| navigator.deviceMemory < 1
// Safari has some performance issue on the blur filter. Remove this when it's fixed.
|| isSafari
}
function calculateDistance(elem, mouseX, mouseY) {
return Math.floor(Math.sqrt(Math.pow(mouseX - (elem.x + (elem.width / 2)), 2) + Math.pow(mouseY - (elem.top + (elem.height / 2)), 2)));
}
function onFocusOut() {
if (!enableAnimation) {
return
}
mouseLight.style.opacity = 0
nuxtImg.style.opacity = 0.7
}
function onMouseMove(e) {
if (!enableAnimation) {
return
}
const pointerRect = nuxtImg.getBoundingClientRect()
if (!bodyRect) {
bodyRect = body.getBoundingClientRect()
}
const distance = calculateDistance(pointerRect, e.pageX, e.pageY)
const size = Math.max((1000 - distance) / 2 / 100, 1)
mouseLight.style.top = `${e.clientY - bodyRect.y - mouseLight.clientHeight / 2}px`
mouseLight.style.left = `${e.clientX - mouseLight.clientWidth / 2}px`
mouseLight.style.width = mouseLight.style.height = `${Math.max(Math.round(size * 100), 300)}px`
mouseLight.style.filter = `blur(${Math.min(Math.max(size * 50, 100), 160)}px)`
mouseLight.style.opacity = Math.min(Math.max(size / 4, 0.6), 1)
const dx = e.pageX - pointerRect.left
const dy = e.pageY - pointerRect.top
const logoGradient = `radial-gradient(circle at ${dx}px ${dy}px, black 75%, transparent 100%)`
nuxtImg.style['-webkit-mask-image'] = logoGradient
nuxtImg.style['mask-image'] = logoGradient
nuxtImg.style.opacity = Math.min(Math.max(size / 4, 0.7), 1)
}
function toggleAnimation(value = !enableAnimation) {
enableAnimation = value
document.body.classList.toggle('visual-effects', enableAnimation)
if (value) {
onFocusOut()
animationToggle.innerText = 'Animation Enabled'
}
else {
mouseLight.style.opacity = 0
nuxtImg.style.opacity = 1
nuxtImg.style['mask-image'] = ''
nuxtImg.style['-webkit-mask-image'] = ''
animationToggle.innerText = 'Animation Disabled'
}
localStorage.setItem(ANIMATION_KEY, enableAnimation ? 'true' : 'false')
}
animationToggle.addEventListener('click', () => toggleAnimation(), { passive: true})
body.addEventListener('mousemove', onMouseMove, { passive: true })
body.addEventListener('mouseleave', onFocusOut, { passive: true })
toggleAnimation(enableAnimation)
if (typeof window.fetch === 'undefined') {
setTimeout(() => window.location.reload(), 1000)
setTimeout(() => window.location.reload(), 200)
} else {
const check = async () => {
try {
@ -178,8 +70,8 @@
.location
.reload()
}
} catch {}
setTimeout(check, 1000)
} catch {}
setTimeout(check, 200)
}
check()
}

View File

@ -1,3 +1,4 @@
{
"loading": "Loading"
"loading": "Loading",
"version": "4.0"
}

View File

@ -1,4 +1,3 @@
{
"appName": "Nuxt",
"version": ""
"appName": "Nuxt"
}

View File

@ -6,361 +6,105 @@
<meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0" name="viewport" />
<link rel="icon" type="image/png" href="/icon.png" />
<script type="module" src="/styles.ts"></script>
<style lang="postcss">
/*linear gradients */
@media (prefers-color-scheme: light) {
.get-started-gradient-border {
background: linear-gradient(to right, #ffffff, #ffffff), linear-gradient(to right, #00DC82, #1DE0B1, #36E4DA);
}
.gradient-border-modules {
background: linear-gradient(var(--gradient-angle), rgba(247, 209, 76), rgba(247, 209, 76, 0.6), rgba(255, 255, 255, 0.8), rgba(247, 209, 76));
}
.gradient-border-examples {
background: linear-gradient(var(--gradient-angle), rgba(141, 234, 255), rgba(141, 234, 255, 0.6), rgba(255, 255, 255, 0.8), rgba(141, 234, 255));
}
.gradient-border-documentation {
background: linear-gradient(var(--gradient-angle), rgba(0, 220, 130), rgba(0, 220, 130, 0.6), rgba(255, 255, 255, 0.8), rgba(0, 220, 130));
}
}
@media (prefers-color-scheme: dark) {
.get-started-gradient-border {
background: linear-gradient(to right, #18181B, #18181B), linear-gradient(to right, #00DC82, #1DE0B1, #36E4DA);
}
.gradient-border-modules {
background: linear-gradient(var(--gradient-angle), rgba(247, 209, 76), rgba(163, 129, 8), rgba(255, 255, 255, 0.3), rgba(163, 129, 8));
}
.gradient-border-examples {
background: linear-gradient(var(--gradient-angle), rgba(141, 234, 255), rgba(0, 138, 169), rgba(255, 255, 255, 0.3), rgba(0, 138, 169));
}
.gradient-border-documentation {
background: linear-gradient(var(--gradient-angle), rgba(0, 220, 130), rgba(0, 63, 37), rgba(255, 255, 255, 0.2), rgba(0, 63, 37));
}
}
/* get started */
.get-started-gradient-border {
background-clip: padding-box, border-box;
background-origin: padding-box, border-box;
border-color: transparent;
border-radius: 12px;
border-width: 1px;
}
.get-started-gradient-border:hover > :is(.get-started-gradient-left, .get-started-gradient-right) {
opacity: 0.2;
}
.get-started-gradient-left, .get-started-gradient-right {
opacity: 0;
}
/* gradient border */
.gradient-border {
opacity: 0;
position: absolute;
top: 0;
left: 0;
width: calc(100% + 2px);
border-radius: 12px;
z-index: -1;
transform: translate(-1px, -1px);
}
.gradient-border-rect {
height: calc(100% + 2px);
}
@media (min-width: 1024px) {
.gradient-border-rect {
height: calc(100% + 1px);
}
}
.gradient-border-square {
height: calc(100% + 2px);
}
.modules-gradient-right {
opacity: 0;
}
.modules-container:hover > .gradient-border, .examples-container:hover > .gradient-border, .documentation-container:hover > .gradient-border {
opacity: 1;
animation: gradient-rotate 5s cubic-bezier(0,0,1,1) 0s infinite reverse;
transition: all 0.3s linear;
}
.modules-container:hover > .modules-gradient-right {
opacity: 0.2;
}
.examples-container:hover > .examples-gradient-right {
opacity: 0.2;
}
.examples-gradient-right {
opacity: 0;
}
/* images */
.documentation-image-color-light, .documentation-image-color-dark {
display: none;
}
.modules-image-color-light, .modules-image-color-dark {
display: none;
}
.examples-image-color-light, .examples-image-color-dark {
display: none;
}
/* image */
@media (prefers-color-scheme: light) {
/*modules*/
.modules-image-light {
display: block;
}
.modules-image-dark {
display: none;
}
.modules-container:hover > a > .modules-image-light {
display: none;
}
.modules-container:hover > a > .modules-image-color-light {
display: block;
}
/* examples */
.examples-image-light {
display: block;
}
.examples-image-dark {
display: none;
}
.examples-container:hover > a > .examples-image-light {
display: none;
}
.examples-container:hover > a > .examples-image-color-light {
display: block;
}
/* documentation */
.documentation-image-light {
display: block;
}
.documentation-image-dark {
display: none;
}
.documentation-container:hover > a > div > .documentation-image-light {
display: none;
}
.documentation-container:hover > a > div > .documentation-image-color-light {
display: block;
}
}
@media (prefers-color-scheme: dark) {
/*modules*/
.modules-image-dark {
display: block;
}
.modules-image-light {
display: none;
}
.modules-container:hover > a > .modules-image-color-dark {
display: block;
}
.modules-container:hover > a > .modules-image-dark {
display: none;
}
/* examples */
.examples-image-dark {
display: block;
}
.examples-image-light {
display: none;
}
.examples-container:hover > a > .examples-image-color-dark {
display: block;
}
.examples-container:hover > a > .examples-image-dark {
display: none;
}
/* documentation */
.documentation-image-dark {
display: block;
}
.documentation-image-light {
display: none;
}
.documentation-container:hover > a > div >.documentation-image-color-dark {
display: block;
}
.documentation-container:hover > a > div > .documentation-image-dark {
display: none;
}
}
@property --gradient-angle {
syntax: '<angle>';
inherits: false;
initial-value: 180deg;
}
@keyframes gradient-rotate {
0% {
--gradient-angle: 0deg;
}
100% {
--gradient-angle: 360deg;
}
}
</style>
</head>
<body class="antialiased bg-white dark:bg-black text-black dark:text-white min-h-screen place-content-center flex flex-col items-center justify-center text-sm sm:text-base">
<div class="flex-1 flex flex-col gap-y-16 py-14">
<body class="antialiased bg-white dark:bg-[#020420] text-[#020420] dark:text-white min-h-screen place-content-center flex flex-col items-center justify-center text-sm sm:text-base">
<div class="flex flex-col mt-6 sm:mt-0">
<div class="flex flex-col gap-y-4 items-center justify-center">
<a href="https://nuxt.com" target="_blank">
<svg width="61" height="42" viewBox="0 0 61 42" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M33.9869 41.2211H56.412C57.1243 41.2212 57.824 41.0336 58.4408 40.6772C59.0576 40.3209 59.5698 39.8083 59.9258 39.191C60.2818 38.5737 60.469 37.8736 60.4687 37.1609C60.4684 36.4482 60.2805 35.7482 59.924 35.1313L44.864 9.03129C44.508 8.41416 43.996 7.90168 43.3793 7.54537C42.7626 7.18906 42.063 7.00147 41.3509 7.00147C40.6387 7.00147 39.9391 7.18906 39.3225 7.54537C38.7058 7.90168 38.1937 8.41416 37.8377 9.03129L33.9869 15.7093L26.458 2.65061C26.1018 2.03354 25.5895 1.52113 24.9726 1.16489C24.3557 0.808639 23.656 0.621094 22.9438 0.621094C22.2316 0.621094 21.5318 0.808639 20.915 1.16489C20.2981 1.52113 19.7858 2.03354 19.4296 2.65061L0.689224 35.1313C0.332704 35.7482 0.144842 36.4482 0.144532 37.1609C0.144222 37.8736 0.331476 38.5737 0.687459 39.191C1.04344 39.8083 1.5556 40.3209 2.17243 40.6772C2.78925 41.0336 3.48899 41.2212 4.20126 41.2211H18.2778C23.8551 41.2211 27.9682 38.7699 30.7984 33.9876L37.6694 22.0813L41.3498 15.7093L52.3951 34.8492H37.6694L33.9869 41.2211ZM18.0484 34.8426L8.2247 34.8404L22.9504 9.32211L30.2979 22.0813L25.3784 30.6092C23.4989 33.7121 21.3637 34.8426 18.0484 34.8426Z" fill="#00DC82" />
<a href="https://nuxt.com?utm_source=nuxt-welcome" target="_blank" class="inline-flex items-end gap-4">
<svg class="h-8 sm:h-12" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 800 200">
<path fill="#00DC82" d="M168.303 200h111.522c3.543 0 7.022-.924 10.09-2.679A20.086 20.086 0 0 0 297.3 190a19.855 19.855 0 0 0 2.7-10.001 19.858 19.858 0 0 0-2.709-9.998L222.396 41.429a20.09 20.09 0 0 0-7.384-7.32 20.313 20.313 0 0 0-10.088-2.679c-3.541 0-7.02.925-10.087 2.68a20.082 20.082 0 0 0-7.384 7.32l-19.15 32.896L130.86 9.998a20.086 20.086 0 0 0-7.387-7.32A20.322 20.322 0 0 0 113.384 0c-3.542 0-7.022.924-10.09 2.679a20.091 20.091 0 0 0-7.387 7.319L2.709 170A19.853 19.853 0 0 0 0 179.999c-.002 3.511.93 6.96 2.7 10.001a20.091 20.091 0 0 0 7.385 7.321A20.322 20.322 0 0 0 20.175 200h70.004c27.737 0 48.192-12.075 62.266-35.633l34.171-58.652 18.303-31.389 54.93 94.285h-73.233L168.303 200Zm-79.265-31.421-48.854-.011 73.232-125.706 36.541 62.853-24.466 42.01c-9.347 15.285-19.965 20.854-36.453 20.854Z" />
<path fill="currentColor" d="M377 200a4 4 0 0 0 4-4v-93s5.244 8.286 15 25l38.707 66.961c1.789 3.119 5.084 5.039 8.649 5.039H470V50h-27a4 4 0 0 0-4 4v94l-17-30-36.588-62.98c-1.792-3.108-5.081-5.02-8.639-5.02H350v150h27ZM676.203 143.857 710.551 92h-25.73a9.972 9.972 0 0 0-8.333 4.522L660.757 120.5l-15.731-23.978A9.972 9.972 0 0 0 636.693 92h-25.527l34.348 51.643L608.524 200h24.966a9.969 9.969 0 0 0 8.29-4.458l19.18-28.756 18.981 28.72a9.968 9.968 0 0 0 8.313 4.494h24.736l-36.787-56.143ZM724.598 92h19.714V60.071h28.251V92H800v24.857h-27.437V159.5c0 10.5 5.284 15.429 14.43 15.429H800V200h-16.869c-23.576 0-38.819-14.143-38.819-39.214v-43.929h-19.714V92ZM590 92h-15c-3.489 0-6.218.145-8.5 2.523-2.282 2.246-2.5 3.63-2.5 7.066v52.486c0 8.058-.376 12.962-4 16.925-3.624 3.831-8.619 5-16 5-7.247 0-12.376-1.169-16-5-3.624-3.963-4-8.867-4-16.925v-52.486c0-3.435-.218-4.82-2.5-7.066C519.218 92.145 516.489 92 513 92h-15v62.422c0 14.004 3.892 25.101 11.676 33.292C517.594 195.905 529.103 200 544 200c14.897 0 26.204-4.095 34.123-12.286 7.918-8.191 11.877-19.288 11.877-33.292V92Z" />
</svg>
<span class="inline-block font-mono leading-none text-[#00DC82] group-hover:border-[#00DC42] text-[12px] sm:text-[14px] font-semibold border-[#00DC42]/50 bg-[#00DC42]/10 group-hover:bg-[#00DC42]/15 px-2 sm:px-2.5 py-1 sm:py-1.5 border rounded">{{ version }}</span>
</a>
<h1 class="text-black dark:text-white text-4xl sm:text-5xl font-semibold text-center">
Welcome to Nuxt!
</h1>
</div>
<div class="grid grid-cols-2 lg:grid-cols-10 gap-6 max-w-[960px] px-4">
<div class="col-span-2 lg:col-span-10 relative get-started-gradient-border">
<div class="get-started-gradient-left absolute left-0 inset-y-0 w-[20%] bg-gradient-to-r to-transparent from-green-400 rounded-xl z-1 transition-opacity duration-300"></div>
<div class="get-started-gradient-right absolute right-0 inset-y-0 w-[20%] bg-gradient-to-l to-transparent from-blue-400 rounded-xl z-1 transition-opacity duration-300"></div>
<div class="w-full absolute inset-x-0 flex justify-center -top-[58px]">
<img src="/icons/get-started.svg" class="hidden dark:block">
<img src="/icons/get-started-light.svg" class="dark:hidden">
<div class="max-w-[980px] w-full grid grid-cols-1 sm:grid-cols-3 mt-6 sm:mt-10 gap-4 sm:gap-6 px-4">
<div class="sm:col-span-2 flex flex-col gap-1 border p-6 rounded-lg border-[#00DC42]/50 dark:bg-white/5 bg-gray-50/10">
<div class="w-[32px] h-[32px] bg-[#00DC82]/5 flex items-center justify-center border rounded border-[#00DC82] dark:border-[#00DC82]/80 dark:bg-[#020420] text-[#00DC82] dark:text-[#00DC82]">
<svg class="size-[18px]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path fill="currentColor" d="m228.1 121.2l-143.9-88A8 8 0 0 0 72 40v176a8 8 0 0 0 12.2 6.8l143.9-88a7.9 7.9 0 0 0 0-13.6Z" opacity=".2"/><path fill="currentColor" d="M80 232a15.5 15.5 0 0 1-7.8-2.1A15.8 15.8 0 0 1 64 216V40a15.8 15.8 0 0 1 8.2-13.9a15.5 15.5 0 0 1 16.1.3l144 87.9a16 16 0 0 1 0 27.4l-144 87.9A15.4 15.4 0 0 1 80 232Zm0-192v176l144-88Z"/></svg>
</div>
<div class="flex flex-col rounded-xl items-center gap-y-4 pt-[58px] px-4 sm:px-28 pb-6 z-10">
<h2 class="font-semibold text-2xl text-black dark:text-white">
Get started
</h2>
<p class="mb-2 text-center">Remove this welcome page by
replacing <a class="bg-gray-100 dark:bg-white/10 rounded font-mono p-1 font-bold">&lt;NuxtWelcome /&gt;</a> in <a href="https://nuxt.com/docs/guide/directory-structure/app" target="_blank" rel="noopener" class="bg-gray-100 dark:bg-white/10 rounded font-mono p-1 font-bold">app.vue</a> with your own code, or creating your own <span class="bg-gray-100 dark:bg-white/10 rounded font-mono p-1 font-bold">app.vue</span> if it doesn't exist.</p>
</div>
</div>
<div class="lg:min-h-min sm:min-h-[220px] md:min-h-[180px] col-span-2 sm:col-span-1 lg:col-span-6 text-black dark:text-white rounded-xl modules-container relative items-center justify-center border border-gray-200 dark:border-transparent hover:border-transparent">
<div class="gradient-border gradient-border-modules gradient-border-rect"></div>
<div class="modules-gradient-right absolute right-0 inset-y-0 w-[20%] bg-gradient-to-l to-transparent from-yellow-400 rounded-xl z-1 transition-opacity duration-300"></div>
<a href="https://nuxt.com/modules" target="_blank" class="py-6 px-5 rounded-xl flex items-center justify-center gap-x-4 dark:border-none bg-white dark:bg-gray-900 sm:min-h-[220px] md:min-h-[180px] lg:min-h-min">
<img src="/icons/modules-color-light.svg" alt="modules icon" class="modules-image-color-light">
<img src="/icons/modules-color.svg" alt="modules icon" class="modules-image-color-dark">
<img src="/icons/modules-light.svg" alt="modules icon" class="modules-image-light">
<img src="/icons/modules.svg" alt="modules icon" class="modules-image-dark">
<div class="flex flex-col space-y text-black dark:text-white">
<h3 class="font-semibold text-xl">
Modules
</h3>
<p class="text-gray-700 dark:text-gray-300">
Discover our list of modules to supercharge your Nuxt project. Created by the Nuxt team and community.
</p>
</div>
</a>
</div>
<div class="row-span-2 col-span-2 order-last lg:order-none lg:col-span-4 text-black dark:text-white documentation-container rounded-xl relative items-center justify-center border border-gray-200 dark:border-transparent hover:border-transparent">
<div class="gradient-border gradient-border-square gradient-border-documentation"></div>
<a href="https://nuxt.com/docs" target="_blank" class="rounded-xl flex lg:flex-col items-center justify-center gap-y-4 bg-white dark:bg-gray-900">
<div class="py-6 lg:py-7 px-5 rounded-xl flex flex-col sm:flex-row lg:flex-col items-center justify-center gap-y-2 ">
<div class="flex flex-col space-y text-black dark:text-white">
<h3 class="font-semibold text-xl">
Documentation
</h3>
<p class="text-gray-700 dark:text-gray-300">
We highly recommend you take a look at the Nuxt documentation to level up.
</p>
</div>
<img src="/icons/documentation-color-light.svg" alt="documentation icon" class="documentation-image-color-light h-32 sm:h-34">
<img src="/icons/documentation-color.svg" alt="documentation icon" class="documentation-image-color-dark h-32 sm:h-34">
<img src="/icons/documentation-light.svg" alt="documentation icon" class="documentation-image-light h-32 sm:h-34">
<img src="/icons/documentation.svg" alt="documentation icon" class="documentation-image-dark h-32 sm:h-34">
</div>
</a>
</div>
<div class="lg:min-h-min sm:min-h-[220px] md:min-h-[180px] col-span-2 sm:col-span-1 lg:col-span-6 text-black dark:text-white rounded-xl examples-container relative items-center justify-center border border-gray-200 dark:border-transparent hover:border-transparent">
<div class="gradient-border gradient-border-examples gradient-border-rect"></div>
<div class="examples-gradient-right absolute right-0 inset-y-0 w-[20%] bg-gradient-to-l to-transparent from-blue-400 rounded-xl z-1 transition-opacity duration-300"></div>
<a href="https://nuxt.com/docs/examples" target="_blank" class="py-6 px-5 rounded-xl flex items-center justify-center gap-x-4 bg-white dark:bg-gray-900 sm:min-h-[220px] md:min-h-[180px] lg:min-h-min">
<img src="/icons/examples-color-light.svg" alt="examples icon" class="examples-image-color-light">
<img src="/icons/examples-color.svg" alt="examples icon" class="examples-image-color-dark">
<img src="/icons/examples-light.svg" alt="examples icon" class="examples-image-light">
<img src="/icons/examples.svg" alt="examples icon" class="examples-image-dark">
<div class="flex flex-col space-y text-black dark:text-white">
<h3 class="font-semibold text-xl">
Examples
</h3>
<p class="text-gray-700 dark:text-gray-300">
Explore different way of using Nuxt features and get inspired with our list of examples.
</p>
</div>
</a>
<h2 class="font-semibold text-base mt-1">Get started</h2>
<p class="text-sm text-gray-700 dark:text-gray-200">Remove this welcome page by replacing <a class="bg-green-50 text-green-700 dark:text-[#00DC82] dark:bg-[#020420] rounded font-mono p-1 font-bold border dark:border-white/10 border-green-600/10">&lt;NuxtWelcome/&gt;</a> in <a href="https://nuxt.com/docs/guide/directory-structure/app" target="_blank" rel="noopener" class="bg-green-50 text-green-700 dark:text-[#00DC82] dark:bg-[#020420] dark:border-white/20 rounded font-mono p-1 font-bold border border-green-600/20 hover:border-[#00DC82]">app.vue</a> with your own code.</p>
</div>
<a href="https://nuxt.com/docs?utm_source=nuxt-welcome" target="_blank" class="relative flex flex-col gap-1 border p-6 rounded-lg border-gray-200 dark:border-white/10 dark:bg-white/5 bg-gray-50/10 group hover:dark:border-[#00DC82] hover:border-[#00DC82] transition-all">
<div class="w-[32px] h-[32px] bg-[#00DC82]/5 flex items-center justify-center border rounded border-[#00DC82] transition-all dark:border-[#00DC82]/50 group-hover:dark:border-[#00DC82]/80 dark:bg-[#020420] text-[#00DC82] dark:text-[#00DC82]">
<svg class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path fill="currentColor" d="M136 48v128H88V80H40V48a8 8 0 0 1 8-8h32a8 8 0 0 1 8 8a8 8 0 0 1 8-8h32a8 8 0 0 1 8 8Zm89.9 149.6l-8.3-30.9l-46.4 12.5l8.3 30.9a8 8 0 0 0 9.8 5.6l30.9-8.3a8 8 0 0 0 5.7-9.8ZM184.5 43.1a8.1 8.1 0 0 0-9.8-5.7l-30.9 8.3a8.1 8.1 0 0 0-5.7 9.8l8.3 30.9L192.8 74Z" opacity=".2"/><path fill="currentColor" d="M233.6 195.6L192.2 41a16 16 0 0 0-19.6-11.3L141.7 38l-1 .3A16 16 0 0 0 128 32H96a15.8 15.8 0 0 0-8 2.2a15.8 15.8 0 0 0-8-2.2H48a16 16 0 0 0-16 16v160a16 16 0 0 0 16 16h32a15.8 15.8 0 0 0 8-2.2a15.8 15.8 0 0 0 8 2.2h32a16 16 0 0 0 16-16v-99.6l27.8 103.7a16 16 0 0 0 15.5 11.9a19.9 19.9 0 0 0 4.1-.5l30.9-8.3a16 16 0 0 0 11.3-19.6ZM156.2 92.1l30.9-8.3l20.7 77.3l-30.9 8.3Zm20.5-46.9l6.3 23.1l-30.9 8.3l-6.3-23.1ZM128 48v120H96V48Zm-48 0v24H48V48ZM48 208V88h32v120Zm80 0H96v-24h32v24Zm90.2-8.3l-30.9 8.3l-6.3-23.2l31-8.3l6.2 23.2Z"/></svg>
</div>
<svg class="absolute right-4 top-4 dark:text-white/40 text-[#020420]/20 group-hover:text-[#00DC82] size-4 group-hover:size-5 transition-all" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path fill="currentColor" d="M200 64v104a8 8 0 0 1-16 0V83.3L69.7 197.7a8.2 8.2 0 0 1-11.4 0a8.1 8.1 0 0 1 0-11.4L172.7 72H88a8 8 0 0 1 0-16h104a8 8 0 0 1 8 8Z"/></svg>
<h2 class="font-semibold text-base mt-1">Documentation</h2>
<p class="text-sm text-gray-700 dark:text-gray-200 group-hover:dark:text-gray-100">We highly recommend you take a look at the Nuxt documentation to level up.</p>
</a>
</div>
<div class="max-w-[980px] w-full grid grid-cols-1 sm:grid-cols-3 gap-4 sm:gap-6 mt-4 sm:mt-6 px-4">
<a href="https://nuxt.com/modules?utm_source=nuxt-welcome" target="_blank" class="relative flex flex-col gap-1 border p-6 rounded-lg border-gray-200 dark:border-white/10 dark:bg-white/5 bg-gray-50/10 group hover:dark:border-[#00DC82] hover:border-[#00DC82] transition-all">
<div class="w-[32px] h-[32px] bg-[#00DC82]/5 flex items-center justify-center border rounded border-[#00DC82] transition-all dark:border-[#00DC82]/50 group-hover:dark:border-[#00DC82]/80 dark:bg-[#020420] text-[#00DC82] dark:text-[#00DC82]">
<svg class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path fill="currentColor" d="M64 216a8 8 0 0 1-8-8v-42.7a27.6 27.6 0 0 1-14.1 2.6A28 28 0 1 1 56 114.7V72a8 8 0 0 1 8-8h46.7a27.6 27.6 0 0 1-2.6-14.1A28 28 0 1 1 161.3 64H208a8 8 0 0 1 8 8v42.7a27.6 27.6 0 0 0-14.1-2.6a28 28 0 1 0 14.1 53.2V208a8 8 0 0 1-8 8Z" opacity=".2"/><path fill="currentColor" d="M220.3 158.5a8.1 8.1 0 0 0-7.7-.4a20.2 20.2 0 0 1-23.2-4.4a20 20 0 0 1 13.1-33.6a19.6 19.6 0 0 1 10.1 1.8a8.1 8.1 0 0 0 7.7-.4a8.2 8.2 0 0 0 3.7-6.8V72a16 16 0 0 0-16-16h-36.2c.1-1.3.2-2.7.2-4a36.1 36.1 0 0 0-38.3-35.9a36 36 0 0 0-33.6 33.3a36.4 36.4 0 0 0 .1 6.6H64a16 16 0 0 0-16 16v32.2l-4-.2a35.6 35.6 0 0 0-26.2 11.4a35.3 35.3 0 0 0-9.7 26.9a36 36 0 0 0 33.3 33.6a36.4 36.4 0 0 0 6.6-.1V208a16 16 0 0 0 16 16h144a16 16 0 0 0 16-16v-42.7a8.2 8.2 0 0 0-3.7-6.8ZM208 208H64v-42.7a8.2 8.2 0 0 0-3.7-6.8a8.1 8.1 0 0 0-7.7-.4a19.6 19.6 0 0 1-10.1 1.8a20 20 0 0 1-13.1-33.6a20.2 20.2 0 0 1 23.2-4.4a8.1 8.1 0 0 0 7.7-.4a8.2 8.2 0 0 0 3.7-6.8V72h46.7a8.2 8.2 0 0 0 6.8-3.7a8.1 8.1 0 0 0 .4-7.7a19.6 19.6 0 0 1-1.8-10.1a20 20 0 0 1 33.6-13.1a20.2 20.2 0 0 1 4.4 23.2a8.1 8.1 0 0 0 .4 7.7a8.2 8.2 0 0 0 6.8 3.7H208v32.2a36.4 36.4 0 0 0-6.6-.1a36 36 0 0 0-33.3 33.6A36.1 36.1 0 0 0 204 176l4-.2Z"/></svg>
</div>
<svg class="absolute right-4 top-4 dark:text-white/40 text-[#020420]/20 group-hover:text-[#00DC82] size-4 group-hover:size-5 transition-all" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path fill="currentColor" d="M200 64v104a8 8 0 0 1-16 0V83.3L69.7 197.7a8.2 8.2 0 0 1-11.4 0a8.1 8.1 0 0 1 0-11.4L172.7 72H88a8 8 0 0 1 0-16h104a8 8 0 0 1 8 8Z"/></svg>
<h2 class="font-semibold text-base mt-1">Modules</h2>
<p class="text-sm text-gray-700 dark:text-gray-200 group-hover:dark:text-gray-100">Discover our list of modules to supercharge your Nuxt project.</p>
</a>
<a href="https://nuxt.com/modules?utm_source=nuxt-welcome" target="_blank" class="relative flex flex-col gap-1 border p-6 rounded-lg border-gray-200 dark:border-white/10 dark:bg-white/5 bg-gray-50/10 group hover:dark:border-[#00DC82] hover:border-[#00DC82] transition-all">
<div class="w-[32px] h-[32px] bg-[#00DC82]/5 flex items-center justify-center border rounded border-[#00DC82] transition-all dark:border-[#00DC82]/50 group-hover:dark:border-[#00DC82]/80 dark:bg-[#020420] text-[#00DC82] dark:text-[#00DC82]">
<svg class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path fill="currentColor" d="M224 56v144a8 8 0 0 1-8 8H40a8 8 0 0 1-8-8V56a8 8 0 0 1 8-8h176a8 8 0 0 1 8 8Z" opacity=".2"/><path fill="currentColor" d="M216 40H40a16 16 0 0 0-16 16v144a16 16 0 0 0 16 16h176a16 16 0 0 0 16-16V56a16 16 0 0 0-16-16Zm0 160H40V56h176v144ZM80 84a12 12 0 1 1-12-12a12 12 0 0 1 12 12Zm40 0a12 12 0 1 1-12-12a12 12 0 0 1 12 12Z"/></svg>
</div>
<svg class="absolute right-4 top-4 dark:text-white/40 text-[#020420]/20 group-hover:text-[#00DC82] size-4 group-hover:size-5 transition-all" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path fill="currentColor" d="M200 64v104a8 8 0 0 1-16 0V83.3L69.7 197.7a8.2 8.2 0 0 1-11.4 0a8.1 8.1 0 0 1 0-11.4L172.7 72H88a8 8 0 0 1 0-16h104a8 8 0 0 1 8 8Z"/></svg>
<h2 class="font-semibold text-base mt-1">Examples</h2>
<p class="text-sm text-gray-700 dark:text-gray-200 group-hover:dark:text-gray-100">Explore different way of using Nuxt features and get inspired.</p>
</a>
<a href="https://nuxt.com/deploy?utm_source=nuxt-welcome" target="_blank" class="relative flex flex-col gap-1 border p-6 rounded-lg border-gray-200 dark:border-white/10 dark:bg-white/5 bg-gray-50/10 group hover:dark:border-[#00DC82] hover:border-[#00DC82] transition-all">
<div class="w-[32px] h-[32px] bg-[#00DC82]/5 flex items-center justify-center border rounded border-[#00DC82] transition-all dark:border-[#00DC82]/50 group-hover:dark:border-[#00DC82]/80 dark:bg-[#020420] text-[#00DC82] dark:text-[#00DC82]">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 256 256"><path fill="currentColor" d="M94.1 184.6c-11.4 33.9-56.6 33.9-56.6 33.9s0-45.2 33.9-56.6Zm90.5-67.9v64.6a8 8 0 0 1-2.4 5.6l-32.3 32.4a8 8 0 0 1-13.5-4.1l-8.4-41.9Zm-45.3-45.3H74.7a8 8 0 0 0-5.6 2.4l-32.4 32.3a8 8 0 0 0 4.1 13.5l41.9 8.4Z" opacity=".2"/><path fill="currentColor" d="M96.6 177a7.9 7.9 0 0 0-10.1 5c-6.6 19.7-27.9 25.8-40.2 27.7c1.9-12.3 8-33.6 27.7-40.2a8 8 0 1 0-5.1-15.1c-16.4 5.4-28.4 18.4-34.8 37.5a91.8 91.8 0 0 0-4.6 26.6a8 8 0 0 0 8 8a91.8 91.8 0 0 0 26.6-4.6c19.1-6.4 32.1-18.4 37.5-34.8a7.9 7.9 0 0 0-5-10.1Z"/><path fill="currentColor" d="M227.6 41.8a15.7 15.7 0 0 0-13.4-13.4c-11.3-1.7-40.6-2.5-69.2 26.1l-9 8.9H74.7a16.2 16.2 0 0 0-11.3 4.7l-32.3 32.4a15.9 15.9 0 0 0-4 15.9a16 16 0 0 0 12.2 11.1l39.5 7.9l41.8 41.8l7.9 39.5a16 16 0 0 0 11.1 12.2a14.7 14.7 0 0 0 4.6.7a15.6 15.6 0 0 0 11.3-4.7l32.4-32.3a16.2 16.2 0 0 0 4.7-11.3V120l8.9-9c28.6-28.6 27.8-57.9 26.1-69.2ZM74.7 79.4H120l-39.9 39.9l-37.7-7.5Zm81.6-13.6c7.8-7.8 28.8-25.6 55.5-21.6c4 26.7-13.8 47.7-21.6 55.5L128 161.9L94.1 128Zm20.3 115.5l-32.4 32.3l-7.5-37.7l39.9-39.9Z"/></svg>
</div>
<svg class="absolute right-4 top-4 dark:text-white/40 text-[#020420]/20 group-hover:text-[#00DC82] size-4 group-hover:size-5 transition-all" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path fill="currentColor" d="M200 64v104a8 8 0 0 1-16 0V83.3L69.7 197.7a8.2 8.2 0 0 1-11.4 0a8.1 8.1 0 0 1 0-11.4L172.7 72H88a8 8 0 0 1 0-16h104a8 8 0 0 1 8 8Z"/></svg>
<h2 class="font-semibold text-base mt-1">Deploy</h2>
<p class="text-sm text-gray-700 dark:text-gray-200 group-hover:dark:text-gray-100">Learn how to deploy your Nuxt project on different providers.</p>
</a>
</div>
<footer class="mx-auto sm:px-6 lg:px-8 px-4 w-full mb-6 mt-6 sm:mt-10 sm:mb-0">
<ul class="flex items-center justify-center gap-4">
<li>
<a
href="https://go.nuxt.com/github"
target="_blank"
class="focus-visible:ring-2 text-gray-500 hover:text-[#020420] dark:text-gray-400 dark:hover:text-white"
>
<span class="sr-only">Nuxt GitHub Repository</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
</a>
</li>
<li>
<a
href="https://go.nuxt.com/discord"
target="_blank"
class="focus-visible:ring-2 text-gray-500 hover:text-[#020420] dark:text-gray-400 dark:hover:text-white"
>
<span class="sr-only">Nuxt Discord Server</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M20.317 4.37a19.8 19.8 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.3 18.3 0 0 0-5.487 0a13 13 0 0 0-.617-1.25a.08.08 0 0 0-.079-.037A19.7 19.7 0 0 0 3.677 4.37a.1.1 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.08.08 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.08.08 0 0 0 .084-.028a14 14 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13 13 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10 10 0 0 0 .372-.292a.07.07 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.07.07 0 0 1 .078.01q.181.149.373.292a.077.077 0 0 1-.006.127a12.3 12.3 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.08.08 0 0 0 .084.028a19.8 19.8 0 0 0 6.002-3.03a.08.08 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.06.06 0 0 0-.031-.03M8.02 15.33c-1.182 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418m7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418"/></svg>
</a>
</li>
<li>
<a
href="https://go.nuxt.com/x"
target="_blank"
class="focus-visible:ring-2 text-gray-500 hover:text-[#020420] dark:text-gray-400 dark:hover:text-white"
>
<span class="sr-only">Nuxt on X</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584l-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z"/></svg>
</a>
</li>
<li>
<a
href="https://go.nuxt.com/linkedin"
target="_blank"
class="focus-visible:ring-2 text-gray-500 hover:text-[#020420] dark:text-gray-400 dark:hover:text-white"
>
<span class="sr-only">Nuxt Linkedin</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037c-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85c3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.06 2.06 0 0 1-2.063-2.065a2.064 2.064 0 1 1 2.063 2.065m1.782 13.019H3.555V9h3.564zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0z"/></svg>
</a>
</li>
</ul>
</footer>
</div>
<footer class="relative border-t bg-white dark:bg-black border-gray-200 dark:border-gray-900 w-full h-[70px] flex items-center">
<div class="absolute inset-x-0 flex items-center justify-center -top-3">
<a href="https://nuxt.com" target="_blank">
<svg width="70" height="20" viewBox="0 0 70 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<ellipse cx="34.6528" cy="10.4209" rx="34.5" ry="9.5" fill="white" class="dark:hidden" />
<ellipse cx="34.6528" cy="10.4209" rx="34.5" ry="9.5" fill="black" class="hidden dark:block" />
<path d="M36.0605 15.9209H42.6256C42.8341 15.9209 43.0389 15.8655 43.2195 15.7602C43.4001 15.6548 43.55 15.5033 43.6543 15.3209C43.7585 15.1384 43.8133 14.9315 43.8132 14.7208C43.8131 14.5102 43.7581 14.3033 43.6537 14.1209L39.2448 6.40667C39.1406 6.22427 38.9907 6.0728 38.8101 5.96748C38.6296 5.86217 38.4248 5.80672 38.2163 5.80672C38.0078 5.80672 37.803 5.86217 37.6225 5.96748C37.4419 6.0728 37.292 6.22427 37.1878 6.40667L36.0605 8.38048L33.8563 4.52076C33.752 4.33837 33.602 4.18692 33.4214 4.08163C33.2409 3.97633 33.036 3.9209 32.8275 3.9209C32.619 3.9209 32.4141 3.97633 32.2335 4.08163C32.053 4.18692 31.903 4.33837 31.7987 4.52076L26.3123 14.1209C26.2079 14.3033 26.1529 14.5102 26.1528 14.7208C26.1527 14.9315 26.2076 15.1384 26.3118 15.3209C26.416 15.5033 26.5659 15.6548 26.7465 15.7602C26.9271 15.8655 27.1319 15.9209 27.3405 15.9209H31.4615C33.0943 15.9209 34.2984 15.1964 35.127 13.7829L37.1385 10.2638L38.216 8.38048L41.4496 14.0376H37.1385L36.0605 15.9209ZM31.3943 14.0356L28.5184 14.035L32.8294 6.49263L34.9805 10.2638L33.5402 12.7844C32.99 13.7015 32.3649 14.0356 31.3943 14.0356Z" fill="#00DC82" />
</svg>
</a>
</div>
<div class="mx-auto sm:px-6 lg:px-8 px-4 w-full">
<div class="flex flex-col items-center gap-3 sm:flex-row sm:justify-between">
<div class="flex flex-col-reverse items-center gap-3 sm:flex-row">
<span class="text-sm text-gray-700 dark:text-gray-300">© 2016-{{ new Date().getFullYear() }} Nuxt - MIT
License</span>
</div>
<ul class="flex items-center justify-end gap-3">
<li>
<a
href="https://chat.nuxt.dev"
target="_blank"
class="focus-visible:ring-2 text-gray-700 hover:text-black dark:text-gray-300 dark:hover:text-white"
>
<span class="sr-only">Nuxt Discord Server</span>
<svg width="16" height="12" viewBox="0 0 16 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.3705 1.07322C13.3663 1.06497 13.3594 1.05851 13.351 1.05499C12.3785 0.599487 11.3522 0.274675 10.2978 0.0886873C10.2882 0.0868693 10.2783 0.0881809 10.2695 0.0924354C10.2607 0.0966899 10.2534 0.103671 10.2487 0.112385C10.109 0.371315 9.98212 0.637279 9.86863 0.909263C8.73205 0.733138 7.57595 0.733138 6.43938 0.909263C6.32514 0.636589 6.19624 0.370559 6.05328 0.112385C6.04838 0.10386 6.04107 0.0970401 6.03232 0.0928132C6.02356 0.0885863 6.01377 0.0871486 6.0042 0.0886873C4.9497 0.274285 3.92333 0.599121 2.95092 1.05502C2.9426 1.05862 2.93558 1.06477 2.93082 1.07262C0.986197 4.03716 0.453491 6.92881 0.714819 9.78465C0.715554 9.79165 0.71766 9.79843 0.721013 9.80458C0.724365 9.81073 0.728896 9.81613 0.734334 9.82046C1.86667 10.6763 3.1332 11.3296 4.47988 11.7525C4.48937 11.7554 4.49949 11.7552 4.5089 11.7521C4.51831 11.7489 4.52655 11.7429 4.53251 11.7349C4.82175 11.3331 5.07803 10.9077 5.29876 10.4629C5.3018 10.4568 5.30353 10.4501 5.30384 10.4433C5.30416 10.4365 5.30305 10.4296 5.3006 10.4233C5.29814 10.4169 5.29439 10.4111 5.2896 10.4064C5.2848 10.4016 5.27906 10.3979 5.27277 10.3955C4.86862 10.2377 4.47736 10.0474 4.10266 9.82645C4.09586 9.82236 4.09014 9.81663 4.08602 9.80976C4.0819 9.80288 4.0795 9.79508 4.07903 9.78703C4.07856 9.77899 4.08004 9.77095 4.08334 9.76362C4.08664 9.7563 4.09166 9.74992 4.09794 9.74504C4.17657 9.68491 4.25524 9.62236 4.33032 9.55918C4.33699 9.55358 4.34506 9.54998 4.35362 9.5488C4.36218 9.54762 4.3709 9.54891 4.37879 9.55252C6.83362 10.6962 9.4913 10.6962 11.9171 9.55252C11.925 9.54868 11.9338 9.54721 11.9425 9.54829C11.9512 9.54936 11.9594 9.55293 11.9662 9.55858C12.0413 9.62176 12.1199 9.68491 12.1991 9.74504C12.2054 9.74987 12.2105 9.75621 12.2138 9.7635C12.2172 9.7708 12.2187 9.77882 12.2183 9.78687C12.2179 9.79492 12.2156 9.80274 12.2115 9.80964C12.2074 9.81654 12.2018 9.82232 12.195 9.82645C11.8211 10.0492 11.4295 10.2394 11.0243 10.3949C11.018 10.3974 11.0123 10.4012 11.0075 10.406C11.0028 10.4109 10.9991 10.4167 10.9967 10.4231C10.9943 10.4295 10.9932 10.4364 10.9936 10.4433C10.9939 10.4501 10.9957 10.4568 10.9988 10.4629C11.2232 10.9052 11.4791 11.3301 11.7645 11.7342C11.7703 11.7425 11.7785 11.7487 11.7879 11.7519C11.7974 11.7552 11.8076 11.7554 11.8171 11.7524C13.1662 11.331 14.4349 10.6776 15.5687 9.82046C15.5742 9.81635 15.5788 9.81108 15.5822 9.80501C15.5855 9.79893 15.5876 9.7922 15.5882 9.78525C15.9011 6.4836 15.0644 3.61565 13.3705 1.07322ZM5.66537 8.04574C4.92629 8.04574 4.31731 7.35337 4.31731 6.50305C4.31731 5.65274 4.91448 4.96032 5.66537 4.96032C6.42213 4.96032 7.02522 5.65875 7.01341 6.503C7.01341 7.35337 6.41622 8.04574 5.66537 8.04574ZM10.6496 8.04574C9.91051 8.04574 9.30153 7.35337 9.30153 6.50305C9.30153 5.65274 9.8987 4.96032 10.6496 4.96032C11.4064 4.96032 12.0094 5.65875 11.9976 6.503C11.9976 7.35337 11.4064 8.04574 10.6496 8.04574Z" fill="currentColor" />
</svg>
</a>
</li>
<li>
<a
href="https://twitter.nuxt.dev"
target="_blank"
class="focus-visible:ring-2 text-gray-700 hover:text-black dark:text-gray-300 dark:hover:text-white"
>
<span class="sr-only">Nuxt Twitter</span>
<svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.486 1.75441C16.8596 2.02615 16.1972 2.20579 15.5193 2.28774C16.2345 1.86051 16.7704 1.18839 17.0277 0.396073C16.3556 0.796126 15.62 1.07799 14.8527 1.22941C14.3398 0.673216 13.6568 0.302987 12.9108 0.176783C12.1649 0.0505786 11.3981 0.175539 10.7308 0.532064C10.0635 0.88859 9.53345 1.45652 9.2237 2.14677C8.91396 2.83702 8.84208 3.61056 9.01934 4.34607C7.66053 4.27734 6.33137 3.92353 5.11822 3.30762C3.90506 2.69171 2.83504 1.82748 1.97767 0.771073C1.67695 1.29621 1.51894 1.89093 1.51934 2.49607C1.51827 3.05806 1.65618 3.61159 1.9208 4.10738C2.18541 4.60317 2.56852 5.02583 3.036 5.33774C2.49265 5.32296 1.96091 5.17716 1.486 4.91274V4.95441C1.49008 5.74182 1.766 6.50365 2.2671 7.11104C2.7682 7.71844 3.46372 8.13411 4.236 8.28774C3.93872 8.37821 3.63007 8.42591 3.31934 8.42941C3.10424 8.42689 2.88969 8.40739 2.67767 8.37107C2.89759 9.04842 3.32319 9.64036 3.89523 10.0645C4.46728 10.4887 5.15732 10.724 5.86934 10.7377C4.66701 11.6838 3.18257 12.2001 1.65267 12.2044C1.37412 12.2053 1.09578 12.1886 0.819336 12.1544C2.38136 13.163 4.20168 13.6983 6.061 13.6961C7.34408 13.7094 8.61695 13.4669 9.80527 12.9828C10.9936 12.4987 12.0735 11.7826 12.982 10.8765C13.8905 9.97033 14.6093 8.89223 15.0965 7.70516C15.5836 6.51809 15.8294 5.24585 15.8193 3.96274C15.8193 3.82107 15.8193 3.67107 15.8193 3.52107C16.4732 3.03342 17.0372 2.43559 17.486 1.75441Z" fill="currentColor" />
</svg>
</a>
</li>
<li>
<a
href="https://github.nuxt.dev"
target="_blank"
class="focus-visible:ring-2 text-gray-700 hover:text-black dark:text-gray-300 dark:hover:text-white"
>
<span class="sr-only">Nuxt GitHub Repository</span>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.15269 0.792969C7.17392 0.793051 5.25974 1.49723 3.75266 2.77951C2.24558 4.06179 1.24394 5.83849 0.92697 7.7917C0.609997 9.74492 0.998373 11.7472 2.02261 13.4403C3.04684 15.1333 4.6401 16.4067 6.51729 17.0325C6.93396 17.1055 7.09021 16.8555 7.09021 16.6367C7.09021 16.4388 7.07978 15.7825 7.07978 15.0846C4.98603 15.47 4.44436 14.5742 4.27769 14.1055C4.09276 13.6496 3.79959 13.2456 3.42353 12.9284C3.13186 12.7721 2.71519 12.3867 3.4131 12.3763C3.67959 12.4052 3.93518 12.498 4.15822 12.6467C4.38125 12.7953 4.56516 12.9956 4.69436 13.2305C4.80833 13.4352 4.96159 13.6155 5.14535 13.7609C5.32911 13.9063 5.53975 14.014 5.76522 14.0779C5.99068 14.1418 6.22653 14.1605 6.45926 14.1331C6.69198 14.1056 6.917 14.0325 7.12143 13.918C7.1575 13.4943 7.34631 13.0982 7.65269 12.8034C5.79853 12.5951 3.86103 11.8763 3.86103 8.68883C3.84931 7.86062 4.15493 7.05931 4.71519 6.44924C4.46043 5.72943 4.49024 4.93948 4.79853 4.24091C4.79853 4.24091 5.49642 4.02215 7.09019 5.09508C8.45376 4.72005 9.89328 4.72005 11.2569 5.09508C12.8506 4.01174 13.5485 4.24091 13.5485 4.24091C13.8569 4.93947 13.8867 5.72943 13.6319 6.44924C14.1938 7.05826 14.4997 7.86027 14.486 8.68883C14.486 11.8867 12.5381 12.5951 10.6839 12.8034C10.8828 13.005 11.036 13.247 11.133 13.513C11.2301 13.779 11.2688 14.0628 11.2464 14.3451C11.2464 15.4597 11.236 16.3555 11.236 16.6367C11.236 16.8555 11.3923 17.1159 11.8089 17.0326C13.6828 16.4016 15.2715 15.1253 16.2914 13.4313C17.3112 11.7374 17.6959 9.73616 17.3768 7.78483C17.0576 5.83351 16.0553 4.05911 14.5489 2.77839C13.0425 1.49768 11.1299 0.793998 9.15269 0.792969Z" fill="currentColor" /> </svg>
</svg>
</a>
</li>
</ul>
</div>
</div>
</footer>
</body>
</html>

View File

@ -1,6 +1 @@
{
"title": "Welcome to Nuxt!",
"readDocs": "We highly recommend you take a look at the Nuxt documentation, whether you are new or have previous experience with the framework.",
"followTwitter": "Follow the Nuxt Twitter account to get latest news about releases, new modules, tutorials and tips.",
"starGitHub": "Nuxt is open source and the code is available on GitHub, feel free to star it, participate in discussions or dive into the source."
}
{}

File diff suppressed because it is too large Load Diff

View File

@ -27,10 +27,9 @@
"@nuxt/schema": "workspace:*",
"@types/clear": "0.1.4",
"@types/estree": "1.0.5",
"@types/fs-extra": "11.0.4",
"rollup": "4.18.0",
"unbuild": "latest",
"vue": "3.4.29"
"vue": "3.4.30"
},
"dependencies": {
"@nuxt/kit": "workspace:*",
@ -46,9 +45,8 @@
"escape-string-regexp": "^5.0.0",
"estree-walker": "^3.0.3",
"externality": "^1.0.2",
"fs-extra": "^11.2.0",
"get-port-please": "^3.1.2",
"h3": "^1.12.0",
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
"knitwork": "^1.1.0",
"magic-string": "^0.30.10",
"mlly": "^1.7.1",

View File

@ -112,7 +112,7 @@ export async function buildClient (ctx: ViteBuildContext) {
...ctx.config.resolve?.alias,
'#internal/nuxt/paths': resolve(ctx.nuxt.options.buildDir, 'paths.mjs'),
'#build/plugins': resolve(ctx.nuxt.options.buildDir, 'plugins/client'),
'#internal/nitro': resolve(ctx.nuxt.options.buildDir, 'nitro.client.mjs'),
'nitro/runtime': resolve(ctx.nuxt.options.buildDir, 'nitro.client.mjs'),
},
dedupe: [
'vue',

View File

@ -1,4 +1,6 @@
import fse from 'fs-extra'
import { readFileSync } from 'node:fs'
import { mkdir, rm, writeFile } from 'node:fs/promises'
import { relative, resolve } from 'pathe'
import { withTrailingSlash, withoutLeadingSlash } from 'ufo'
import escapeRE from 'escape-string-regexp'
@ -30,7 +32,7 @@ export async function writeManifest (ctx: ViteBuildContext, css: string[] = [])
const manifestFile = resolve(clientDist, 'manifest.json')
const clientManifest = ctx.nuxt.options.dev
? devClientManifest
: await fse.readJSON(manifestFile)
: JSON.parse(readFileSync(manifestFile, 'utf-8'))
const buildAssetsDir = withTrailingSlash(withoutLeadingSlash(ctx.nuxt.options.app.buildAssetsDir))
const BASE_RE = new RegExp(`^${escapeRE(buildAssetsDir)}`)
@ -47,7 +49,7 @@ export async function writeManifest (ctx: ViteBuildContext, css: string[] = [])
}
}
await fse.mkdirp(serverDist)
await mkdir(serverDist, { recursive: true })
if (ctx.config.build?.cssCodeSplit === false) {
for (const key in clientManifest as Record<string, { file?: string }>) {
@ -64,10 +66,10 @@ export async function writeManifest (ctx: ViteBuildContext, css: string[] = [])
const manifest = normalizeViteManifest(clientManifest)
await ctx.nuxt.callHook('build:manifest', manifest)
const stringifiedManifest = JSON.stringify(manifest, null, 2)
await fse.writeFile(resolve(serverDist, 'client.manifest.json'), stringifiedManifest, 'utf8')
await fse.writeFile(resolve(serverDist, 'client.manifest.mjs'), 'export default ' + stringifiedManifest, 'utf8')
await writeFile(resolve(serverDist, 'client.manifest.json'), stringifiedManifest, 'utf8')
await writeFile(resolve(serverDist, 'client.manifest.mjs'), 'export default ' + stringifiedManifest, 'utf8')
if (!ctx.nuxt.options.dev) {
await fse.rm(manifestFile, { force: true })
await rm(manifestFile, { force: true })
}
}

View File

@ -16,10 +16,10 @@ export function analyzePlugin (ctx: ViteBuildContext): Plugin[] {
async generateBundle (_opts, outputBundle) {
for (const _bundleId in outputBundle) {
const bundle = outputBundle[_bundleId]
if (bundle.type !== 'chunk') { continue }
if (!bundle || bundle.type !== 'chunk') { continue }
const minifiedModuleEntryPromises: Array<Promise<[string, RenderedModule]>> = []
for (const moduleId in bundle.modules) {
const module = bundle.modules[moduleId]
const module = bundle.modules[moduleId]!
minifiedModuleEntryPromises.push(
transform(module.code || '', { minify: true })
.then(result => [moduleId, { ...module, code: result.code }]),

View File

@ -17,7 +17,7 @@ interface ComposableKeysOptions {
composables: Array<{ name: string, source?: string | RegExp, argumentLength: number }>
}
const stringTypes = ['Literal', 'TemplateLiteral']
const stringTypes: Array<string | undefined> = ['Literal', 'TemplateLiteral']
const NUXT_LIB_RE = /node_modules\/(?:nuxt|nuxt3|nuxt-nightly)\//
const SUPPORTED_EXT_RE = /\.(?:m?[jt]sx?|vue)/
@ -182,7 +182,7 @@ class ScopeTracker {
leaveScope () {
this.scopeIndexStack.pop()
this.curScopeKey = this.getKey()
this.scopeIndexStack[this.scopeIndexStack.length - 1]++
this.scopeIndexStack[this.scopeIndexStack.length - 1]!++
}
}
@ -255,9 +255,9 @@ export function detectImportNames (code: string, composableMeta: Record<string,
for (const i of findStaticImports(code)) {
if (NUXT_IMPORT_RE.test(i.specifier)) { continue }
const { namedImports, defaultImport, namespacedImport } = parseStaticImport(i)
for (const name in namedImports || {}) {
addName(namedImports![name], i.specifier)
const { namedImports = {}, defaultImport, namespacedImport } = parseStaticImport(i)
for (const name in namedImports) {
addName(namedImports[name]!, i.specifier)
}
if (defaultImport) {
addName(defaultImport, i.specifier)

View File

@ -38,7 +38,7 @@ export const VitePublicDirsPlugin = createUnplugin((options: { sourcemap?: boole
const s = new MagicString(code)
const q = code.match(/(?<= = )['"`]/)?.[0] || '"'
for (const [full, url] of code.matchAll(CSS_URL_RE)) {
if (resolveFromPublicAssets(url)) {
if (url && resolveFromPublicAssets(url)) {
s.replace(full, `url(${q} + publicAssetsURL(${q}${url}${q}) + ${q})`)
}
}
@ -53,13 +53,13 @@ export const VitePublicDirsPlugin = createUnplugin((options: { sourcemap?: boole
},
generateBundle (_outputOptions, bundle) {
for (const file in bundle) {
const chunk = bundle[file]
const chunk = bundle[file]!
if (!file.endsWith('.css') || chunk.type !== 'asset') { continue }
let css = chunk.source.toString()
let wasReplaced = false
for (const [full, url] of css.matchAll(CSS_URL_RE)) {
if (resolveFromPublicAssets(url)) {
if (url && resolveFromPublicAssets(url)) {
const relativeURL = relative(withLeadingSlash(dirname(file)), url)
css = css.replace(full, `url(${relativeURL})`)
wasReplaced = true

View File

@ -24,7 +24,7 @@ interface SSRStylePluginOptions {
const SUPPORTED_FILES_RE = /\.(?:vue|(?:[cm]?j|t)sx?)$/
export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
const cssMap: Record<string, { files: string[], inBundle: boolean }> = {}
const cssMap: Record<string, { files: string[], inBundle?: boolean }> = {}
const idRefMap: Record<string, string> = {}
const relativeToSrcDir = (path: string) => relative(options.srcDir, path)
@ -63,7 +63,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
const emitted: Record<string, string> = {}
for (const file in cssMap) {
const { files, inBundle } = cssMap[file]
const { files, inBundle } = cssMap[file]!
// File has been tree-shaken out of build (or there are no styles to inline)
if (!files.length || !inBundle) { continue }
const fileName = filename(file)
@ -115,18 +115,19 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
// 'Teleport' CSS chunks that made it into the bundle on the client side
// to be inlined on server rendering
if (options.mode === 'client') {
options.clientCSSMap[moduleId] ||= new Set()
const moduleMap = options.clientCSSMap[moduleId] ||= new Set()
if (isCSS(moduleId)) {
// Vue files can (also) be their own entrypoints as they are tracked separately
if (isVue(moduleId)) {
options.clientCSSMap[moduleId].add(moduleId)
moduleMap.add(moduleId)
const parent = moduleId.replace(/\?.+$/, '')
options.clientCSSMap[parent] ||= new Set()
options.clientCSSMap[parent].add(moduleId)
const parentMap = options.clientCSSMap[parent] ||= new Set()
parentMap.add(moduleId)
}
// This is required to track CSS in entry chunk
if (isEntry) {
options.clientCSSMap[chunk.facadeModuleId!].add(moduleId)
if (isEntry && chunk.facadeModuleId) {
const facadeMap = options.clientCSSMap[chunk.facadeModuleId] ||= new Set()
facadeMap.add(moduleId)
}
}
continue
@ -134,7 +135,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
const relativePath = relativeToSrcDir(moduleId)
if (relativePath in cssMap) {
cssMap[relativePath].inBundle = cssMap[relativePath].inBundle ?? ((isVue(moduleId) && relativeToSrcDir(moduleId)) || isEntry)
cssMap[relativePath]!.inBundle = cssMap[relativePath]!.inBundle ?? ((isVue(moduleId) && !!relativeToSrcDir(moduleId)) || isEntry)
}
}
@ -146,7 +147,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
// or include it here in the client build so it is emitted in the CSS.
if (id === options.entry && (options.shouldInline === true || (typeof options.shouldInline === 'function' && options.shouldInline(id)))) {
const s = new MagicString(code)
options.clientCSSMap[id] ||= new Set()
const idClientCSSMap = options.clientCSSMap[id] ||= new Set()
if (!options.globalCSS.length) { return }
for (const file of options.globalCSS) {
@ -160,7 +161,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
s.prepend(`${genImport(file)}\n`)
continue
}
options.clientCSSMap[id].add(resolved.id)
idClientCSSMap.add(resolved.id)
}
if (s.hasChanged()) {
return {
@ -184,7 +185,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
}
const relativeId = relativeToSrcDir(id)
cssMap[relativeId] = cssMap[relativeId] || { files: [] }
const idMap = cssMap[relativeId] ||= { files: [] }
const emittedIds = new Set<string>()
@ -208,7 +209,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
})
idRefMap[relativeToSrcDir(file)] = ref
cssMap[relativeId].files.push(ref)
idMap.files.push(ref)
}
if (!SUPPORTED_FILES_RE.test(pathname)) { return }
@ -235,7 +236,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
})
idRefMap[relativeToSrcDir(resolved.id)] = ref
cssMap[relativeId].files.push(ref)
idMap.files.push(ref)
}
},
}

View File

@ -9,7 +9,7 @@ export function typeCheckPlugin (options: { sourcemap?: boolean } = {}): Plugin
name: 'nuxt:type-check',
configResolved (config) {
const input = config.build.rollupOptions.input
if (input && typeof input !== 'string' && !Array.isArray(input)) {
if (input && typeof input !== 'string' && !Array.isArray(input) && input.entry) {
entry = input.entry
}
},

View File

@ -39,7 +39,7 @@ export default function virtual (vfs: Record<string, string>): Plugin {
const idNoPrefix = id.slice(PREFIX.length)
if (idNoPrefix in vfs) {
return {
code: vfs[idNoPrefix],
code: vfs[idNoPrefix] || '',
map: null,
}
}

View File

@ -61,7 +61,7 @@ export async function buildServer (ctx: ViteBuildContext) {
},
ssr: {
external: [
'#internal/nitro', '#internal/nitro/utils',
'nitro/runtime',
],
noExternal: [
...transpile({ isServer: true, isDev: ctx.nuxt.options.dev }),
@ -80,7 +80,7 @@ export async function buildServer (ctx: ViteBuildContext) {
ssr: true,
rollupOptions: {
input: { server: entry },
external: ['#internal/nitro', '#internal/nuxt/paths'],
external: ['nitro/runtime', '#internal/nuxt/paths', '#internal/nuxt/app-config'],
output: {
entryFileNames: '[name].mjs',
format: 'module',
@ -104,8 +104,8 @@ export async function buildServer (ctx: ViteBuildContext) {
} satisfies vite.InlineConfig, ctx.nuxt.options.vite.$server || {}))
if (!ctx.nuxt.options.dev) {
const nitroDependencies = await tryResolveModule('nitropack/package.json', ctx.nuxt.options.modulesDir)
.then(r => import(r!)).then(r => r.dependencies ? Object.keys(r.dependencies) : []).catch(() => [])
const nitroDependencies = await tryResolveModule('nitro/runtime/meta', ctx.nuxt.options.modulesDir)
.then(r => import(r!)).then(r => r.runtimeDependencies || []).catch(() => [])
if (Array.isArray(serverConfig.ssr!.external)) {
serverConfig.ssr!.external.push(
// explicit dependencies we use in our ssr renderer - these can be inlined (if necessary) in the nitro build

View File

@ -1,11 +1,5 @@
import { hash } from 'ohash'
export { isVue } from '../../../nuxt/src/core/utils/plugins'
export function uniq<T> (arr: T[]): T[] {
return Array.from(new Set(arr))
}
// Copied from vue-bundle-renderer utils
const IS_CSS_RE = /\.(?:css|scss|sass|postcss|pcss|less|stylus|styl)(?:\?[^.]+)?$/
@ -13,10 +7,6 @@ export function isCSS (file: string) {
return IS_CSS_RE.test(file)
}
export function hashId (id: string) {
return '$id_' + hash(id)
}
export function matchWithStringOrRegex (value: string, matcher: string | RegExp) {
if (typeof matcher === 'string') {
return value === matcher

View File

@ -10,7 +10,7 @@ interface Envs {
export function transpile (envs: Envs): Array<string | RegExp> {
const nuxt = useNuxt()
const transpile: Array<string | RegExp> = []
const transpile: RegExp[] = []
for (let pattern of nuxt.options.build.transpile) {
if (typeof pattern === 'function') {

View File

@ -1,7 +0,0 @@
import { join } from 'pathe'
import fse from 'fs-extra'
export const wpfs = {
...fse,
join,
} as typeof fse & { join: typeof join }

View File

@ -1,7 +1,7 @@
import { writeFile } from 'node:fs/promises'
import { pathToFileURL } from 'node:url'
import { createApp, createError, defineEventHandler, defineLazyEventHandler, eventHandler, toNodeListener } from 'h3'
import { ViteNodeServer } from 'vite-node/server'
import fse from 'fs-extra'
import { isAbsolute, normalize, resolve } from 'pathe'
import { addDevServerHandler } from '@nuxt/kit'
import { isFileServingAllowed } from 'vite'
@ -181,11 +181,11 @@ export async function initViteNodeServer (ctx: ViteBuildContext) {
const serverResolvedPath = resolve(distDir, 'runtime/vite-node.mjs')
const manifestResolvedPath = resolve(distDir, 'runtime/client.manifest.mjs')
await fse.writeFile(
await writeFile(
resolve(ctx.nuxt.options.buildDir, 'dist/server/server.mjs'),
`export { default } from ${JSON.stringify(pathToFileURL(serverResolvedPath).href)}`,
)
await fse.writeFile(
await writeFile(
resolve(ctx.nuxt.options.buildDir, 'dist/server/client.manifest.mjs'),
`export { default } from ${JSON.stringify(pathToFileURL(manifestResolvedPath).href)}`,
)

View File

@ -41,8 +41,8 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
...app.plugins.map(p => dirname(p.src)),
...app.middleware.map(m => dirname(m.path)),
...Object.values(app.layouts || {}).map(l => dirname(l.file)),
dirname(nuxt.apps.default.rootComponent!),
dirname(nuxt.apps.default.errorComponent!),
dirname(nuxt.apps.default!.rootComponent!),
dirname(nuxt.apps.default!.errorComponent!),
]),
].filter(d => d && existsSync(d))
@ -183,7 +183,7 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
clientCSSMap,
chunksWithInlinedCSS,
shouldInline: ctx.nuxt.options.features.inlineStyles,
components: ctx.nuxt.apps.default.components,
components: ctx.nuxt.apps.default!.components || [],
globalCSS: ctx.nuxt.options.css,
mode: isServer ? 'server' : 'client',
entry: ctx.entry,
@ -193,7 +193,7 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
// Remove CSS entries for files that will have inlined styles
ctx.nuxt.hook('build:manifest', (manifest) => {
for (const key in manifest) {
const entry = manifest[key]
const entry = manifest[key]!
const shouldRemoveCSS = chunksWithInlinedCSS.has(key) && !entry.isEntry
if (entry.isEntry && chunksWithInlinedCSS.has(key)) {
// @ts-expect-error internal key

Some files were not shown because too many files have changed in this diff Show More