├── .npmrc
├── .nuxtrc
├── playground
├── tsconfig.json
├── redirects.csv
├── server
│ └── tsconfig.json
├── pages
│ ├── some-other.vue
│ ├── [generic].vue
│ └── index.vue
├── app.vue
├── nuxt.config.ts
└── package.json
├── tsconfig.json
├── .eslintrc
├── .eslintignore
├── test
├── fixtures
│ └── basic
│ │ ├── package.json
│ │ ├── pages
│ │ ├── some-other.vue
│ │ ├── p-[slug].vue
│ │ └── p-page-with-query.vue
│ │ ├── app.vue
│ │ ├── redirects.csv
│ │ └── nuxt.config.ts
└── basic.test.ts
├── .editorconfig
├── .gitignore
├── LICENSE
├── package.json
├── README.md
├── src
├── module.ts
└── runtime
│ └── redirectsMiddleware.global
└── CHANGELOG.md
/.npmrc:
--------------------------------------------------------------------------------
1 | shamefully-hoist=true
2 | strict-peer-dependencies=false
3 |
--------------------------------------------------------------------------------
/.nuxtrc:
--------------------------------------------------------------------------------
1 | imports.autoImport=false
2 | typescript.includeWorkspace=true
3 |
--------------------------------------------------------------------------------
/playground/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.nuxt/tsconfig.json"
3 | }
4 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./playground/.nuxt/tsconfig.json"
3 | }
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": ["@nuxt/eslint-config"]
4 | }
5 |
--------------------------------------------------------------------------------
/playground/redirects.csv:
--------------------------------------------------------------------------------
1 | code,from,to
2 | 301,/pages?q=some,/page-stoca
3 |
4 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | src/templates/redirectsMiddleware.global
4 |
--------------------------------------------------------------------------------
/playground/server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../.nuxt/tsconfig.server.json"
3 | }
4 |
--------------------------------------------------------------------------------
/test/fixtures/basic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "basic",
4 | "type": "module"
5 | }
6 |
--------------------------------------------------------------------------------
/playground/pages/some-other.vue:
--------------------------------------------------------------------------------
1 |
2 | Redirect worked
3 |
4 |
5 |
--------------------------------------------------------------------------------
/test/fixtures/basic/pages/some-other.vue:
--------------------------------------------------------------------------------
1 |
2 | some other
3 |
4 |
5 |
--------------------------------------------------------------------------------
/playground/pages/[generic].vue:
--------------------------------------------------------------------------------
1 |
2 | Redirected to {{ $route.params.generic }}
3 |
4 |
5 |
--------------------------------------------------------------------------------
/test/fixtures/basic/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/test/fixtures/basic/pages/p-[slug].vue:
--------------------------------------------------------------------------------
1 |
2 | Welcome to p-{{ $route.params.slug }}
3 |
4 |
5 |
--------------------------------------------------------------------------------
/test/fixtures/basic/pages/p-page-with-query.vue:
--------------------------------------------------------------------------------
1 |
2 | Welcome to p-page-with-query
3 |
4 |
5 |
--------------------------------------------------------------------------------
/test/fixtures/basic/redirects.csv:
--------------------------------------------------------------------------------
1 | code,from,to
2 | 301,/some,/some-other
3 | 301,/t-page-with-query?q="some",/p-page-with-query
4 | 301,^.*t-(.*)$,/p-$1
5 |
--------------------------------------------------------------------------------
/test/fixtures/basic/nuxt.config.ts:
--------------------------------------------------------------------------------
1 | import MyModule from '../../../src/module'
2 |
3 | export default defineNuxtConfig({
4 | modules: [
5 | MyModule
6 | ]
7 | })
8 |
--------------------------------------------------------------------------------
/playground/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/playground/nuxt.config.ts:
--------------------------------------------------------------------------------
1 | export default defineNuxtConfig({
2 | modules: ["../src/module"],
3 | redirects: {
4 | // alwaysRedirect: false,
5 | // trailingSlash: false,
6 | },
7 | devtools: { enabled: true },
8 | });
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_size = 2
5 | indent_style = space
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/playground/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "my-module-playground",
4 | "type": "module",
5 | "scripts": {
6 | "dev": "nuxi dev",
7 | "build": "nuxi build",
8 | "generate": "nuxi generate"
9 | },
10 | "devDependencies": {
11 | "nuxt": "latest"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/playground/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Link to some, should go to some-other
4 |
5 | Should go to regex
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | node_modules
3 |
4 | # Logs
5 | *.log*
6 |
7 | # Temp directories
8 | .temp
9 | .tmp
10 | .cache
11 |
12 | # Yarn
13 | **/.yarn/cache
14 | **/.yarn/*state*
15 |
16 | # Generated dirs
17 | dist
18 |
19 | # Nuxt
20 | .nuxt
21 | .output
22 | .vercel_build_output
23 | .build-*
24 | .env
25 | .netlify
26 |
27 | # Env
28 | .env
29 |
30 | # Testing
31 | reports
32 | coverage
33 | *.lcov
34 | .nyc_output
35 |
36 | # VSCode
37 | .vscode/*
38 | !.vscode/settings.json
39 | !.vscode/tasks.json
40 | !.vscode/launch.json
41 | !.vscode/extensions.json
42 | !.vscode/*.code-snippets
43 |
44 | # Intellij idea
45 | *.iml
46 | .idea
47 |
48 | # OSX
49 | .DS_Store
50 | .AppleDouble
51 | .LSOverride
52 | .AppleDB
53 | .AppleDesktop
54 | Network Trash Folder
55 | Temporary Items
56 | .apdisk
57 |
--------------------------------------------------------------------------------
/test/basic.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from "vitest";
2 | import { fileURLToPath } from "node:url";
3 | import { setup, $fetch } from "@nuxt/test-utils";
4 |
5 | describe("ssr", async () => {
6 | await setup({
7 | rootDir: fileURLToPath(new URL("./fixtures/basic", import.meta.url)),
8 | });
9 |
10 | it("redirects to /some-other page when visiting /some", async () => {
11 | // Get response to a server-rendered page with `$fetch`.
12 | const html = await $fetch("/some");
13 | expect(html).toContain("
some other
");
14 | });
15 |
16 | it("redirects to /p-page page when visiting /t-page", async () => {
17 | // Get response to a server-rendered page with `$fetch`.
18 | const html = await $fetch("/t-page");
19 | expect(html).toContain("p-page");
20 | });
21 |
22 | it("redirects to /p-page-with-query when visiting /t-page-with-query?q=some", async () => {
23 | const html = await $fetch("/t-page-with-query?q=some")
24 | expect(html).toContain("p-page-with-query")
25 | })
26 | });
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 atoms - a retex brand
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@atoms-studio/nuxt-redirects",
3 | "version": "1.2.2",
4 | "description": "Massive redirect rules module handler",
5 | "repository": "@atoms-studio/nuxt-redirects",
6 | "private": false,
7 | "license": "MIT",
8 | "type": "module",
9 | "exports": {
10 | ".": {
11 | "types": "./dist/types.d.ts",
12 | "import": "./dist/module.mjs",
13 | "require": "./dist/module.cjs"
14 | }
15 | },
16 | "main": "./dist/module.cjs",
17 | "types": "./dist/types.d.ts",
18 | "files": [
19 | "dist"
20 | ],
21 | "scripts": {
22 | "prepack": "nuxt-module-build",
23 | "dev": "nuxi dev playground",
24 | "dev:analyze": "nuxi analyze playground",
25 | "dev:build": "nuxi build playground",
26 | "dev:prepare": "nuxt-module-build --stub && nuxi prepare playground",
27 | "release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
28 | "lint": "eslint .",
29 | "test": "vitest run",
30 | "test:watch": "vitest watch"
31 | },
32 | "dependencies": {
33 | "@nuxt/kit": "^3.6.5",
34 | "consola": "^3.2.3",
35 | "zod": "^3.21.4",
36 | "zod-csv": "^0.0.3"
37 | },
38 | "devDependencies": {
39 | "@nuxt/devtools": "latest",
40 | "@nuxt/eslint-config": "^0.1.1",
41 | "@nuxt/module-builder": "^0.4.0",
42 | "@nuxt/schema": "^3.6.5",
43 | "@nuxt/test-utils": "^3.6.5",
44 | "@types/node": "^18.17.0",
45 | "changelogen": "^0.5.4",
46 | "eslint": "^8.45.0",
47 | "nuxt": "^3.6.5",
48 | "vitest": "^0.33.0"
49 | }
50 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @atoms-studio/nuxt-redirects
2 |
3 | [![npm version][npm-version-src]][npm-version-href]
4 | [![npm downloads][npm-downloads-src]][npm-downloads-href]
5 | [![License][license-src]][license-href]
6 | [![Nuxt][nuxt-src]][nuxt-href]
7 |
8 | A Nuxt module that allows for redirect management using a simple CSV file, it leverages [zod-csv](https://github.com/bartoszgolebiowski/zod-csv)
9 | under the hood for validation, handling both punctual and regex redirects. An example CSV file can be found [here](/playground/redirects.csv).
10 |
11 | ## Quick Setup
12 |
13 | 1. Add the `@atoms-studio/nuxt-redirects` dependency to your project.
14 |
15 | ```bash
16 | # Using pnpm
17 | pnpm add -D @atoms-studio/nuxt-redirects
18 |
19 | # Using yarn
20 | yarn add --dev @atoms-studio/nuxt-redirects
21 |
22 | # Using npm
23 | npm install --save-dev @atoms-studio/nuxt-redirects
24 | ```
25 |
26 | 2. Add `@atoms-studio/nuxt-redirects` to the `modules` section of your `nuxt.config.ts` file.
27 |
28 | ```js
29 | export default defineNuxtConfig({
30 | modules: ["@atoms-studio/nuxt-redirects"],
31 | });
32 | ```
33 |
34 | That's it! You can now start using it in your Nuxt app. ✨
35 |
36 | ## Development
37 |
38 | ```bash
39 | # Install dependencies
40 | npm install
41 |
42 | # Generate type stubs
43 | npm run dev:prepare
44 |
45 | # Develop with the playground
46 | npm run dev
47 |
48 | # Build the playground
49 | npm run dev:build
50 |
51 | # Run ESLint
52 | npm run lint
53 |
54 | # Run Vitest
55 | npm run test
56 | npm run test:watch
57 |
58 | # Release new version
59 | npm run release
60 | ```
61 |
62 | ## License
63 |
64 | Licensed under the [MIT License](LICENSE).
65 |
66 | [npm-version-src]: https://img.shields.io/npm/v/@atoms-studio/nuxt-redirects/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
67 | [npm-version-href]: https://npmjs.com/package/@atoms-studio/nuxt-redirects
68 | [npm-downloads-src]: https://img.shields.io/npm/dm/@atoms-studio/nuxt-redirects.svg?style=flat&colorA=18181B&colorB=28CF8D
69 | [npm-downloads-href]: https://npmjs.com/package/@atoms-studio/nuxt-redirects
70 | [license-src]: https://img.shields.io/npm/l/@atoms-studio/nuxt-redirects.svg?style=flat&colorA=18181B&colorB=28CF8D
71 | [license-href]: https://npmjs.com/package/@atoms-studio/nuxt-redirects
72 | [nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
73 | [nuxt-href]: https://nuxt.com
74 |
--------------------------------------------------------------------------------
/src/module.ts:
--------------------------------------------------------------------------------
1 | import {
2 | defineNuxtModule,
3 | createResolver,
4 | addTemplate,
5 | addRouteMiddleware,
6 | } from "@nuxt/kit";
7 | import { consola } from "consola";
8 | import { zcsv, parseCSVContent } from "zod-csv";
9 | import { z } from "zod";
10 | import { readFile } from "fs/promises";
11 |
12 | // Module options TypeScript interface definition
13 | export interface ModuleOptions {
14 | csv: string;
15 | trailingSlash: boolean;
16 | alwaysRedirect: boolean;
17 | }
18 |
19 | export default defineNuxtModule({
20 | meta: {
21 | name: "nuxt-redirects",
22 | configKey: "redirects",
23 | },
24 | // Default configuration options of the Nuxt module
25 | defaults: {
26 | csv: "redirects.csv",
27 | trailingSlash: false,
28 | alwaysRedirect: false,
29 | },
30 | async setup(options, nuxt) {
31 | const resolver = createResolver(import.meta.url);
32 |
33 | const redirectsPath = await resolver.resolvePath(options.csv, {
34 | cwd: nuxt.options.srcDir,
35 | });
36 |
37 | // schema
38 | const redirectsSchema = z.object({
39 | code: zcsv.number(),
40 | from: zcsv.string(),
41 | to: zcsv.string(),
42 | });
43 | // reading csv
44 | const csv = await readFile(redirectsPath, { encoding: "utf8" }).catch(
45 | () => {
46 | throw new Error("Error reading redirects csv file");
47 | },
48 | );
49 | const parsedCsv = parseCSVContent(csv, redirectsSchema);
50 |
51 | // get valid rows and write them as a template inside nuxt dir
52 | // you can access it later importing redirects from '#build/nuxt-redirects/redirects'
53 | addTemplate({
54 | filename: "nuxt-redirects/redirects.ts",
55 | write: true,
56 | getContents: () => {
57 | return `
58 | export const redirects = ${JSON.stringify(parsedCsv.validRows)} as const
59 | `;
60 | },
61 | });
62 |
63 | const { dst } = addTemplate({
64 | filename: "nuxt-redirects/redirectsMiddleware.ts",
65 | write: true,
66 | options,
67 | src: await resolver.resolvePath("./runtime/redirectsMiddleware.global"),
68 | });
69 |
70 | addRouteMiddleware({
71 | name: "redirectsMiddleware",
72 | path: dst!,
73 | global: true,
74 | });
75 |
76 | // @ts-ignore
77 | consola.info(`Added ${parsedCsv.validRows.length} redirection rules`);
78 | },
79 | });
80 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 |
4 | ## v1.2.2
5 |
6 | [compare changes](https://undefined/undefined/compare/v1.2.1...v1.2.2)
7 |
8 | ## v1.2.1
9 |
10 | [compare changes](https://undefined/undefined/compare/v1.2.0...v1.2.1)
11 |
12 | ### 🩹 Fixes
13 |
14 | - Regex redirects corrected (d11df2e)
15 |
16 | ### ❤️ Contributors
17 |
18 | - Matteo Rigoni
19 |
20 | ## v1.2.0
21 |
22 | [compare changes](https://undefined/undefined/compare/v1.1.3...v1.2.0)
23 |
24 | ### 🚀 Enhancements
25 |
26 | - Bring query alongside redirection (91f2b65)
27 |
28 | ### ❤️ Contributors
29 |
30 | - Matteo Rigoni
31 |
32 | ## v1.1.3
33 |
34 | [compare changes](https://undefined/undefined/compare/v1.1.2...v1.1.3)
35 |
36 | ### 🩹 Fixes
37 |
38 | - **redirection:** Fixed excluding query from url (36e12bf)
39 |
40 | ### ❤️ Contributors
41 |
42 | - Matteo Rigoni
43 |
44 | ## v1.1.2
45 |
46 | [compare changes](https://undefined/undefined/compare/v1.1.1...v1.1.2)
47 |
48 | ### 🩹 Fixes
49 |
50 | - Do not include redirects in client bundle if its server only redirection (fe18f60)
51 |
52 | ### ❤️ Contributors
53 |
54 | - Matteo Rigoni
55 |
56 | ## v1.1.1
57 |
58 | [compare changes](https://undefined/undefined/compare/v1.1.0...v1.1.1)
59 |
60 | ### 🏡 Chore
61 |
62 | - Added license (8c25042)
63 | - Updated readme (6ecd4af)
64 | - Update README.md (cedabcd)
65 | - Fix typo in README.md (462f7e6)
66 |
67 | ### ❤️ Contributors
68 |
69 | - Matteo Rigoni ([@Rigo-m](http://github.com/Rigo-m))
70 | - Gigantino
71 |
72 | ## v1.1.0
73 |
74 | [compare changes](https://undefined/undefined/compare/v1.0.3...v1.1.0)
75 |
76 | ### 🚀 Enhancements
77 |
78 | - **test:** Added basic testing with a punctual and a regex redirection (e9f6a95)
79 |
80 | ### ❤️ Contributors
81 |
82 | - Matteo Rigoni
83 |
84 | ## v1.0.3
85 |
86 | [compare changes](https://undefined/undefined/compare/v1.0.2...v1.0.3)
87 |
88 | ### 🩹 Fixes
89 |
90 | - **example:** Fixed example regex, it should have a forward slash at the beginning (f77fe3c)
91 | - Redirect mixin (0cb8a2f)
92 |
93 | ### ❤️ Contributors
94 |
95 | - Matteo Rigoni
96 |
97 | ## v1.0.2
98 |
99 | [compare changes](https://undefined/undefined/compare/v1.0.1...v1.0.2)
100 |
101 | ### 🩹 Fixes
102 |
103 | - **build:** Build fix and release (903c777)
104 | - **redirection:** Accounted for trailing slash while matching regex (b1cda4a)
105 |
106 | ### ❤️ Contributors
107 |
108 | - Matteo Rigoni
109 |
110 | ## v1.0.1
111 |
112 |
113 | ### 🩹 Fixes
114 |
115 | - **package.json:** Added correct name (b01a479)
116 |
117 | ### 🏡 Chore
118 |
119 | - First commit (aec09d3)
120 |
121 | ### ❤️ Contributors
122 |
123 | - Matteo Rigoni
124 |
125 |
--------------------------------------------------------------------------------
/src/runtime/redirectsMiddleware.global:
--------------------------------------------------------------------------------
1 | import { defineNuxtRouteMiddleware, navigateTo } from "nuxt/app";
2 |
3 | export default defineNuxtRouteMiddleware(async (to, _) => {
4 | if (to.fullPath.includes("robots.txt")) return
5 | const getTypedRedirects = (redirects: any) =>
6 | redirects as Array<{ code: number; from: string; to: string }>;
7 | const isRegex = (s: string) => s.startsWith("^") && s.endsWith("$");
8 |
9 | // if redirection is only server side, skip redirection entirely, else load redirection rules
10 | let redirects: typeof import("./redirects")["redirects"] | null = null
11 | if (!<% print(options.alwaysRedirect) %> && !process.server) {
12 | return
13 | } else {
14 | const { redirects: r } = await import("./redirects")
15 | redirects = r
16 | }
17 |
18 | // divide redirection rules in punctual redirects and regex redirection rules
19 | const typedRedirects = getTypedRedirects(redirects);
20 |
21 |
22 | const punctualRedirects = typedRedirects.filter(
23 | (redirectionRule) => !isRegex(redirectionRule.from)
24 | );
25 |
26 | const regexRedirects = typedRedirects.filter((redirectionRule) =>
27 | isRegex(redirectionRule.from)
28 | );
29 |
30 | const hasTrailingSlash = (path: string) => path.endsWith("/")
31 | const removeTrailingSlash = (path: string) => hasTrailingSlash(path) ? path.slice(0, -1) : path
32 | const addTrailingSlash = (path: string) => {
33 | if (<% print(options.trailingSlash) %>) {
34 | if (!hasTrailingSlash(path)) {
35 | return path + "/"
36 | }
37 | }
38 |
39 | return path
40 | }
41 | const getQueriesFromPath = (path: string) => {
42 | return path.split("?")?.[1]?.split("&") ?? []
43 | }
44 |
45 | const joinQueriesAndPath = (path: string, queries: string[]) => {
46 | if (!queries.length) return path
47 | return path + "?" + queries.join('&')
48 | }
49 |
50 | const getUrl = (path: string) => {
51 | return path.split("?")[0]
52 | }
53 |
54 | const getQuery = (path: string) => {
55 | return path.split("?")?.[1] ?? ''
56 | }
57 |
58 | // redirection only happens server side
59 | if (process.server || <% print(options.alwaysRedirect) %>) {
60 | // removing = and trailingSlash, ignore query
61 | let path = removeTrailingSlash(to.fullPath.endsWith("=")
62 | ? to.fullPath.slice(0, -1)
63 | : to.fullPath);
64 |
65 | const query = getQuery(path)
66 | const urlPath = getUrl(path)
67 |
68 | if (to.fullPath !== "/") {
69 | // handling punctual redirects
70 | const punctualRedirection = punctualRedirects.find(
71 | (r) => {
72 | const from = r.from
73 | const fromPath = getUrl(from)
74 | const fromQuery = getQuery(from)
75 | if (fromQuery !== '') {
76 | // check validity of pathQuery
77 | const qArr = query.split("&")
78 | const fromQArr = fromQuery.split("&")
79 |
80 | const queryValidated = fromQArr.every(q => qArr.includes(q))
81 |
82 | return queryValidated && removeTrailingSlash(fromPath) === urlPath
83 | }
84 | return removeTrailingSlash(fromPath) === urlPath
85 | }
86 | );
87 |
88 | if (typeof punctualRedirection !== "undefined") {
89 | // join request and redirection queries
90 | const queryParams = [
91 | ...getQueriesFromPath(to.fullPath),
92 | ...getQueriesFromPath(punctualRedirection.to)
93 | ]
94 | const uri = joinQueriesAndPath(addTrailingSlash(punctualRedirection.to), queryParams)
95 | return navigateTo(uri, {
96 | redirectCode: punctualRedirection.code,
97 | external: true,
98 | });
99 | }
100 | // punctual redirects had the priority, now we look for any regex redirections
101 | let regexRedirection: { code: number, path: string } | undefined
102 | regexRedirects.forEach((r) => {
103 | const regex = new RegExp(r.from);
104 | if (regex.test(path)) {
105 | const toPath = addTrailingSlash(urlPath.replace(regex, r.to));
106 | regexRedirection = { code: r.code, path: toPath }
107 | }
108 | });
109 |
110 | if (typeof regexRedirection !== 'undefined') {
111 | // join request and redirection queries
112 | const queryParams = [
113 | ...getQueriesFromPath(to.fullPath),
114 | ...getQueriesFromPath(regexRedirection.path)
115 | ]
116 | const uri = joinQueriesAndPath(addTrailingSlash(regexRedirection.path), queryParams)
117 |
118 | return navigateTo(uri, {
119 | redirectCode: regexRedirection.code,
120 | external: true,
121 | });
122 | }
123 |
124 |
125 | // trailing-slash
126 | if (<% print(options.trailingSlash) %>) {
127 | const originalSplittedPath = to.fullPath.split("?")
128 | const originalPath = originalSplittedPath[0]
129 |
130 | if (!hasTrailingSlash(originalPath)) {
131 | originalSplittedPath[0] = addTrailingSlash(originalPath)
132 | return navigateTo(originalSplittedPath.join("?"), {
133 | redirectCode: 301,
134 | external: true
135 | })
136 | }
137 | }
138 | }
139 | }
140 | });
141 |
142 |
--------------------------------------------------------------------------------