Merge branch 'main' into patch-21

This commit is contained in:
Michael Brevard 2024-06-07 16:08:20 +03:00 committed by GitHub
commit 7137a7b3b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 1059 additions and 714 deletions

View File

@ -83,7 +83,7 @@ jobs:
run: pnpm install
- name: Initialize CodeQL
uses: github/codeql-action/init@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6
uses: github/codeql-action/init@2e230e8fe0ad3a14a340ad0815ddb96d599d2aff # v3.25.8
with:
languages: javascript
queries: +security-and-quality
@ -95,7 +95,7 @@ jobs:
path: packages
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6
uses: github/codeql-action/analyze@2e230e8fe0ad3a14a340ad0815ddb96d599d2aff # v3.25.8
with:
category: "/language:javascript"

View File

@ -19,4 +19,4 @@ jobs:
- name: 'Checkout Repository'
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: 'Dependency Review'
uses: actions/dependency-review-action@0c155c5e8556a497adf53f2c18edabf945ed8e70 # v4.3.2
uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3

View File

@ -1,61 +0,0 @@
name: nuxt2-nightly
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * *'
# https://github.com/vitejs/vite/blob/main/.github/workflows/ci.yml
env:
# 7 GiB by default on GitHub, setting to 6 GiB
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
NODE_OPTIONS: --max-old-space-size=6144
permissions:
contents: read
jobs:
nightly:
if: github.repository_owner == 'nuxt'
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with:
ref: '2.x'
fetch-depth: 0 # All history
- name: fetch tags
run: git fetch --depth=1 origin "+refs/tags/*:refs/tags/*"
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 18
registry-url: 'https://registry.npmjs.org'
- name: install
run: yarn --check-files --frozen-lockfile --non-interactive
- name: lint
run: yarn test:lint
- name: audit
run: yarn run audit
- name: build
run: yarn test:fixtures -i
- name: lint app
run: yarn lint:app
- name: test types
run: yarn test:types
- name: test dev
run: yarn test:dev
- name: test unit
run: yarn test:unit
- name: test e2e
run: yarn test:e2e
- name: bump version
run: yarn lerna version --yes --no-changelog --no-git-tag-version --no-push --force-publish "*" --loglevel verbose
- name: build
run: PACKAGE_SUFFIX=edge yarn build
- name: publish
run: ./scripts/workspace-run npm publish -q
env:
NODE_AUTH_TOKEN: ${{secrets.NODE_AUTH_TOKEN}}
NPM_CONFIG_PROVENANCE: true

View File

@ -68,7 +68,7 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6
uses: github/codeql-action/upload-sarif@2e230e8fe0ad3a14a340ad0815ddb96d599d2aff # v3.25.8
if: github.repository == 'nuxt/nuxt' && success()
with:
sarif_file: results.sarif

View File

