Skip to content

Commit

Permalink
fix: improved default router.options scrollBehaviour
Browse files Browse the repository at this point in the history
handle first page load and page refresh scenarios (restore scroll position without jumps), fixes hash-navigation when changing pages and browser back/forward positions, especiall when page transitions are involved. fixes nuxt#24941, nuxt#22487, nuxt#25030 and nuxt#19664

Signed-off-by: Bernhard Berger <bernhard.berger@gmail.com>
  • Loading branch information
bernhardberger committed Jan 7, 2024
2 parents 797aa2d + 6736dec commit 2f1d8b6
Show file tree
Hide file tree
Showing 48 changed files with 1,094 additions and 978 deletions.
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Expand Up @@ -33,4 +33,5 @@ Please carefully read the contribution docs before creating a pull request
<!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [ ] I have linked an issue or discussion.
- [ ] I have added tests (if possible).
- [ ] I have updated the documentation accordingly.
2 changes: 1 addition & 1 deletion .github/workflows/benchmark.yml
Expand Up @@ -48,7 +48,7 @@ jobs:
run: pnpm build

- name: Run benchmarks
uses: CodSpeedHQ/action@ad0378e48c3cb4c700f1cdc5e10943dbad3cc4ec # v2.0.2
uses: CodSpeedHQ/action@5ef3186765fa3fafd8b2729b064025d19626050c # v2.0.3
with:
run: pnpm vitest bench
token: ${{ secrets.CODSPEED_TOKEN }}
2 changes: 1 addition & 1 deletion .github/workflows/check-links.yml
Expand Up @@ -28,7 +28,7 @@ jobs:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

- name: Lychee link checker
uses: lycheeverse/lychee-action@ef8c8f32c278e9eed290c6e6c96748f16b6cd335 # for v1.8.0
uses: lycheeverse/lychee-action@22134d37a1fff6c2974df9c92a7c7e1e86a08f9c # for v1.8.0
with:
# arguments with file types to check
args: >-
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/dependency-review.yml
Expand Up @@ -19,4 +19,4 @@ jobs:
- name: 'Checkout Repository'
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: 'Dependency Review'
uses: actions/dependency-review-action@01bc87099ba56df1e897b6874784491ea6309bc4 # v3.1.4
uses: actions/dependency-review-action@c74b580d73376b7750d3d2a50bfb8adc2c937507 # v3.1.5
5 changes: 5 additions & 0 deletions docs/1.getting-started/2.installation.md
Expand Up @@ -96,6 +96,11 @@ bun install

::

