diff --git a/.eslintrc b/.eslintrc index d9c5599b0158..4ce0513f3aa9 100644 --- a/.eslintrc +++ b/.eslintrc @@ -43,6 +43,10 @@ { "pattern": "@nuxt/test-utils", "group": "external" + }, + { + "pattern": "#vue-router", + "group": "external" } ] } diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 54ab4991091b..81dbca5995ee 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -94,6 +94,7 @@ "unenv": "^1.4.1", "unimport": "^3.0.6", "unplugin": "^1.3.1", + "unplugin-vue-router": "^0.6.4", "untyped": "^1.3.2", "vue": "^3.2.47", "vue-bundle-renderer": "^1.0.3", diff --git a/packages/nuxt/src/app/components/nuxt-link.ts b/packages/nuxt/src/app/components/nuxt-link.ts index b2859cf38867..a78ff6a79d2b 100644 --- a/packages/nuxt/src/app/components/nuxt-link.ts +++ b/packages/nuxt/src/app/components/nuxt-link.ts @@ -1,6 +1,6 @@ import type { ComputedRef, DefineComponent, PropType } from 'vue' import { computed, defineComponent, h, onBeforeUnmount, onMounted, ref, resolveComponent } from 'vue' -import type { RouteLocation, RouteLocationRaw } from 'vue-router' +import type { RouteLocation, RouteLocationRaw } from '#vue-router' import { hasProtocol, parseQuery, parseURL, withTrailingSlash, withoutTrailingSlash } from 'ufo' import { preloadRouteComponents } from '../composables/preload' @@ -24,8 +24,8 @@ export type NuxtLinkOptions = { export type NuxtLinkProps = { // Routing - to?: string | RouteLocationRaw - href?: string | RouteLocationRaw + to?: RouteLocationRaw + href?: RouteLocationRaw external?: boolean replace?: boolean custom?: boolean @@ -81,12 +81,12 @@ export function defineNuxtLink (options: NuxtLinkOptions) { props: { // Routing to: { - type: [String, Object] as PropType, + type: [String, Object] as PropType, default: undefined, required: false }, href: { - type: [String, Object] as PropType, + type: [String, Object] as PropType, default: undefined, required: false }, diff --git a/packages/nuxt/src/app/composables/preload.ts b/packages/nuxt/src/app/composables/preload.ts index 3844c9d0c008..6710ae2b8210 100644 --- a/packages/nuxt/src/app/composables/preload.ts +++ b/packages/nuxt/src/app/composables/preload.ts @@ -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 { useRouter } from './router' diff --git a/packages/nuxt/src/app/composables/router.ts b/packages/nuxt/src/app/composables/router.ts index d4a9852aa003..b7d490da8a0e 100644 --- a/packages/nuxt/src/app/composables/router.ts +++ b/packages/nuxt/src/app/composables/router.ts @@ -1,6 +1,6 @@ import { getCurrentInstance, inject, onUnmounted } from 'vue' import type { Ref } from 'vue' -import type { NavigationFailure, NavigationGuard, RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteLocationPathRaw, RouteLocationRaw, Router } from 'vue-router' +import type { NavigationFailure, NavigationGuard, RouteLocationNormalized, RouteLocationPathRaw, RouteLocationRaw, Router, useRoute as _useRoute, useRouter as _useRouter } from '#vue-router' import { sanitizeStatusCode } from 'h3' import { hasProtocol, joinURL, parseURL } from 'ufo' @@ -11,11 +11,11 @@ import { useState } from './state' import type { PageMeta } from '#app' -export const useRouter = () => { +export const useRouter: typeof _useRouter = () => { return useNuxtApp()?.$router as Router } -export const useRoute = (): RouteLocationNormalizedLoaded => { +export const useRoute: typeof _useRoute = () => { if (process.dev && isProcessingMiddleware()) { console.warn('[nuxt] Calling `useRoute` within middleware may lead to misleading results. Instead, use the (to, from) arguments passed to the middleware to access the new and old routes.') } diff --git a/packages/nuxt/src/app/nuxt.ts b/packages/nuxt/src/app/nuxt.ts index 0386b559319e..15f51d81f0d8 100644 --- a/packages/nuxt/src/app/nuxt.ts +++ b/packages/nuxt/src/app/nuxt.ts @@ -1,7 +1,7 @@ /* eslint-disable no-use-before-define */ import { getCurrentInstance, reactive } from 'vue' import type { App, 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' diff --git a/packages/nuxt/src/pages/module.ts b/packages/nuxt/src/pages/module.ts index 11638b1c2113..bfd569ce3e51 100644 --- a/packages/nuxt/src/pages/module.ts +++ b/packages/nuxt/src/pages/module.ts @@ -1,10 +1,14 @@ import { existsSync, readdirSync } from 'node:fs' +import { mkdir, readFile } from 'node:fs/promises' import { addComponent, addPlugin, addTemplate, addVitePlugin, addWebpackPlugin, defineNuxtModule, findPath, updateTemplates } from '@nuxt/kit' -import { join, relative, resolve } from 'pathe' +import { dirname, join, relative, resolve } from 'pathe' import { genImport, genObjectFromRawEntries, genString } from 'knitwork' import escapeRE from 'escape-string-regexp' import { joinURL } from 'ufo' import type { NuxtApp, NuxtPage } from 'nuxt/schema' +import { createRoutesContext } from 'unplugin-vue-router' +import { resolveOptions } from 'unplugin-vue-router/options' +import type { EditableTreeNode, Options as TypedRouterOptions } from 'unplugin-vue-router' import { distDir } from '../dirs' import { normalizeRoutes, resolvePagesRoutes } from './utils' @@ -15,7 +19,9 @@ export default defineNuxtModule({ meta: { name: 'pages' }, - setup (_options, nuxt) { + async setup (_options, nuxt) { + const useExperimentalTypedPages = nuxt.options.experimental.typedPages + const pagesDirs = nuxt.options._layers.map( layer => resolve(layer.config.srcDir, layer.config.dir?.pages || 'pages') ) @@ -67,18 +73,82 @@ export default defineNuxtModule({ return } + if (useExperimentalTypedPages) { + const declarationFile = './types/typed-router.d.ts' + + const options: TypedRouterOptions = { + routesFolder: [], + dts: resolve(nuxt.options.buildDir, declarationFile), + logs: nuxt.options.debug, + async beforeWriteFiles (rootPage) { + rootPage.children.forEach(child => child.delete()) + const pages = await resolvePagesRoutes() + await nuxt.callHook('pages:extend', pages) + function addPage (parent: EditableTreeNode, page: NuxtPage) { + // @ts-expect-error TODO: either fix types upstream or figure out another + // way to add a route without a file, which must be possible + const route = parent.insert(page.path, page.file) + if (page.meta) { + route.addToMeta(page.meta) + } + if (page.alias) { + route.addAlias(page.alias) + } + if (page.name) { + route.name = page.name + } + // TODO: implement redirect support + // if (page.redirect) {} + if (page.children) { + page.children.forEach(child => addPage(route, child)) + } + } + + for (const page of pages) { + addPage(rootPage, page) + } + } + } + + nuxt.hook('prepare:types', ({ references }) => { + // This file will be generated by unplugin-vue-router + references.push({ path: declarationFile }) + }) + + const context = createRoutesContext(resolveOptions(options)) + const dtsFile = resolve(nuxt.options.buildDir, declarationFile) + await mkdir(dirname(dtsFile), { recursive: true }) + await context.scanPages(false) + + if (nuxt.options._prepare) { + // TODO: could we generate this from context instead? + const dts = await readFile(dtsFile, 'utf-8') + addTemplate({ + filename: 'types/typed-router.d.ts', + getContents: () => dts + }) + } + + // Regenerate types/typed-router.d.ts when adding or removing pages + nuxt.hook('builder:generateApp', async (options) => { + if (!options?.filter || options.filter({ filename: 'routes.mjs' } as any)) { + await context.scanPages() + } + }) + } + const runtimeDir = resolve(distDir, 'pages/runtime') // Add $router types nuxt.hook('prepare:types', ({ references }) => { - references.push({ types: 'vue-router' }) + 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' && s.imports.includes('onBeforeRouteLeave')) if (routerImports) { - routerImports.from = 'vue-router' + routerImports.from = '#vue-router' } }) @@ -144,7 +214,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' } ) }) @@ -177,6 +247,7 @@ export default defineNuxtModule({ await nuxt.callHook('pages:extend', pages) const sourceFiles = getSources(pages) + for (const key in manifest) { if (manifest[key].isEntry) { manifest[key].dynamicImports = @@ -185,6 +256,17 @@ export default defineNuxtModule({ } }) + // adds support for #vue-router alias + addTemplate({ + filename: 'vue-router.mjs', + // TODO: use `vue-router/auto` when we have support for page metadata + getContents: () => 'export * from \'vue-router\';' + }) + addTemplate({ + filename: 'vue-router.d.ts', + getContents: () => `export * from '${useExperimentalTypedPages ? 'vue-router/auto' : 'vue-router'}'` + }) + // Add routes template addTemplate({ filename: 'routes.mjs', @@ -278,10 +360,13 @@ export default defineNuxtModule({ filePath: resolve(distDir, 'pages/runtime/page') }) + nuxt.options.alias['#vue-router'] = join(nuxt.options.buildDir, 'vue-router') + // Add declarations for middleware keys nuxt.hook('prepare:types', ({ references }) => { references.push({ path: resolve(nuxt.options.buildDir, 'types/middleware.d.ts') }) references.push({ path: resolve(nuxt.options.buildDir, 'types/layouts.d.ts') }) + references.push({ path: resolve(nuxt.options.buildDir, 'vue-router.d.ts') }) }) } }) diff --git a/packages/nuxt/src/pages/runtime/composables.ts b/packages/nuxt/src/pages/runtime/composables.ts index 5c3db581aee2..00299913380d 100644 --- a/packages/nuxt/src/pages/runtime/composables.ts +++ b/packages/nuxt/src/pages/runtime/composables.ts @@ -1,6 +1,6 @@ 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 { NuxtError } from '#app' diff --git a/packages/nuxt/src/pages/runtime/page.ts b/packages/nuxt/src/pages/runtime/page.ts index c21663df85b6..7e971a36526f 100644 --- a/packages/nuxt/src/pages/runtime/page.ts +++ b/packages/nuxt/src/pages/runtime/page.ts @@ -1,8 +1,8 @@ import { Suspense, Transition, computed, defineComponent, h, nextTick, onMounted, provide, reactive } 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 { RouteLocation, RouteLocationNormalized, RouteLocationNormalizedLoaded } from 'vue-router' +import type { RouteLocation, RouteLocationNormalized, RouteLocationNormalizedLoaded } from '#vue-router' import type { RouterViewSlotProps } from './utils' import { generateRouteKey, wrapInKeepAlive } from './utils' diff --git a/packages/nuxt/src/pages/runtime/plugins/router.ts b/packages/nuxt/src/pages/runtime/plugins/router.ts index 052a7e26146c..2167180e9e22 100644 --- a/packages/nuxt/src/pages/runtime/plugins/router.ts +++ b/packages/nuxt/src/pages/runtime/plugins/router.ts @@ -1,12 +1,12 @@ import { computed, isReadonly, reactive, shallowRef } from 'vue' import type { Ref } from 'vue' -import type { RouteLocation, Router } from 'vue-router' +import type { RouteLocation, Router } from '#vue-router' import { createMemoryHistory, createRouter, createWebHashHistory, createWebHistory -} from 'vue-router' +} from '#vue-router' import { createError } from 'h3' import { withoutBase } from 'ufo' @@ -44,7 +44,7 @@ function createCurrentLocation ( return path + search + hash } -export default defineNuxtPlugin({ +const plugin: Plugin<{ router: Router }> = defineNuxtPlugin({ name: 'nuxt:router', enforce: 'pre', async setup (nuxtApp) { @@ -201,4 +201,6 @@ export default defineNuxtPlugin({ return { provide: { router } } } -}) as Plugin<{ router: Router }> +}) + +export default plugin diff --git a/packages/nuxt/src/pages/runtime/router.options.ts b/packages/nuxt/src/pages/runtime/router.options.ts index 3ad1d625d1c1..1c5df83f3a25 100644 --- a/packages/nuxt/src/pages/runtime/router.options.ts +++ b/packages/nuxt/src/pages/runtime/router.options.ts @@ -1,4 +1,4 @@ -import type { RouteLocationNormalized, RouterScrollBehavior } from 'vue-router' +import type { RouteLocationNormalized, RouterScrollBehavior } from '#vue-router' import { nextTick } from 'vue' import type { RouterConfig } from 'nuxt/schema' import { useNuxtApp } from '#app/nuxt' diff --git a/packages/nuxt/src/pages/runtime/utils.ts b/packages/nuxt/src/pages/runtime/utils.ts index 6083ced61781..1004c8bd9b5a 100644 --- a/packages/nuxt/src/pages/runtime/utils.ts +++ b/packages/nuxt/src/pages/runtime/utils.ts @@ -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 extends new (...args: any[]) => infer R ? R : never type RouterViewSlot = Exclude['$slots']['default'], undefined> diff --git a/packages/schema/src/config/experimental.ts b/packages/schema/src/config/experimental.ts index f5227d9ed9e1..26a70e1deb4c 100644 --- a/packages/schema/src/config/experimental.ts +++ b/packages/schema/src/config/experimental.ts @@ -156,6 +156,9 @@ export default defineUntypedSchema({ /** Resolve `~`, `~~`, `@` and `@@` aliases located within layers with respect to their layer source and root directories. */ localLayerAliases: true, + /** Enable the new experimental typed router using [unplugin-vue-router](https://github.com/posva/unplugin-vue-router). */ + typedPages: false, + /** * Set an alternative watcher that will be used as the watching service for Nuxt. * diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d7f6cc28da7..e483d0797128 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -447,6 +447,9 @@ importers: unplugin: specifier: ^1.3.1 version: 1.3.1 + unplugin-vue-router: + specifier: ^0.6.4 + version: 0.6.4(rollup@3.21.5)(vue-router@4.1.6)(vue@3.2.47) untyped: specifier: ^1.3.2 version: 1.3.2 @@ -1068,6 +1071,11 @@ packages: resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} engines: {node: '>=6.9.0'} + /@babel/helper-string-parser@7.21.5: + resolution: {integrity: sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==} + engines: {node: '>=6.9.0'} + dev: false + /@babel/helper-validator-identifier@7.19.1: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} @@ -1278,6 +1286,15 @@ packages: '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 + /@babel/types@7.21.5: + resolution: {integrity: sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.21.5 + '@babel/helper-validator-identifier': 7.19.1 + to-fast-properties: 2.0.0 + dev: false + /@cloudflare/kv-asset-handler@0.3.0: resolution: {integrity: sha512-9CB/MKf/wdvbfkUdfrj+OkEwZ5b7rws0eogJ4293h+7b6KX5toPwym+VQKmILafNB9YiehqY0DlNrDcDhdWHSQ==} dependencies: @@ -2505,6 +2522,25 @@ packages: '@volar/vue-language-core': 1.6.4 typescript: 5.0.4 + /@vue-macros/common@1.3.1(rollup@3.21.5)(vue@3.2.47): + resolution: {integrity: sha512-Lc5aP/8HNJD1XrnvpeNuWcCf82bZdR3auN/chA1b/1rKZgSnmQkH9f33tKO9qLwXSy+u4hpCi8Rw+oUuF1KCeg==} + engines: {node: '>=14.19.0'} + peerDependencies: + vue: ^2.7.0 || ^3.2.25 + peerDependenciesMeta: + vue: + optional: true + dependencies: + '@babel/types': 7.21.5 + '@rollup/pluginutils': 5.0.2(rollup@3.21.5) + '@vue/compiler-sfc': 3.3.0-beta.5 + local-pkg: 0.4.3 + magic-string-ast: 0.1.2 + vue: 3.2.47 + transitivePeerDependencies: + - rollup + dev: false + /@vue/babel-helper-vue-transform-on@1.0.2: resolution: {integrity: sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==} @@ -2540,6 +2576,15 @@ packages: estree-walker: 2.0.2 source-map-js: 1.0.2 + /@vue/compiler-core@3.3.0-beta.5: + resolution: {integrity: sha512-rwKXIMPDKBzKypcZ7Zc+i4e7ItnhlMEu9QZveek2yLxzMG0QimvZnVKB7eD21cQ9MCwEYW4bb8zjisPMJNNaqQ==} + dependencies: + '@babel/parser': 7.21.4 + '@vue/shared': 3.3.0-beta.5 + estree-walker: 2.0.2 + source-map-js: 1.0.2 + dev: false + /@vue/compiler-dom@3.2.47: resolution: {integrity: sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==} dependencies: @@ -2552,6 +2597,13 @@ packages: '@vue/compiler-core': 3.3.0-beta.3 '@vue/shared': 3.3.0-beta.3 + /@vue/compiler-dom@3.3.0-beta.5: + resolution: {integrity: sha512-OsYuAzl8zHRym5TfDhCLrcTSBt71BFJXnTC9uWO+SfgqadadWZxv1piPebjtwJcODkks5OAGfdhxzKdNzzddXw==} + dependencies: + '@vue/compiler-core': 3.3.0-beta.5 + '@vue/shared': 3.3.0-beta.5 + dev: false + /@vue/compiler-sfc@3.2.47: resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==} dependencies: @@ -2580,6 +2632,21 @@ packages: postcss: 8.4.23 source-map-js: 1.0.2 + /@vue/compiler-sfc@3.3.0-beta.5: + resolution: {integrity: sha512-CbiY2dkzU5IG652ygLUSufLGvXPKI12TQp1PeHs9acjgFjhvSJCSKmAOaCWnXgFsAgpbipPHgAMfWJ8B0h6Sjw==} + dependencies: + '@babel/parser': 7.21.4 + '@vue/compiler-core': 3.3.0-beta.5 + '@vue/compiler-dom': 3.3.0-beta.5 + '@vue/compiler-ssr': 3.3.0-beta.5 + '@vue/reactivity-transform': 3.3.0-beta.5 + '@vue/shared': 3.3.0-beta.5 + estree-walker: 2.0.2 + magic-string: 0.30.0 + postcss: 8.4.23 + source-map-js: 1.0.2 + dev: false + /@vue/compiler-ssr@3.2.47: resolution: {integrity: sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==} dependencies: @@ -2592,6 +2659,13 @@ packages: '@vue/compiler-dom': 3.3.0-beta.3 '@vue/shared': 3.3.0-beta.3 + /@vue/compiler-ssr@3.3.0-beta.5: + resolution: {integrity: sha512-16njciFrQ8ejVdH5tsaPbJwpkpBB2z2VWCxfC69UOylCgPxiW01syE9S/mozRvv5Ken9Sr9bd2MjebG/SEpPNg==} + dependencies: + '@vue/compiler-dom': 3.3.0-beta.5 + '@vue/shared': 3.3.0-beta.5 + dev: false + /@vue/devtools-api@6.5.0: resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==} @@ -2613,6 +2687,16 @@ packages: estree-walker: 2.0.2 magic-string: 0.30.0 + /@vue/reactivity-transform@3.3.0-beta.5: + resolution: {integrity: sha512-v/PCri6+vL8WwwgowHlIopwiqonCj9wV60ZYDdX8AMDxk9Q01h2cWh61JF4XOX7qDs7NdJ7zSVyKSz4B98OXBw==} + dependencies: + '@babel/parser': 7.21.4 + '@vue/compiler-core': 3.3.0-beta.5 + '@vue/shared': 3.3.0-beta.5 + estree-walker: 2.0.2 + magic-string: 0.30.0 + dev: false + /@vue/reactivity@3.2.47: resolution: {integrity: sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==} dependencies: @@ -2651,6 +2735,10 @@ packages: /@vue/shared@3.3.0-beta.3: resolution: {integrity: sha512-st1SnB/Bkbb9TsieeI4TRX9TqHYIR5wvIma3ZtEben55EYSWa1q5u2BhTNgABSdH+rv3Xwfrvpwh5PmCw6Y53g==} + /@vue/shared@3.3.0-beta.5: + resolution: {integrity: sha512-ImwhHfOzuQrfA05Kx4s7J9g7QJt0sZqSlPvPdd6xj5tTEnPNNJYZOHaIP973mtuEuv4Zfh9v+CLiER6E6gtSqg==} + dev: false + /@webassemblyjs/ast@1.11.5: resolution: {integrity: sha512-LHY/GSAZZRpsNQH+/oHqhRQ5FT7eoULcBqgfyTB5nQHogFnK3/7QoN7dLnwSE/JkUAF0SrRuclT7ODqMFtWxxQ==} dependencies: @@ -2997,6 +3085,14 @@ packages: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: true + /ast-walker-scope@0.4.1: + resolution: {integrity: sha512-Ro3nmapMxi/remlJdzFH0tiA7A59KDbxVoLlKWaLDrPELiftb9b8w+CCyWRM+sXZH5KHRAgv8feedW6mihvCHA==} + engines: {node: '>=14.19.0'} + dependencies: + '@babel/parser': 7.21.4 + '@babel/types': 7.21.5 + dev: false + /async-sema@3.1.1: resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} @@ -6077,6 +6173,13 @@ packages: resolution: {integrity: sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==} engines: {node: 14 || >=16.14} + /magic-string-ast@0.1.2: + resolution: {integrity: sha512-P53AZrzq7hclCU6HWj88xNZHmP15DKjMmK/vBytO1qnpYP3ul4IEZlyCE0aU3JRnmgWmZPmoTKj4Bls7v0pMyA==} + engines: {node: '>=14.19.0'} + dependencies: + magic-string: 0.30.0 + dev: false + /magic-string@0.30.0: resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==} engines: {node: '>=12'} @@ -8352,6 +8455,33 @@ packages: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} + /unplugin-vue-router@0.6.4(rollup@3.21.5)(vue-router@4.1.6)(vue@3.2.47): + resolution: {integrity: sha512-9THVhhtbVFxbsIibjK59oPwMI1UCxRWRPX7azSkTUABsxovlOXJys5SJx0kd/0oKIqNJuYgkRfAgPuO77SqCOg==} + peerDependencies: + vue-router: ^4.1.0 + peerDependenciesMeta: + vue-router: + optional: true + dependencies: + '@babel/types': 7.21.5 + '@rollup/pluginutils': 5.0.2(rollup@3.21.5) + '@vue-macros/common': 1.3.1(rollup@3.21.5)(vue@3.2.47) + ast-walker-scope: 0.4.1 + chokidar: 3.5.3 + fast-glob: 3.2.12 + json5: 2.2.3 + local-pkg: 0.4.3 + mlly: 1.2.0 + pathe: 1.1.0 + scule: 1.0.0 + unplugin: 1.3.1 + vue-router: 4.1.6(vue@3.2.47) + yaml: 2.2.2 + transitivePeerDependencies: + - rollup + - vue + dev: false + /unplugin@1.3.1: resolution: {integrity: sha512-h4uUTIvFBQRxUKS2Wjys6ivoeofGhxzTe2sRWlooyjHXVttcVfV/JiavNd3d4+jty0SVV0dxGw9AkY9MwiaCEw==} dependencies: @@ -9044,6 +9174,11 @@ packages: engines: {node: '>= 14'} dev: true + /yaml@2.2.2: + resolution: {integrity: sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==} + engines: {node: '>= 14'} + dev: false + /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} diff --git a/test/fixtures/basic/nuxt.config.ts b/test/fixtures/basic/nuxt.config.ts index c9325385d8d5..12921560fc3c 100644 --- a/test/fixtures/basic/nuxt.config.ts +++ b/test/fixtures/basic/nuxt.config.ts @@ -198,6 +198,7 @@ export default defineNuxtConfig({ } }, experimental: { + typedPages: true, polyfillVueUseHead: true, renderJsonPayloads: process.env.TEST_PAYLOAD !== 'js', respectNoSSRHeader: true, diff --git a/test/fixtures/basic/pages/[...slug].vue b/test/fixtures/basic/pages/[...slug].vue index ee23ccf1ef78..031a5097b339 100644 --- a/test/fixtures/basic/pages/[...slug].vue +++ b/test/fixtures/basic/pages/[...slug].vue @@ -1,7 +1,7 @@ @@ -11,7 +11,8 @@ definePageMeta({ middleware: ['override'], validate: to => to.path !== '/forbidden' }) -if (useRoute().path.includes('navigate-some-path')) { +const route = useRoute('slug') +if (route.path.includes('navigate-some-path')) { throw createError('navigate-some-path setup running') } diff --git a/test/fixtures/basic/pages/nested/[foo]/[bar].vue b/test/fixtures/basic/pages/nested/[foo]/[bar].vue index 2f0a50e69f9b..168f7737dd30 100644 --- a/test/fixtures/basic/pages/nested/[foo]/[bar].vue +++ b/test/fixtures/basic/pages/nested/[foo]/[bar].vue @@ -1,7 +1,11 @@ + + diff --git a/test/fixtures/basic/pages/nested/[foo]/index.vue b/test/fixtures/basic/pages/nested/[foo]/index.vue index b2ea680a54b9..beab2f220e17 100644 --- a/test/fixtures/basic/pages/nested/[foo]/index.vue +++ b/test/fixtures/basic/pages/nested/[foo]/index.vue @@ -1,6 +1,10 @@ + + diff --git a/test/fixtures/basic/pages/nested/[foo]/user-[group].vue b/test/fixtures/basic/pages/nested/[foo]/user-[group].vue index 027545a80f4d..4a4da9fe55a2 100644 --- a/test/fixtures/basic/pages/nested/[foo]/user-[group].vue +++ b/test/fixtures/basic/pages/nested/[foo]/user-[group].vue @@ -1,7 +1,11 @@ + + diff --git a/test/fixtures/basic/pages/nuxt-link/trailing-slash.vue b/test/fixtures/basic/pages/nuxt-link/trailing-slash.vue index a0a22fc8d185..7f3eee7ebeed 100644 --- a/test/fixtures/basic/pages/nuxt-link/trailing-slash.vue +++ b/test/fixtures/basic/pages/nuxt-link/trailing-slash.vue @@ -14,7 +14,7 @@ const links = [ { name: 'nuxt-link-trailing-slash' }, { query: { 'with-state': 'true' }, state: { foo: 'bar' } }, { query: { 'without-state': 'true' } } -] +] as const const route = useRoute() const windowState = computed(() => { diff --git a/test/fixtures/basic/pages/random/[id].vue b/test/fixtures/basic/pages/random/[id].vue index 2a1f24ada723..75eb362f2290 100644 --- a/test/fixtures/basic/pages/random/[id].vue +++ b/test/fixtures/basic/pages/random/[id].vue @@ -35,7 +35,7 @@