mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
Merge branch 'patch-26' of https://github.com/GalacticHypernova/nuxt into patch-26
This commit is contained in:
commit
c6c4f4efc8
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -75,7 +75,7 @@ jobs:
|
||||
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
|
||||
uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
|
||||
with:
|
||||
config: |
|
||||
paths:
|
||||
@ -91,7 +91,7 @@ jobs:
|
||||
queries: +security-and-quality
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
|
||||
uses: github/codeql-action/analyze@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
|
||||
with:
|
||||
category: "/language:javascript-typescript"
|
||||
|
||||
|
2
.github/workflows/dependency-review.yml
vendored
2
.github/workflows/dependency-review.yml
vendored
@ -19,4 +19,4 @@ jobs:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4
|
||||
uses: actions/dependency-review-action@a6993e2c61fd5dc440b409aa1d6904921c5e1894 # v4.3.5
|
||||
|
2
.github/workflows/docs-check-links.yml
vendored
2
.github/workflows/docs-check-links.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
steps:
|
||||
# Cache lychee results (e.g. to avoid hitting rate limits)
|
||||
- name: Restore lychee cache
|
||||
uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
with:
|
||||
path: .lycheecache
|
||||
key: cache-lychee-${{ github.sha }}
|
||||
|
2
.github/workflows/scorecards.yml
vendored
2
.github/workflows/scorecards.yml
vendored
@ -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@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
|
||||
uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
|
||||
if: github.repository == 'nuxt/nuxt' && success()
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
@ -67,6 +67,7 @@ export default defineNuxtConfig({
|
||||
// app: 'app'
|
||||
// },
|
||||
// experimental: {
|
||||
// scanPageMeta: 'after-resolve',
|
||||
// sharedPrerenderData: false,
|
||||
// compileTemplate: true,
|
||||
// resetAsyncDataToUndefined: true,
|
||||
@ -236,6 +237,45 @@ export default defineNuxtConfig({
|
||||
})
|
||||
```
|
||||
|
||||
#### Scan Page Meta After Resolution
|
||||
|
||||
🚦 **Impact Level**: Minimal
|
||||
|
||||
##### What Changed
|
||||
|
||||
We now scan page metadata (defined in `definePageMeta`) _after_ calling the `pages:extend` hook rather than before.
|
||||
|
||||
##### Reasons for Change
|
||||
|
||||
This was to allow scanning metadata for pages that users wanted to add in `pages:extend`. We still offer an opportunity to change or override page metadata in a new `pages:resolved` hook.
|
||||
|
||||
##### Migration Steps
|
||||
|
||||
If you want to override page metadata, do that in `pages:resolved` rather than in `pages:extend`.
|
||||
|
||||
```diff
|
||||
export default defineNuxtConfig({
|
||||
hooks: {
|
||||
- 'pages:extend'(pages) {
|
||||
+ 'pages:resolved'(pages) {
|
||||
const myPage = pages.find(page => page.path === '/')
|
||||
myPage.meta ||= {}
|
||||
myPage.meta.layout = 'overridden-layout'
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Alternatively, you can revert to the previous behaviour with:
|
||||
|
||||
```ts twoslash [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
experimental: {
|
||||
scanPageMeta: true
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Shared Prerender Data
|
||||
|
||||
🚦 **Impact Level**: Medium
|
||||
|
@ -334,6 +334,8 @@ This option allows exposing some route metadata defined in `definePageMeta` at b
|
||||
|
||||
This only works with static or strings/arrays rather than variables or conditional assignment. See [original issue](https://github.com/nuxt/nuxt/issues/24770) for more information and context.
|
||||
|
||||
It is also possible to scan page metadata only after all routes have been registered in `pages:extend`. Then another hook, `pages:resolved` will be called. To enable this behavior, set `scanPageMeta: 'after-resolve'`.
|
||||
|
||||
You can disable this feature if it causes issues in your project.
|
||||
|
||||
```ts twoslash [nuxt.config.ts]
|
||||
|
@ -61,6 +61,7 @@ export default defineNuxtConfig({
|
||||
app: 'app'
|
||||
},
|
||||
experimental: {
|
||||
scanPageMeta: 'after-resolve',
|
||||
sharedPrerenderData: false,
|
||||
compileTemplate: true,
|
||||
resetAsyncDataToUndefined: true,
|
||||
|
@ -30,6 +30,7 @@ interface PageMeta {
|
||||
redirect?: RouteRecordRedirectOption
|
||||
name?: string
|
||||
path?: string
|
||||
props?: RouteRecordRaw['props']
|
||||
alias?: string | string[]
|
||||
pageTransition?: boolean | TransitionProps
|
||||
layoutTransition?: boolean | TransitionProps
|
||||
@ -63,6 +64,12 @@ interface PageMeta {
|
||||
|
||||
You may define a [custom regular expression](#using-a-custom-regular-expression) if you have a more complex pattern than can be expressed with the file name.
|
||||
|
||||
**`props`**
|
||||
|
||||
- **Type**: [`RouteRecordRaw['props']`](https://router.vuejs.org/guide/essentials/passing-props)
|
||||
|
||||
Allows accessing the route `params` as props passed to the page component.
|
||||
|
||||
**`alias`**
|
||||
|
||||
- **Type**: `string | string[]`
|
||||
|
@ -40,7 +40,7 @@
|
||||
"@nuxt/ui-templates": "workspace:*",
|
||||
"@nuxt/vite-builder": "workspace:*",
|
||||
"@nuxt/webpack-builder": "workspace:*",
|
||||
"@types/node": "20.16.13",
|
||||
"@types/node": "20.16.15",
|
||||
"@vue/compiler-core": "3.5.12",
|
||||
"@vue/compiler-dom": "3.5.12",
|
||||
"@vue/shared": "3.5.12",
|
||||
@ -57,7 +57,7 @@
|
||||
"typescript": "5.6.3",
|
||||
"ufo": "1.5.4",
|
||||
"unbuild": "3.0.0-rc.11",
|
||||
"vite": "5.4.9",
|
||||
"vite": "5.4.10",
|
||||
"vue": "3.5.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -69,7 +69,7 @@
|
||||
"@nuxt/webpack-builder": "workspace:*",
|
||||
"@testing-library/vue": "8.1.0",
|
||||
"@types/eslint__js": "8.42.3",
|
||||
"@types/node": "20.16.13",
|
||||
"@types/node": "20.16.15",
|
||||
"@types/semver": "7.5.8",
|
||||
"@unhead/schema": "1.11.10",
|
||||
"@unhead/vue": "1.11.10",
|
||||
@ -92,7 +92,7 @@
|
||||
"jiti": "2.3.3",
|
||||
"markdownlint-cli": "0.42.0",
|
||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||
"nuxi": "3.14.0",
|
||||
"nuxi": "3.15.0",
|
||||
"nuxt": "workspace:*",
|
||||
"nuxt-content-twoslash": "0.1.1",
|
||||
"ofetch": "1.4.1",
|
||||
|
@ -48,12 +48,12 @@
|
||||
"untyped": "^1.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rspack/core": "1.0.13",
|
||||
"@rspack/core": "1.0.14",
|
||||
"@types/hash-sum": "1.0.2",
|
||||
"@types/semver": "7.5.8",
|
||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||
"unbuild": "3.0.0-rc.11",
|
||||
"vite": "5.4.9",
|
||||
"vite": "5.4.10",
|
||||
"vitest": "2.1.3",
|
||||
"webpack": "5.95.0"
|
||||
},
|
||||
|
@ -95,7 +95,7 @@
|
||||
"mlly": "^1.7.2",
|
||||
"nanotar": "^0.1.1",
|
||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||
"nuxi": "^3.14.0",
|
||||
"nuxi": "^3.15.0",
|
||||
"nypm": "^0.3.12",
|
||||
"ofetch": "^1.4.1",
|
||||
"ohash": "^1.1.4",
|
||||
@ -132,7 +132,7 @@
|
||||
"@vitejs/plugin-vue": "5.1.4",
|
||||
"@vue/compiler-sfc": "3.5.12",
|
||||
"unbuild": "3.0.0-rc.11",
|
||||
"vite": "5.4.9",
|
||||
"vite": "5.4.10",
|
||||
"vitest": "2.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -505,7 +505,7 @@ export default defineNuxtModule({
|
||||
const { routes, imports } = normalizeRoutes(app.pages, new Set(), {
|
||||
serverComponentRuntime,
|
||||
clientComponentRuntime,
|
||||
overrideMeta: nuxt.options.experimental.scanPageMeta,
|
||||
overrideMeta: !!nuxt.options.experimental.scanPageMeta,
|
||||
})
|
||||
return [...imports, `export default ${routes}`].join('\n')
|
||||
},
|
||||
|
@ -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, RouteRecordRaw, RouteRecordRedirectOption } from 'vue-router'
|
||||
import { useRoute } from 'vue-router'
|
||||
import type { NitroRouteConfig } from 'nitro/types'
|
||||
import { useNuxtApp } from '#app/nuxt'
|
||||
@ -37,6 +37,11 @@ export interface PageMeta {
|
||||
name?: string
|
||||
/** You may define a path matcher, if you have a more complex pattern than can be expressed with the file name. */
|
||||
path?: string
|
||||
/**
|
||||
* Allows accessing the route `params` as props passed to the page component.
|
||||
* @see https://router.vuejs.org/guide/essentials/passing-props
|
||||
*/
|
||||
props?: RouteRecordRaw['props']
|
||||
/** Set to `false` to avoid scrolling to top on page navigations */
|
||||
scrollToTop?: boolean | ((to: RouteLocationNormalizedLoaded, from: RouteLocationNormalizedLoaded) => boolean)
|
||||
}
|
||||
|
@ -64,18 +64,25 @@ export async function resolvePagesRoutes (): Promise<NuxtPage[]> {
|
||||
})
|
||||
|
||||
const pages = uniqueBy(allRoutes, 'path')
|
||||
|
||||
const shouldAugment = nuxt.options.experimental.scanPageMeta || nuxt.options.experimental.typedPages
|
||||
|
||||
if (shouldAugment) {
|
||||
if (shouldAugment === false) {
|
||||
await nuxt.callHook('pages:extend', pages)
|
||||
return pages
|
||||
}
|
||||
|
||||
if (shouldAugment === 'after-resolve') {
|
||||
await nuxt.callHook('pages:extend', pages)
|
||||
await augmentPages(pages, nuxt.vfs)
|
||||
} else {
|
||||
const augmentedPages = await augmentPages(pages, nuxt.vfs)
|
||||
await nuxt.callHook('pages:extend', pages)
|
||||
await augmentPages(pages, nuxt.vfs, augmentedPages)
|
||||
augmentedPages.clear()
|
||||
} else {
|
||||
await nuxt.callHook('pages:extend', pages)
|
||||
}
|
||||
|
||||
await nuxt.callHook('pages:resolved', pages)
|
||||
|
||||
return pages
|
||||
}
|
||||
|
||||
@ -184,7 +191,7 @@ export function extractScriptContent (html: string) {
|
||||
}
|
||||
|
||||
const PAGE_META_RE = /definePageMeta\([\s\S]*?\)/
|
||||
const extractionKeys = ['name', 'path', 'alias', 'redirect'] as const
|
||||
const extractionKeys = ['name', 'path', 'props', 'alias', 'redirect'] as const
|
||||
const DYNAMIC_META_KEY = '__nuxt_dynamic_meta_key' as const
|
||||
|
||||
const pageContentsCache: Record<string, string> = {}
|
||||
@ -266,7 +273,7 @@ export async function getRouteMeta (contents: string, absolutePath: string): Pro
|
||||
continue
|
||||
}
|
||||
|
||||
if (property.value.type !== 'Literal' || typeof property.value.value !== 'string') {
|
||||
if (property.value.type !== 'Literal' || (typeof property.value.value !== 'string' && typeof property.value.value !== 'boolean')) {
|
||||
console.debug(`[nuxt] Skipping extraction of \`${key}\` metadata as it is not a string literal or array of string literals (reading \`${absolutePath}\`).`)
|
||||
dynamicProperties.add(key)
|
||||
continue
|
||||
@ -535,6 +542,7 @@ export function normalizeRoutes (routes: NuxtPage[], metaImports: Set<string> =
|
||||
const metaRoute: NormalizedRoute = {
|
||||
name: `${metaImportName}?.name ?? ${route.name}`,
|
||||
path: `${metaImportName}?.path ?? ${route.path}`,
|
||||
props: `${metaImportName}?.props ?? false`,
|
||||
meta: `${metaImportName} || {}`,
|
||||
alias: `${metaImportName}?.alias || []`,
|
||||
redirect: `${metaImportName}?.redirect`,
|
||||
@ -578,7 +586,7 @@ async function createClientPage(loader) {
|
||||
}
|
||||
|
||||
// set to extracted value or delete if none extracted
|
||||
for (const key of ['meta', 'alias', 'redirect'] satisfies NormalizedRouteKeys) {
|
||||
for (const key of ['meta', 'alias', 'redirect', 'props'] satisfies NormalizedRouteKeys) {
|
||||
if (markedDynamic.has(key)) { continue }
|
||||
|
||||
if (route[key] == null) {
|
||||
|
@ -6,6 +6,7 @@
|
||||
"meta": "{ ...(mockMeta || {}), ...{"someMetaData":true} }",
|
||||
"name": "mockMeta?.name ?? "pushed-route"",
|
||||
"path": "mockMeta?.path ?? "/"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -24,6 +25,18 @@
|
||||
"meta": "{ ...(mockMeta || {}), ...{"test":1} }",
|
||||
"name": "mockMeta?.name ?? "page-with-meta"",
|
||||
"path": "mockMeta?.path ?? "/page-with-meta"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
"route.meta props generate by file": [
|
||||
{
|
||||
"alias": "mockMeta?.alias || []",
|
||||
"component": "() => import("pages/page-with-props.vue")",
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "page-with-props"",
|
||||
"path": "mockMeta?.path ?? "/page-with-props"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -34,6 +47,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "test:name"",
|
||||
"path": "mockMeta?.path ?? "/test\\:name"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -50,6 +64,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "param-index"",
|
||||
"path": "mockMeta?.path ?? """,
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -58,6 +73,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "param-index-sibling"",
|
||||
"path": "mockMeta?.path ?? "sibling"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -65,6 +81,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? undefined",
|
||||
"path": "mockMeta?.path ?? """,
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -73,6 +90,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "param-sibling"",
|
||||
"path": "mockMeta?.path ?? "sibling"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -80,6 +98,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? undefined",
|
||||
"path": "mockMeta?.path ?? "/param"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -91,6 +110,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "wrapper-expose-other"",
|
||||
"path": "mockMeta?.path ?? """,
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -99,6 +119,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "wrapper-expose-other-sibling"",
|
||||
"path": "mockMeta?.path ?? "sibling"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -106,6 +127,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? undefined",
|
||||
"path": "mockMeta?.path ?? "/wrapper-expose/other"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -116,6 +138,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "home"",
|
||||
"path": "mockMeta?.path ?? "/"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": ""/"",
|
||||
},
|
||||
],
|
||||
@ -126,6 +149,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "slug"",
|
||||
"path": "mockMeta?.path ?? "/:slug(.*)*"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -134,6 +158,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "index"",
|
||||
"path": "mockMeta?.path ?? "/"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -144,6 +169,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "index"",
|
||||
"path": "mockMeta?.path ?? "/"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -152,6 +178,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "slug"",
|
||||
"path": "mockMeta?.path ?? "/:slug()"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -163,6 +190,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "foo"",
|
||||
"path": "mockMeta?.path ?? """,
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -170,6 +198,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? undefined",
|
||||
"path": "mockMeta?.path ?? "/:foo?"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -178,6 +207,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "optional-opt"",
|
||||
"path": "mockMeta?.path ?? "/optional/:opt?"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -186,6 +216,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "optional-prefix-opt"",
|
||||
"path": "mockMeta?.path ?? "/optional/prefix-:opt?"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -194,6 +225,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "optional-opt-postfix"",
|
||||
"path": "mockMeta?.path ?? "/optional/:opt?-postfix"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -202,6 +234,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "optional-prefix-opt-postfix"",
|
||||
"path": "mockMeta?.path ?? "/optional/prefix-:opt?-postfix"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -210,6 +243,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "bar"",
|
||||
"path": "mockMeta?.path ?? "/:bar()"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -218,6 +252,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "nonopt-slug"",
|
||||
"path": "mockMeta?.path ?? "/nonopt/:slug()"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -226,6 +261,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "opt-slug"",
|
||||
"path": "mockMeta?.path ?? "/opt/:slug?"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -234,6 +270,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "sub-route-slug"",
|
||||
"path": "mockMeta?.path ?? "/:sub?/route-:slug()"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -244,6 +281,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "stories"",
|
||||
"path": "mockMeta?.path ?? "/:stories(.*)*"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -252,6 +290,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "stories-id"",
|
||||
"path": "mockMeta?.path ?? "/stories/:id()"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -262,6 +301,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "stories-id"",
|
||||
"path": "mockMeta?.path ?? "/stories/:id()"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -270,6 +310,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "stories"",
|
||||
"path": "mockMeta?.path ?? "/:stories(.*)*"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -280,6 +321,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "kebab-case"",
|
||||
"path": "mockMeta?.path ?? "/kebab-case"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -290,6 +332,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "snake_case"",
|
||||
"path": "mockMeta?.path ?? "/snake_case"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -300,6 +343,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "index"",
|
||||
"path": "mockMeta?.path ?? "/"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -308,6 +352,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "parent"",
|
||||
"path": "mockMeta?.path ?? "/parent"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -316,6 +361,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "parent-child"",
|
||||
"path": "mockMeta?.path ?? "/parent/child"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -329,6 +375,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "parent-child"",
|
||||
"path": "mockMeta?.path ?? "child"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -336,6 +383,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "parent"",
|
||||
"path": "mockMeta?.path ?? "/parent"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -346,6 +394,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "index"",
|
||||
"path": "mockMeta?.path ?? "/"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -357,6 +406,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "about"",
|
||||
"path": "mockMeta?.path ?? """,
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -364,6 +414,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? undefined",
|
||||
"path": "mockMeta?.path ?? "/about"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -377,6 +428,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "index-index-all"",
|
||||
"path": "mockMeta?.path ?? "all"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -384,6 +436,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "index"",
|
||||
"path": "mockMeta?.path ?? "/"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -394,6 +447,7 @@
|
||||
"meta": "{ ...(mockMeta || {}), ...{"test":1} }",
|
||||
"name": "mockMeta?.name ?? "page-with-meta"",
|
||||
"path": "mockMeta?.path ?? "/page-with-meta"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -404,6 +458,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "parent-child"",
|
||||
"path": "mockMeta?.path ?? "/parent/:child()"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -412,6 +467,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "parent-child"",
|
||||
"path": "mockMeta?.path ?? "/parent-:child()"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -422,6 +478,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "foo"",
|
||||
"path": "mockMeta?.path ?? "/:foo?"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -430,6 +487,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "foo"",
|
||||
"path": "mockMeta?.path ?? "/:foo()"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -440,6 +498,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "a1_1a"",
|
||||
"path": "mockMeta?.path ?? "/:a1_1a()"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -448,6 +507,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "b2.2b"",
|
||||
"path": "mockMeta?.path ?? "/:b2.2b()"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -456,6 +516,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "b2_2b"",
|
||||
"path": "mockMeta?.path ?? "/:b2()_:2b()"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -464,6 +525,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "c33c"",
|
||||
"path": "mockMeta?.path ?? "/:c33c?"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
{
|
||||
@ -472,6 +534,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "d44d"",
|
||||
"path": "mockMeta?.path ?? "/:d44d?"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -482,6 +545,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "home"",
|
||||
"path": "mockMeta?.path ?? "/"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
@ -492,6 +556,7 @@
|
||||
"meta": "mockMeta || {}",
|
||||
"name": "mockMeta?.name ?? "index"",
|
||||
"path": "mockMeta?.path ?? "/"",
|
||||
"props": "mockMeta?.props ?? false",
|
||||
"redirect": "mockMeta?.redirect",
|
||||
},
|
||||
],
|
||||
|
@ -24,6 +24,13 @@
|
||||
"path": ""/page-with-meta"",
|
||||
},
|
||||
],
|
||||
"route.meta props generate by file": [
|
||||
{
|
||||
"component": "() => import("pages/page-with-props.vue")",
|
||||
"name": ""page-with-props"",
|
||||
"path": ""/page-with-props"",
|
||||
},
|
||||
],
|
||||
"should allow pages with `:` in their path": [
|
||||
{
|
||||
"component": "() => import("pages/test:name.vue")",
|
||||
|
@ -211,6 +211,7 @@ describe('normalizeRoutes', () => {
|
||||
{
|
||||
name: indexN6pT4Un8hYMeta?.name ?? undefined,
|
||||
path: indexN6pT4Un8hYMeta?.path ?? "/",
|
||||
props: indexN6pT4Un8hYMeta?.props ?? false,
|
||||
meta: { ...(indexN6pT4Un8hYMeta || {}), ...{"layout":"test","foo":"bar"} },
|
||||
alias: indexN6pT4Un8hYMeta?.alias || [],
|
||||
redirect: indexN6pT4Un8hYMeta?.redirect,
|
||||
|
@ -601,6 +601,30 @@ describe('pages:generateRoutesFromFiles', () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
description: 'route.meta props generate by file',
|
||||
files: [
|
||||
{
|
||||
path: `${pagesDir}/page-with-props.vue`,
|
||||
template: `
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
props: true
|
||||
})
|
||||
</script>
|
||||
`,
|
||||
},
|
||||
],
|
||||
output: [
|
||||
{
|
||||
name: 'page-with-props',
|
||||
path: '/page-with-props',
|
||||
file: `${pagesDir}/page-with-props.vue`,
|
||||
children: [],
|
||||
props: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
description: 'should handle route groups',
|
||||
files: [
|
||||
|
@ -31,7 +31,7 @@
|
||||
"dependencies": {
|
||||
"@nuxt/friendly-errors-webpack-plugin": "^2.6.0",
|
||||
"@nuxt/kit": "workspace:*",
|
||||
"@rspack/core": "^1.0.13",
|
||||
"@rspack/core": "^1.0.14",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"css-loader": "^7.1.2",
|
||||
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||
|
@ -53,7 +53,7 @@
|
||||
"unbuild": "3.0.0-rc.11",
|
||||
"unctx": "2.3.1",
|
||||
"unenv": "1.10.0",
|
||||
"vite": "5.4.9",
|
||||
"vite": "5.4.10",
|
||||
"vue": "3.5.12",
|
||||
"vue-bundle-renderer": "2.1.1",
|
||||
"vue-loader": "17.4.2",
|
||||
|
@ -297,8 +297,13 @@ export default defineUntypedSchema({
|
||||
* This only works with static or strings/arrays rather than variables or conditional assignment.
|
||||
*
|
||||
* @see [Nuxt Issues #24770](https://github.com/nuxt/nuxt/issues/24770)
|
||||
* @type {boolean | 'after-resolve'}
|
||||
*/
|
||||
scanPageMeta: true,
|
||||
scanPageMeta: {
|
||||
async $resolve (val, get) {
|
||||
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion === 4 ? 'after-resolve' : true)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Automatically share payload _data_ between pages that are prerendered. This can result in a significant
|
||||
|
@ -8,7 +8,7 @@ import type { Import, InlinePreset, Unimport } from 'unimport'
|
||||
import type { Compiler, Configuration, Stats } from 'webpack'
|
||||
import type { Nitro, NitroConfig } from 'nitro/types'
|
||||
import type { Schema, SchemaDefinition } from 'untyped'
|
||||
import type { RouteLocationRaw } from 'vue-router'
|
||||
import type { RouteLocationRaw, RouteRecordRaw } from 'vue-router'
|
||||
import type { VueCompilerOptions } from '@vue/language-core'
|
||||
import type { NuxtCompatibility, NuxtCompatibilityIssues, ViteConfig } from '..'
|
||||
import type { Component, ComponentsOptions } from './components'
|
||||
@ -28,6 +28,7 @@ export type VueTSConfig = 0 extends 1 & VueCompilerOptions ? TSConfig : TSConfig
|
||||
export type NuxtPage = {
|
||||
name?: string
|
||||
path: string
|
||||
props?: RouteRecordRaw['props']
|
||||
file?: string
|
||||
meta?: Record<string, any>
|
||||
alias?: string[] | string
|
||||
@ -183,12 +184,19 @@ export interface NuxtHooks {
|
||||
'builder:watch': (event: WatchEvent, path: string) => HookResult
|
||||
|
||||
/**
|
||||
* Called after pages routes are resolved.
|
||||
* @param pages Array containing resolved pages
|
||||
* Called after page routes are scanned from the file system.
|
||||
* @param pages Array containing scanned pages
|
||||
* @returns Promise
|
||||
*/
|
||||
'pages:extend': (pages: NuxtPage[]) => HookResult
|
||||
|
||||
/**
|
||||
* Called after page routes have been augmented with scanned metadata.
|
||||
* @param pages Array containing resolved pages
|
||||
* @returns Promise
|
||||
*/
|
||||
'pages:resolved': (pages: NuxtPage[]) => HookResult
|
||||
|
||||
/**
|
||||
* Called when resolving `app/router.options` files. It allows modifying the detected router options files
|
||||
* and adding new ones.
|
||||
|
@ -18,7 +18,7 @@
|
||||
"test": "pnpm lint && pnpm build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@unocss/reset": "0.63.4",
|
||||
"@unocss/reset": "0.63.6",
|
||||
"critters": "0.0.25",
|
||||
"html-validate": "8.24.2",
|
||||
"htmlnano": "2.1.1",
|
||||
@ -29,7 +29,7 @@
|
||||
"scule": "1.3.0",
|
||||
"tinyexec": "0.3.1",
|
||||
"tinyglobby": "0.2.9",
|
||||
"unocss": "0.63.4",
|
||||
"vite": "5.4.9"
|
||||
"unocss": "0.63.6",
|
||||
"vite": "5.4.10"
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,7 @@
|
||||
"ufo": "^1.5.4",
|
||||
"unenv": "^1.10.0",
|
||||
"unplugin": "^1.14.1",
|
||||
"vite": "^5.4.9",
|
||||
"vite": "^5.4.10",
|
||||
"vite-node": "^2.1.3",
|
||||
"vite-plugin-checker": "^0.8.0",
|
||||
"vue-bundle-renderer": "^2.1.1"
|
||||
|
@ -9,13 +9,12 @@ function sortPlugins ({ plugins, order }: NuxtOptions['postcss']): string[] {
|
||||
}
|
||||
|
||||
export async function resolveCSSOptions (nuxt: Nuxt): Promise<ViteConfig['css']> {
|
||||
const css: ViteConfig['css'] & { postcss: NonNullable<Exclude<NonNullable<ViteConfig['css']>['postcss'], string>> } = {
|
||||
const css: ViteConfig['css'] & { postcss: NonNullable<Exclude<NonNullable<ViteConfig['css']>['postcss'], string>> & { plugins: Plugin[] } } = {
|
||||
postcss: {
|
||||
plugins: [],
|
||||
},
|
||||
}
|
||||
|
||||
css.postcss.plugins = []
|
||||
const postcssOptions = nuxt.options.postcss
|
||||
|
||||
const jiti = createJiti(nuxt.options.rootDir, { alias: nuxt.options.alias })
|
||||
|
@ -76,7 +76,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/schema": "workspace:*",
|
||||
"@rspack/core": "1.0.13",
|
||||
"@rspack/core": "1.0.14",
|
||||
"@types/hash-sum": "1.0.2",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/pify": "5.0.4",
|
||||
|
@ -34,24 +34,26 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
|
||||
}))
|
||||
|
||||
/** Inject rollup plugin for Nitro to handle dynamic imports from webpack chunks */
|
||||
const nitro = useNitro()
|
||||
const dynamicRequirePlugin = dynamicRequire({
|
||||
dir: resolve(nuxt.options.buildDir, 'dist/server'),
|
||||
inline:
|
||||
if (!nuxt.options.dev) {
|
||||
const nitro = useNitro()
|
||||
const dynamicRequirePlugin = dynamicRequire({
|
||||
dir: resolve(nuxt.options.buildDir, 'dist/server'),
|
||||
inline:
|
||||
nitro.options.node === false || nitro.options.inlineDynamicImports,
|
||||
ignore: [
|
||||
'client.manifest.mjs',
|
||||
'server.js',
|
||||
'server.cjs',
|
||||
'server.mjs',
|
||||
'server.manifest.mjs',
|
||||
],
|
||||
})
|
||||
const prerenderRollupPlugins = nitro.options._config.rollupConfig!.plugins as InputPluginOption[]
|
||||
const rollupPlugins = nitro.options.rollupConfig!.plugins as InputPluginOption[]
|
||||
ignore: [
|
||||
'client.manifest.mjs',
|
||||
'server.js',
|
||||
'server.cjs',
|
||||
'server.mjs',
|
||||
'server.manifest.mjs',
|
||||
],
|
||||
})
|
||||
const prerenderRollupPlugins = nitro.options._config.rollupConfig!.plugins as InputPluginOption[]
|
||||
const rollupPlugins = nitro.options.rollupConfig!.plugins as InputPluginOption[]
|
||||
|
||||
prerenderRollupPlugins.push(dynamicRequirePlugin)
|
||||
rollupPlugins.push(dynamicRequirePlugin)
|
||||
prerenderRollupPlugins.push(dynamicRequirePlugin)
|
||||
rollupPlugins.push(dynamicRequirePlugin)
|
||||
}
|
||||
|
||||
await nuxt.callHook(`${builder}:config`, webpackConfigs)
|
||||
|
||||
|
713
pnpm-lock.yaml
713
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -13,13 +13,18 @@ export default defineNuxtModule({
|
||||
name: 'page-extend',
|
||||
path: '/page-extend',
|
||||
file: resolver.resolve('../runtime/page.vue'),
|
||||
}, {
|
||||
})
|
||||
})
|
||||
|
||||
nuxt.hook('pages:resolved', (pages) => {
|
||||
pages.push({
|
||||
path: '/big-page-1',
|
||||
file: resolver.resolve('./pages/big-page.vue'),
|
||||
meta: {
|
||||
layout: false,
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
path: '/big-page-2',
|
||||
file: resolver.resolve('./pages/big-page.vue'),
|
||||
meta: {
|
||||
|
4
test/fixtures/basic/nuxt.config.ts
vendored
4
test/fixtures/basic/nuxt.config.ts
vendored
@ -75,7 +75,7 @@ export default defineNuxtConfig({
|
||||
_layout: page.meta?.layout,
|
||||
},
|
||||
})
|
||||
nuxt.hook('pages:extend', (pages) => {
|
||||
nuxt.hook('pages:resolved', (pages) => {
|
||||
const newPages = []
|
||||
for (const page of pages) {
|
||||
if (routesToDuplicate.includes(page.path)) {
|
||||
@ -88,7 +88,7 @@ export default defineNuxtConfig({
|
||||
},
|
||||
function (_options, nuxt) {
|
||||
// to check that page metadata is preserved
|
||||
nuxt.hook('pages:extend', (pages) => {
|
||||
nuxt.hook('pages:resolved', (pages) => {
|
||||
const customName = pages.find(page => page.name === 'some-custom-name')
|
||||
if (!customName) { throw new Error('Page with custom name not found') }
|
||||
if (customName.path !== '/some-custom-path') { throw new Error('Page path not extracted') }
|
||||
|
Loading…
Reference in New Issue
Block a user