::callout
If you are using Yarn 2+ (Berry), add `nodeLinker: node-modules` to your `.yarnrc.yml` file.
[You can follow this issue status here](https://github.com/nuxt/nuxt/issues/22861)
::

## Development Server

Now you'll be able to start your Nuxt app in development mode:
Expand Down
6 changes: 3 additions & 3 deletions docs/2.guide/1.concepts/7.esm.md
Expand Up @@ -52,7 +52,7 @@ So in Nuxt 2, the bundler (webpack) would pull in the CJS file ('main') for the

However, in recent Node.js LTS releases, it is now possible to [use native ESM module](https://nodejs.org/api/esm.html) within Node.js. That means that Node.js itself can process JavaScript using ESM syntax, although it doesn't do it by default. The two most common ways to enable ESM syntax are:

- set `type: 'module'` within your `package.json` and keep using `.js` extension
- set `"type": "module"` within your `package.json` and keep using `.js` extension
- use the `.mjs` file extensions (recommended)

This is what we do for Nuxt Nitro; we output a `.output/server/index.mjs` file. That tells Node.js to treat this file as a native ES module.
Expand All @@ -67,7 +67,7 @@ Node supports the following kinds of imports (see [docs](https://nodejs.org/api/

1. files ending in `.mjs` - these are expected to use ESM syntax
1. files ending in `.cjs` - these are expected to use CJS syntax
1. files ending in `.js` - these are expected to use CJS syntax unless their `package.json` has `type: 'module'`
1. files ending in `.js` - these are expected to use CJS syntax unless their `package.json` has `"type": "module"`

### What Kinds of Problems Can There Be?

Expand Down Expand Up @@ -211,7 +211,7 @@ The good news is that it's relatively simple to fix issues of ESM compatibility.
1. **You can opt to make your entire library ESM-only**.
This would mean setting `type: 'module'` in your `package.json` and ensuring that your built library uses ESM syntax. However, you may face issues with your dependencies - and this approach means your library can _only_ be consumed in an ESM context.
This would mean setting `"type": "module"` in your `package.json` and ensuring that your built library uses ESM syntax. However, you may face issues with your dependencies - and this approach means your library can _only_ be consumed in an ESM context.
### Migration
Expand Down
2 changes: 1 addition & 1 deletion docs/2.guide/2.directory-structure/1.pages.md
Expand Up @@ -263,7 +263,7 @@ Of course, you are welcome to define metadata for your own use throughout your a

#### `alias`

You can define page aliases. They allow you to access the same page from different paths. It can be either a string or an array of strings as defined [here](https://router.vuejs.org/guide/essentials/redirect-and-alias.html#alias) on vue-router documentation.
You can define page aliases. They allow you to access the same page from different paths. It can be either a string or an array of strings as defined [here](https://router.vuejs.org/guide/essentials/redirect-and-alias.html#Alias) on vue-router documentation.

#### `keepalive`

Expand Down
19 changes: 19 additions & 0 deletions docs/2.guide/2.directory-structure/1.plugins.md
Expand Up @@ -157,6 +157,7 @@ Normally, Vue.js composables are bound to the current component instance while p

If you would like to provide a helper on the [`NuxtApp`](/docs/api/composables/use-nuxt-app) instance, return it from the plugin under a `provide` key.

::code-group
```ts [plugins/hello.ts]
export default defineNuxtPlugin(() => {
return {
Expand All @@ -166,6 +167,19 @@ export default defineNuxtPlugin(() => {
}
})
```
```ts [plugins/hello-object-syntax.ts]
export default defineNuxtPlugin({
name: 'hello',
setup () {
return {
provide: {
hello: (msg: string) => `Hello ${msg}!`
}
}
}
})
```
::

You can then use the helper in your components:

Expand All @@ -186,6 +200,11 @@ const { $hello } = useNuxtApp()
Note that we highly recommend using [`composables`](/docs/guide/directory-structure/composables) instead of providing helpers to avoid polluting the global namespace and keep your main bundle entry small.
::

::callout{color="amber" icon="i-ph-warning-duotone"}
**If your plugin provides a `ref` or `computed`, it will not be unwrapped in a component `<template>`.** :br
This is due to how Vue works with refs that aren't top-level to the template. You can read more about it [in the Vue documentation](https://vuejs.org/guide/essentials/reactivity-fundamentals.html#caveat-when-unwrapping-in-templates).
::

## Typing Plugins

If you return your helpers from the plugin, they will be typed automatically; you'll find them typed for the return of `useNuxtApp()` and within your templates.
Expand Down
12 changes: 12 additions & 0 deletions docs/2.guide/2.directory-structure/1.server.md
Expand Up @@ -158,6 +158,10 @@ export default defineEventHandler((event) => {
})
```

::callout{icon="i-ph-lightbulb" color="green"}
Alternatively, use `getValidatedRouterParams` with a schema validator such as Zod for runtime and type safety.
::

You can now universally call this API on `/api/hello/nuxt` and get `Hello, nuxt!`.

### Matching HTTP Method
Expand Down Expand Up @@ -230,6 +234,10 @@ export default defineEventHandler(async (event) => {
})
```

::callout{icon="i-ph-lightbulb" color="green" to="https://unjs.io/blog/2023-08-15-h3-towards-the-edge-of-the-web#runtime-type-safe-request-utils"}
Alternatively, use `readValidatedBody` with a schema validator such as Zod for runtime and type safety.
::

You can now universally call this API using:

```vue [app.vue]
Expand Down Expand Up @@ -259,6 +267,10 @@ export default defineEventHandler((event) => {
})
```

::callout{icon="i-ph-lightbulb" color="green" to="https://unjs.io/blog/2023-08-15-h3-towards-the-edge-of-the-web#runtime-type-safe-request-utils"}
Alternatively, use `getValidatedQuery` with a schema validator such as Zod for runtime and type safety.
::

### Error Handling

If no errors are thrown, a status code of `200 OK` will be returned.
Expand Down
4 changes: 4 additions & 0 deletions docs/3.api/2.composables/use-route.md
Expand Up @@ -44,4 +44,8 @@ Apart from dynamic parameters and query parameters, `useRoute()` also provides t
- `path`: encoded pathname section of the URL
- `redirectedFrom`: route location that was attempted to access before ending up on the current route location

::callout
Browsers don't send [URL fragments](https://url.spec.whatwg.org/#concept-url-fragment) (for example `#foo`) when making requests. So using `route.fullPath` in your template can trigger hydration issues because this will include the fragment on client but not the server.
::

:read-more{icon="i-simple-icons-vuedotjs" to="https://router.vuejs.org/api/interfaces/RouteLocationNormalizedLoaded.html"}
6 changes: 6 additions & 0 deletions docs/3.api/3.utils/call-once.md
@@ -1,13 +1,19 @@
---
title: "callOnce"
description: "Run a given function or block of code once during SSR or CSR."
navigation:
badge: New
links:
- label: Source
icon: i-simple-icons-github
to: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/once.ts
size: xs
---

::callout{icon="i-ph-info-duotone" color="blue"}
This utility is available since [Nuxt v3.9](/blog/v3-9).
::

## Purpose

The `callOnce` function is designed to execute a given function or block of code only once during:
Expand Down
4 changes: 2 additions & 2 deletions docs/3.api/5.kit/11.nitro.md
Expand Up @@ -382,7 +382,7 @@ A directory or an array of directories to register to be scanned by Nitro
### Examples

```ts
import { defineNuxtModule, addServerImportsDir } from '@nuxt/kit'
import { defineNuxtModule, createResolver, addServerImportsDir } from '@nuxt/kit'
export default defineNuxtModule({
meta: {
Expand Down Expand Up @@ -420,7 +420,7 @@ A directory or an array of directories to register to be scanned for by Nitro as
### Examples

```ts
import { defineNuxtModule, addServerScanDir } from '@nuxt/kit'
import { defineNuxtModule, createResolver, addServerScanDir } from '@nuxt/kit'
export default defineNuxtModule({
meta: {
name: 'my-module',
Expand Down
2 changes: 1 addition & 1 deletion docs/5.community/6.roadmap.md
Expand Up @@ -60,7 +60,7 @@ Going forward from v3, we commit to support each major version of Nuxt for a min

The current active version of [Nuxt](https://nuxt.com) is **v3** which is available as `nuxt` on npm with the `latest` tag.

Nuxt 2 is in maintenance mode and is available on npm with the `2x` tag. It will reach End of Life (EOL) on December 31st, 2023 at the same time as Vue 2 does.
Nuxt 2 is in maintenance mode and is available on npm with the `2x` tag. It will reach End of Life (EOL) on June 30, 2024.

Each active version has its own nightly releases which are generated automatically. For more about enabling the Nuxt 3 nightly release channel, see [the nightly release channel docs](/docs/guide/going-further/nightly-release-channel).

Expand Down
20 changes: 10 additions & 10 deletions package.json
Expand Up @@ -35,10 +35,10 @@
"@nuxt/schema": "workspace:*",
"@nuxt/vite-builder": "workspace:*",
"@nuxt/webpack-builder": "workspace:*",
"rollup": "^4.9.1",
"rollup": "^4.9.3",
"nuxt": "workspace:*",
"vite": "5.0.10",
"vue": "3.3.13",
"vite": "5.0.11",
"vue": "3.4.5",
"magic-string": "^0.30.5"
},
"devDependencies": {
Expand All @@ -47,23 +47,23 @@
"@nuxt/test-utils": "3.9.0",
"@nuxt/webpack-builder": "workspace:*",
"@types/fs-extra": "11.0.4",
"@types/node": "20.10.5",
"@types/node": "20.10.6",
"@types/semver": "7.5.6",
"@vitest/coverage-v8": "1.1.0",
"@vitest/coverage-v8": "1.1.1",
"@vue/test-utils": "2.4.3",
"case-police": "0.6.1",
"changelogen": "0.5.5",
"consola": "3.2.3",
"devalue": "4.3.2",
"eslint": "8.56.0",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-jsdoc": "46.9.1",
"eslint-plugin-jsdoc": "48.0.2",
"eslint-plugin-no-only-tests": "3.1.0",
"eslint-plugin-unicorn": "50.0.1",
"execa": "8.0.1",
"fs-extra": "11.2.0",
"globby": "14.0.0",
"h3": "1.9.0",
"h3": "1.10.0",
"happy-dom": "12.10.3",
"jiti": "1.21.0",
"markdownlint-cli": "0.38.0",
Expand All @@ -78,14 +78,14 @@
"std-env": "3.7.0",
"typescript": "5.3.3",
"ufo": "1.3.2",
"vitest": "1.1.0",
"vitest": "1.1.1",
"vitest-environment-nuxt": "1.0.0",
"vue": "3.3.13",
"vue": "3.4.5",
"vue-eslint-parser": "9.3.2",
"vue-router": "4.2.5",
"vue-tsc": "1.8.27"
},
"packageManager": "pnpm@8.13.1",
"packageManager": "pnpm@8.14.0",
"engines": {
"node": "^14.18.0 || >=16.10.0"
},
Expand Down
12 changes: 6 additions & 6 deletions packages/kit/package.json
@@ -1,6 +1,6 @@
{
"name": "@nuxt/kit",
"version": "3.9.0",
"version": "3.9.1",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/nuxt.git",
Expand All @@ -27,9 +27,9 @@
},
"dependencies": {
"@nuxt/schema": "workspace:*",
"c12": "^1.5.1",
"c12": "^1.6.1",
"consola": "^3.2.3",
"defu": "^6.1.3",
"defu": "^6.1.4",
"globby": "^14.0.0",
"hash-sum": "^2.0.0",
"ignore": "^5.3.0",
Expand All @@ -42,7 +42,7 @@
"semver": "^7.5.4",
"ufo": "^1.3.2",
"unctx": "^2.3.1",
"unimport": "^3.7.0",
"unimport": "^3.7.1",
"untyped": "^1.4.0"
},
"devDependencies": {
Expand All @@ -52,8 +52,8 @@
"lodash-es": "4.17.21",
"nitropack": "2.8.1",
"unbuild": "latest",
"vite": "5.0.10",
"vitest": "1.1.0",
"vite": "5.0.11",
"vitest": "1.1.1",
"webpack": "5.89.0"
},
"engines": {
Expand Down
6 changes: 4 additions & 2 deletions packages/kit/src/compatibility.ts
Expand Up @@ -73,14 +73,16 @@ export async function hasNuxtCompatibility (constraints: NuxtCompatibility, nuxt
* Check if current nuxt instance is version 2 legacy
*/
export function isNuxt2 (nuxt: Nuxt = useNuxt()) {
return getNuxtVersion(nuxt).startsWith('2.')
const version = getNuxtVersion(nuxt)
return version[0] === '2' && version[1] === '.'
}

/**
* Check if current nuxt instance is version 3
*/
export function isNuxt3 (nuxt: Nuxt = useNuxt()) {
return getNuxtVersion(nuxt).startsWith('3.')
const version = getNuxtVersion(nuxt)
return version[0] === '3' && version[1] === '.'
}

/**
Expand Down
5 changes: 3 additions & 2 deletions packages/kit/src/ignore.ts
Expand Up @@ -22,7 +22,7 @@ export function isIgnored (pathname: string): boolean {
const cwds = nuxt.options._layers?.map(layer => layer.cwd).sort((a, b) => b.length - a.length)
const layer = cwds?.find(cwd => pathname.startsWith(cwd))
const relativePath = relative(layer ?? nuxt.options.rootDir, pathname)
if (relativePath.startsWith('..')) {
if (relativePath[0] === '.' && relativePath[1] === '.') {
return false
}
return !!(relativePath && nuxt._ignore.ignores(relativePath))
Expand All @@ -47,7 +47,8 @@ export function resolveIgnorePatterns (relativePath?: string): string[] {
}

if (relativePath) {
return nuxt._ignorePatterns.map(p => p.startsWith('*') || p.startsWith('!*') ? p : relative(relativePath, resolve(nuxt.options.rootDir, p)))
// Map ignore patterns based on if they start with * or !*
return nuxt._ignorePatterns.map(p => p[0] === '*' || (p[0] === '!' && p[1] === '*') ? p : relative(relativePath, resolve(nuxt.options.rootDir, p)))
}

return nuxt._ignorePatterns
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/module/install.ts
Expand Up @@ -90,7 +90,7 @@ export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, n
continue
}
}
if (!nuxtModule && error) {
if (typeof nuxtModule !== 'function' && error) {
logger.error(`Error while requiring module \`${nuxtModule}\`: ${error}`)
throw error
}
Expand Down

0 comments on commit 2f1d8b6

Please sign in to comment.