mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 08:33:53 +00:00
Merge branch 'main' into patch-21
This commit is contained in:
commit
97fd06e4ff
2
.github/workflows/autofix-docs.yml
vendored
2
.github/workflows/autofix-docs.yml
vendored
@ -30,4 +30,4 @@ jobs:
|
||||
- name: Lint (docs)
|
||||
run: pnpm lint:docs:fix
|
||||
|
||||
- uses: autofix-ci/action@ea32e3a12414e6d3183163c3424a7d7a8631ad84
|
||||
- uses: autofix-ci/action@dd55f44df8f7cdb7a6bf74c78677eb8acd40cd0a
|
||||
|
2
.github/workflows/autofix.yml
vendored
2
.github/workflows/autofix.yml
vendored
@ -52,4 +52,4 @@ jobs:
|
||||
- name: Lint (code)
|
||||
run: pnpm lint:fix
|
||||
|
||||
- uses: autofix-ci/action@ea32e3a12414e6d3183163c3424a7d7a8631ad84
|
||||
- uses: autofix-ci/action@dd55f44df8f7cdb7a6bf74c78677eb8acd40cd0a
|
||||
|
4
.github/workflows/release-pr.yml
vendored
4
.github/workflows/release-pr.yml
vendored
@ -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
|
||||
|
2
.github/workflows/scorecards.yml
vendored
2
.github/workflows/scorecards.yml
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
```
|
||||
|
@ -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]
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -90,7 +90,7 @@ declare module '#app' {
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'nitropack' {
|
||||
declare module 'nitro/types' {
|
||||
interface NitroRuntimeHooks {
|
||||
'your-nitro-hook': () => void;
|
||||
}
|
||||
|
@ -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 {}
|
||||
}
|
||||
|
@ -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.
|
||||
::
|
||||
|
@ -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.
|
||||
::
|
||||
|
@ -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..
|
||||
::
|
||||
|
@ -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
|
||||
|
@ -148,17 +148,6 @@ export default createConfigForNuxt({
|
||||
],
|
||||
},
|
||||
],
|
||||
'import/order': [
|
||||
'error',
|
||||
{
|
||||
pathGroups: [
|
||||
{
|
||||
group: 'external',
|
||||
pattern: '#vue-router',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
'jsdoc/check-tag-names': [
|
||||
'error',
|
||||
{
|
||||
|
30
package.json
30
package.json
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ export default defineBuildConfig({
|
||||
externals: [
|
||||
'@nuxt/schema',
|
||||
'nitropack',
|
||||
'nitro',
|
||||
'webpack',
|
||||
'vite',
|
||||
'h3',
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
||||
|
@ -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('')}`)
|
||||
}
|
||||
|
||||
|
@ -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}\`.`,
|
||||
)
|
||||
|
@ -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> {
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -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 }
|
||||
|
@ -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))
|
||||
|
@ -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}"` : ''
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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') {
|
||||
|
@ -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],
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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__
|
||||
},
|
||||
})
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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])/
|
||||
|
@ -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 : ''}
|
||||
|
@ -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() })
|
||||
|
||||
|
@ -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)) {
|
||||
|
@ -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.')])
|
||||
}
|
||||
|
||||
|
38
packages/nuxt/src/core/runtime/nitro/app-config.ts
Normal file
38
packages/nuxt/src/core/runtime/nitro/app-config.ts
Normal 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)
|
||||
}
|
@ -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'
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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`
|
||||
|
@ -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'
|
||||
|
@ -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)}`
|
||||
|
@ -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>',
|
||||
' }',
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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],
|
||||
|
@ -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'
|
||||
|
@ -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>
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
|
@ -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' })
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -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', () => {
|
||||
|
@ -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
|
||||
|
25
packages/nuxt/types.d.ts
vendored
25
packages/nuxt/types.d.ts
vendored
@ -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
|
||||
|
@ -31,6 +31,7 @@ export default defineBuildConfig({
|
||||
'vue',
|
||||
'unctx',
|
||||
'hookable',
|
||||
'nitro',
|
||||
'nitropack',
|
||||
'webpack',
|
||||
'webpack-bundle-analyzer',
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -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.
|
||||
|
@ -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: [],
|
||||
})
|
||||
|
@ -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',
|
||||
]
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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()}`
|
||||
|
@ -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),
|
||||
|
@ -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",
|
||||
|
@ -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>
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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": ""
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"loading": "Loading"
|
||||
"loading": "Loading",
|
||||
"version": "4.0"
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
{
|
||||
"appName": "Nuxt",
|
||||
"version": ""
|
||||
"appName": "Nuxt"
|
||||
}
|
||||
|
@ -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"><NuxtWelcome /></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"><NuxtWelcome/></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>
|
||||
|
@ -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
@ -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",
|
||||
|
@ -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',
|
||||
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
@ -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 }]),
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
},
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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') {
|
||||
|
@ -1,7 +0,0 @@
|
||||
import { join } from 'pathe'
|
||||
import fse from 'fs-extra'
|
||||
|
||||
export const wpfs = {
|
||||
...fse,
|
||||
join,
|
||||
} as typeof fse & { join: typeof join }
|
@ -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)}`,
|
||||
)
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user