@ -623,3 +623,37 @@ const { data } = await useFetch('/api/superjson', {
})
</script>
```
## Recipes
### Consuming SSE (Server Sent Events) via POST request
::tip
If you're consuming SSE via GET request, you can use [`EventSource`](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) or VueUse composable [`useEventSource`](https://vueuse.org/core/useEventSource/).
::
When consuming SSE via POST request, you need to handle the connection manually. Here's how you can do it:
```ts
// Make a POST request to the SSE endpoint
const response = await $fetch<ReadableStream>('/chats/ask-ai', {
method: 'POST',
body: {
query: "Hello AI, how are you?",
},
responseType: 'stream',
})
// Create a new ReadableStream from the response with TextDecoderStream to get the data as text
const reader = response.pipeThrough(new TextDecoderStream()).getReader()
// Read the chunk of data as we get it
while (true) {
const { value, done } = await reader.read()
if (done)
break
console.log('Received:', value)
}
```

View File

@ -69,6 +69,36 @@ If you do use `ssr: false`, you should also place an HTML file in `~/app/spa-loa
:read-more{title="SPA Loading Template" to="/docs/api/configuration/nuxt-config#spaloadingtemplate"}
::
::tip{to="https://www.youtube.com/watch?v=7Lr0QTP1Ro8" icon="i-logos-youtube-icon" target="_blank"}
Watch a video from Alexander Lichter about **Building a plain SPA with Nuxt!?**.
::
### Deploying a Static Client-Rendered App
If you deploy your app to [static hosting](/docs/getting-started/deployment#static-hosting) with the `nuxi generate` or `nuxi build --prerender` commands, then by default, Nuxt will render every page as a separate static HTML file.
If you are using purely client-side rendering, then this might be unnecessary. You might only need a single `index.html` file, plus `200.html` and `404.html` fallbacks, which you can tell your static web host to serve up for all requests.
In order to achieve this we can change how the routes are prerendered. Just add this to [your hooks](/docs/api/advanced/hooks#nuxt-hooks-build-time) in your `nuxt.config.ts`:
```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
hooks: {
'prerender:routes' ({ routes }) {
routes.clear() // Do not generate any routes (except the defaults)
}
},
})
```
This will produce three files:
- `index.html`
- `200.html`
- `404.html`
The `200.html` and `404.html` might be useful for the hosting provider you are using.
## Hybrid Rendering
Hybrid rendering allows different caching rules per route using **Route Rules** and decides how the server should respond to a new request on a given URL.

View File

@ -52,6 +52,18 @@ export default defineNuxtRouteMiddleware((to, from) => {
### External URL
The `external` parameter in `navigateTo` influences how navigating to URLs is handled:
- **Without `external: true`**:
- Internal URLs navigate as expected.
- External URLs throw an error.
- **With `external: true`**:
- Internal URLs navigate with a full-page reload.
- External URLs navigate as expected.
#### Example
```vue
<script setup lang="ts">
// will throw an error;

View File

@ -12,7 +12,7 @@
"build:stub": "pnpm dev:prepare",
"cleanup": "rimraf 'packages/**/node_modules' 'playground/node_modules' 'node_modules'",
"dev": "pnpm play",
"dev:prepare": "pnpm --filter './packages/**' prepack --stub",
"dev:prepare": "pnpm --filter './packages/**' prepack --stub && pnpm --filter './packages/ui-templates' build",
"lint": "eslint . --cache",
"lint:fix": "eslint . --cache --fix",
"lint:docs": "markdownlint ./docs && case-police 'docs/**/*.md' *.md",
@ -46,7 +46,7 @@
"vue": "3.4.27"
},
"devDependencies": {
"@eslint/js": "9.3.0",
"@eslint/js": "9.4.0",
"@nuxt/eslint-config": "0.3.13",
"@nuxt/kit": "workspace:*",
"@nuxt/test-utils": "3.13.1",
@ -54,7 +54,7 @@
"@testing-library/vue": "8.1.0",
"@types/eslint__js": "8.42.3",
"@types/fs-extra": "11.0.4",
"@types/node": "20.12.13",
"@types/node": "20.14.2",
"@types/semver": "7.5.8",
"@vitest/coverage-v8": "1.6.0",
"@vue/test-utils": "2.4.6",
@ -62,7 +62,7 @@
"changelogen": "0.5.5",
"consola": "3.2.3",
"devalue": "5.0.0",
"eslint": "9.3.0",
"eslint": "9.4.0",
"eslint-plugin-no-only-tests": "3.1.0",
"eslint-plugin-perfectionist": "2.10.0",
"eslint-typegen": "0.2.4",
@ -91,7 +91,7 @@
"vue-router": "4.3.2",
"vue-tsc": "2.0.19"
},
"packageManager": "pnpm@9.1.3",
"packageManager": "pnpm@9.2.0",
"engines": {
"node": "^16.10.0 || >=18.0.0"
},

View File

@ -37,7 +37,7 @@
"jiti": "^1.21.0",
"klona": "^2.0.6",
"knitwork": "^1.1.0",
"mlly": "^1.7.0",
"mlly": "^1.7.1",
"pathe": "^1.1.2",
"pkg-types": "^1.1.1",
"scule": "^1.3.0",

View File

@ -60,14 +60,14 @@
},
"dependencies": {
"@nuxt/devalue": "^2.0.2",
"@nuxt/devtools": "^1.3.2",
"@nuxt/devtools": "^1.3.3",
"@nuxt/kit": "workspace:*",
"@nuxt/schema": "workspace:*",
"@nuxt/telemetry": "^2.5.4",
"@nuxt/vite-builder": "workspace:*",
"@unhead/dom": "^1.9.11",
"@unhead/ssr": "^1.9.11",
"@unhead/vue": "^1.9.11",
"@unhead/dom": "^1.9.12",
"@unhead/ssr": "^1.9.12",
"@unhead/vue": "^1.9.12",
"@vue/shared": "^3.4.27",
"acorn": "8.11.3",
"c12": "^1.10.0",
@ -88,7 +88,7 @@
"klona": "^2.0.6",
"knitwork": "^1.1.0",
"magic-string": "^0.30.10",
"mlly": "^1.7.0",
"mlly": "^1.7.1",
"nitropack": "^2.9.6",
"nuxi": "^3.11.1",
"nypm": "^0.3.8",

View File

@ -24,9 +24,13 @@ function fetchManifest () {
if (!isAppManifestEnabled) {
throw new Error('[nuxt] app manifest should be enabled with `experimental.appManifest`')
}
manifest = $fetch<NuxtAppManifest>(buildAssetsURL(`builds/meta/${useRuntimeConfig().app.buildId}.json`))
manifest = $fetch<NuxtAppManifest>(buildAssetsURL(`builds/meta/${useRuntimeConfig().app.buildId}.json`), {
responseType: 'json',
})
manifest.then((m) => {
matcher = createMatcherFromExport(m.matcher)
}).catch((e) => {
console.error('[nuxt] Error fetching app manifest.', e)
})
return manifest
}
@ -48,5 +52,14 @@ export async function getRouteRules (url: string) {
return defu({} as Record<string, any>, ..._routeRulesMatcher.matchAll(url).reverse())
}
await getAppManifest()
return defu({} as Record<string, any>, ...matcher.matchAll(url).reverse())
if (!matcher) {
console.error('[nuxt] Error creating app manifest matcher.', matcher)
return {}
}
try {
return defu({} as Record<string, any>, ...matcher.matchAll(url).reverse())
} catch (e) {
console.error('[nuxt] Error matching route rules.', e)
return {}
}
}

View File

@ -4,7 +4,7 @@ import ignore from 'ignore'
import type { LoadNuxtOptions } from '@nuxt/kit'
import { addBuildPlugin, addComponent, addPlugin, addRouteMiddleware, addServerPlugin, addVitePlugin, addWebpackPlugin, installModule, loadNuxtConfig, logger, nuxtCtx, resolveAlias, resolveFiles, resolveIgnorePatterns, resolvePath, tryResolveModule, useNitro } from '@nuxt/kit'
import { resolvePath as _resolvePath } from 'mlly'
import type { Nuxt, NuxtHooks, NuxtOptions, RuntimeConfig } from 'nuxt/schema'
import type { Nuxt, NuxtHooks, NuxtModule, NuxtOptions, RuntimeConfig } from 'nuxt/schema'
import type { PackageJson } from 'pkg-types'
import { readPackageJSON, resolvePackageJSON } from 'pkg-types'
@ -277,7 +277,7 @@ async function initNuxt (nuxt: Nuxt) {
// Init user modules
await nuxt.callHook('modules:before')
const modulesToInstall = []
const modulesToInstall = new Map<string | NuxtModule, Record<string, any>>()
const watchedPaths = new Set<string>()
const specifiedModules = new Set<string>()
@ -300,12 +300,21 @@ async function initNuxt (nuxt: Nuxt) {
watchedPaths.add(mod)
if (specifiedModules.has(mod)) { continue }
specifiedModules.add(mod)
modulesToInstall.push(mod)
modulesToInstall.set(mod, {})
}
}
// Register user and then ad-hoc modules
modulesToInstall.push(...nuxt.options.modules, ...nuxt.options._modules)
for (const key of ['modules', '_modules'] as const) {
for (const item of nuxt.options[key as 'modules']) {
if (item) {
const [key, options = {}] = Array.isArray(item) ? item : [item]
if (!modulesToInstall.has(key)) {
modulesToInstall.set(key, options)
}
}
}
}
// Add <NuxtWelcome>
addComponent({
@ -481,12 +490,8 @@ async function initNuxt (nuxt: Nuxt) {
addPlugin(resolve(nuxt.options.appDir, 'plugins/debug'))
}
for (const m of modulesToInstall) {
if (Array.isArray(m)) {
await installModule(m[0], m[1])
} else {
await installModule(m, {})
}
for (const [key, options] of modulesToInstall) {
await installModule(key, options)
}
// (Re)initialise ignore handler with resolved ignores from modules

View File

@ -39,9 +39,9 @@
"@types/file-loader": "5.0.4",
"@types/pug": "2.0.10",
"@types/sass-loader": "8.0.8",
"@unhead/schema": "1.9.11",
"@unhead/schema": "1.9.12",
"@vitejs/plugin-vue": "5.0.4",
"@vitejs/plugin-vue-jsx": "3.1.0",
"@vitejs/plugin-vue-jsx": "4.0.0",
"@vue/compiler-core": "3.4.27",
"@vue/compiler-sfc": "3.4.27",
"@vue/language-core": "2.0.19",

View File

@ -141,12 +141,14 @@ export interface AppConfigInput extends CustomAppConfig {
server?: never
}
type Serializable<T> = T extends Function ? never : T extends Promise<infer U> ? Serializable<U> : T extends Record<string, any> ? { [K in keyof T]: Serializable<T[K]> } : T
export interface NuxtAppConfig {
head: AppHeadMetaObject
layoutTransition: boolean | TransitionProps
pageTransition: boolean | TransitionProps
head: Serializable<AppHeadMetaObject>
layoutTransition: boolean | Serializable<TransitionProps>
pageTransition: boolean | Serializable<TransitionProps>
viewTransition?: boolean | 'always'
keepalive: boolean | KeepAliveProps
keepalive: boolean | Serializable<KeepAliveProps>
}
export interface AppConfig {

View File

@ -20,7 +20,7 @@
},
"devDependencies": {
"@types/html-minifier": "4.0.5",
"@unocss/reset": "0.60.3",
"@unocss/reset": "0.60.4",
"critters": "0.0.22",
"execa": "9.1.0",
"globby": "14.0.1",
@ -28,9 +28,9 @@
"jiti": "1.21.0",
"knitwork": "1.1.0",
"pathe": "1.1.2",
"prettier": "3.2.5",
"prettier": "3.3.1",
"scule": "1.3.0",
"unocss": "0.60.3",
"unocss": "0.60.4",
"vite": "5.2.12"
}
}

View File

@ -33,13 +33,13 @@
},
"dependencies": {
"@nuxt/kit": "workspace:*",
"@rollup/plugin-replace": "^5.0.5",
"@rollup/plugin-replace": "^5.0.7",
"@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"@vitejs/plugin-vue-jsx": "^4.0.0",
"autoprefixer": "^10.4.19",
"clear": "^0.1.0",
"consola": "^3.2.3",
"cssnano": "^7.0.1",
"cssnano": "^7.0.2",
"defu": "^6.1.4",
"esbuild": "^0.21.4",
"escape-string-regexp": "^5.0.0",
@ -50,7 +50,7 @@
"h3": "^1.11.1",
"knitwork": "^1.1.0",
"magic-string": "^0.30.10",
"mlly": "^1.7.0",
"mlly": "^1.7.1",
"ohash": "^1.1.3",
"pathe": "^1.1.2",
"perfect-debounce": "^1.0.0",

View File

@ -30,7 +30,7 @@
"autoprefixer": "^10.4.19",
"css-loader": "^7.1.2",
"css-minimizer-webpack-plugin": "^7.0.0",
"cssnano": "^7.0.1",
"cssnano": "^7.0.2",
"defu": "^6.1.4",
"esbuild-loader": "^4.1.0",
"escape-string-regexp": "^5.0.0",
@ -44,7 +44,7 @@
"magic-string": "^0.30.10",
"memfs": "^4.9.2",
"mini-css-extract-plugin": "^2.9.0",
"mlly": "^1.7.0",
"mlly": "^1.7.1",
"ohash": "^1.1.3",
"pathe": "^1.1.2",
"pify": "^6.1.0",
@ -66,7 +66,7 @@
"webpack-bundle-analyzer": "^4.10.2",
"webpack-dev-middleware": "^7.2.1",
"webpack-hot-middleware": "^2.26.1",
"webpack-virtual-modules": "^0.6.1",
"webpack-virtual-modules": "^0.6.2",
"webpackbar": "^6.0.1"
},
"devDependencies": {

File diff suppressed because it is too large Load Diff

View File

@ -75,7 +75,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"527k"`)
const modules = await analyzeSizes('node_modules/**/*', serverDir)
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"76.1k"`)
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"76.2k"`)
const packages = modules.files
.filter(m => m.endsWith('package.json'))

View File

@ -15,6 +15,18 @@ export default defineNuxtConfig({
extends: [
'./extends/node_modules/foo',
],
app: {
head: {
// @ts-expect-error Promises are not allowed
title: Promise.resolve('Nuxt Fixture'),
// @ts-expect-error Functions are not allowed
titleTemplate: title => 'test',
},
pageTransition: {
// @ts-expect-error Functions are not allowed
onBeforeEnter: el => console.log(el),
},
},
runtimeConfig: {
baseURL: '',
baseAPIToken: '',

View File

@ -320,6 +320,7 @@ describe('runtimeConfig', () => {
describe('head', () => {
it('correctly types nuxt.config options', () => {
// @ts-expect-error invalid head option
defineNuxtConfig({ app: { head: { titleTemplate: () => 'test' } } })
defineNuxtConfig({
app: {