├── .gitattributes ├── .github └── workflows │ └── nodejs.yml ├── .gitignore ├── CHANGELOG.md ├── DEPCOST.md ├── LICENSE ├── README.md ├── lib ├── client.js ├── index.js └── shared.js ├── package.json └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [8.x, 10.x, 12.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - run: npm install 21 | - run: npm run build --if-present 22 | - run: npm run lint --if-present 23 | - run: npm run cov 24 | env: 25 | CI: true 26 | - uses: codecov/codecov-action@v1 27 | 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.2.1](https://github.com/vuepressjs/vuepress-plugin-html-redirect/compare/v0.2.0...v0.2.1) (2023-03-08) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * only trigger history redirect at client side ([159aa6f](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/159aa6fffc297241ada9817ad52605e1236e43ae)) 7 | * TypeError: Cannot read properties of undefined (reading 'startsWith') ([aada81e](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/aada81eca9be976df5b209f65a3b3611cb98892d)) 8 | 9 | 10 | 11 | # [0.2.0](https://github.com/vuepressjs/vuepress-plugin-html-redirect/compare/75312b53da99868aedc3e109929c2d30709fd316...v0.2.0) (2023-03-08) 12 | 13 | 14 | ### Bug Fixes 15 | 16 | * docs ([69a467d](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/69a467db440b0fefb55b7b68430a958196f9e58b)) 17 | * only ensure leading and ending slash for internal links ([#3](https://github.com/vuepressjs/vuepress-plugin-html-redirect/issues/3)) ([90c2ffb](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/90c2ffb80f81269febce789c3dabd54dac1b5c90)) 18 | * script error when countdown is 0. ([ec8020e](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/ec8020e3d6fc9132902c83998587ed2e7d6c77c1)) 19 | * should not generate tip when countdown is 0. ([4235e61](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/4235e6114d0ccee87a5e3a4c07f2823ab34ff22b)) 20 | * typo (close: [#1](https://github.com/vuepressjs/vuepress-plugin-html-redirect/issues/1)) ([a0819f8](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/a0819f815f4c69664000382a32f196bc27529c8e)) 21 | 22 | 23 | ### Features 24 | 25 | * `enableHistoryRedirect` option ([#14](https://github.com/vuepressjs/vuepress-plugin-html-redirect/issues/14)) ([a0b12b6](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/a0b12b6094b6b5f15f1224d74ec94192a9e0e7c0)) 26 | * init ([75312b5](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/75312b53da99868aedc3e109929c2d30709fd316)) 27 | 28 | 29 | 30 | ## [0.1.4](https://github.com/vuepressjs/vuepress-plugin-html-redirect/compare/v0.1.3...v0.1.4) (2021-06-04) 31 | 32 | 33 | ### Bug Fixes 34 | 35 | * typo (close: [#1](https://github.com/vuepressjs/vuepress-plugin-html-redirect/issues/1)) ([a0819f8](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/a0819f815f4c69664000382a32f196bc27529c8e)) 36 | 37 | 38 | 39 | ## [0.1.3](https://github.com/vuepressjs/vuepress-plugin-html-redirect/compare/75312b53da99868aedc3e109929c2d30709fd316...v0.1.3) (2021-06-03) 40 | 41 | 42 | ### Bug Fixes 43 | 44 | * only ensure leading and ending slash for internal links ([#3](https://github.com/vuepressjs/vuepress-plugin-html-redirect/issues/3)) ([90c2ffb](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/90c2ffb80f81269febce789c3dabd54dac1b5c90)) 45 | * script error when countdown is 0. ([ec8020e](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/ec8020e3d6fc9132902c83998587ed2e7d6c77c1)) 46 | * should not generate tip when countdown is 0. ([4235e61](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/4235e6114d0ccee87a5e3a4c07f2823ab34ff22b)) 47 | 48 | 49 | ### Features 50 | 51 | * init ([75312b5](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/75312b53da99868aedc3e109929c2d30709fd316)) 52 | 53 | 54 | 55 | ## [0.1.2](https://github.com/vuepressjs/vuepress-plugin-html-redirect/compare/v0.1.1...v0.1.2) (2020-03-28) 56 | 57 | 58 | ### Bug Fixes 59 | 60 | * script error when countdown is 0. ([ec8020e](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/ec8020e3d6fc9132902c83998587ed2e7d6c77c1)) 61 | 62 | 63 | 64 | ## [0.1.1](https://github.com/vuepressjs/vuepress-plugin-html-redirect/compare/v0.1.0...v0.1.1) (2020-03-28) 65 | 66 | 67 | ### Bug Fixes 68 | 69 | * should not generate tip when countdown is 0. ([4235e61](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/4235e6114d0ccee87a5e3a4c07f2823ab34ff22b)) 70 | 71 | 72 | 73 | # [0.1.0](https://github.com/vuepressjs/vuepress-plugin-html-redirect/compare/75312b53da99868aedc3e109929c2d30709fd316...v0.1.0) (2020-03-28) 74 | 75 | 76 | ### Features 77 | 78 | * init ([75312b5](https://github.com/vuepressjs/vuepress-plugin-html-redirect/commit/75312b53da99868aedc3e109929c2d30709fd316)) 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /DEPCOST.md: -------------------------------------------------------------------------------- 1 | ## 0.2.1 2 | 3 | | name | install size | reuqire time | 4 | | --- | --- | --- | 5 | | @vuepress/plugin-html-redirect@0.2.1 | 36K | 0.699ms | 6 | 7 | 8 | ## 0.2.0 9 | 10 | | name | install size | reuqire time | 11 | | --- | --- | --- | 12 | | @vuepress/plugin-html-redirect@0.2.0 | 36K | 0.7ms | 13 | 14 | 15 | ## 0.1.4 16 | 17 | | name | install size | reuqire time | 18 | | --- | --- | --- | 19 | | @vuepress/plugin-html-redirect@0.1.4 | 32K | 1.680ms | 20 | 21 | 22 | ## 0.1.3 23 | 24 | | name | install size | reuqire time | 25 | | --- | --- | --- | 26 | | @vuepress/plugin-html-redirect@0.1.3 | 32K | 2.181ms | 27 | 28 | 29 | ## 0.1.2 30 | 31 | | name | install size | reuqire time | 32 | | --- | --- | --- | 33 | | @vuepress/plugin-html-redirect@0.1.2 | 32K | 2.148ms | 34 | 35 | 36 | ## 0.1.1 37 | 38 | | name | install size | reuqire time | 39 | | --- | --- | --- | 40 | | @vuepress/plugin-html-redirect@0.1.1 | 28K | 4.506ms | 41 | 42 | 43 | ## 0.1.0 44 | 45 | | name | install size | reuqire time | 46 | | --- | --- | --- | 47 | | @vuepress/plugin-html-redirect@0.1.0 | 32K | 4.950ms | 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 真山 (http://gitlab.alipay-inc.com/u/haoli.chl) 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @vuepress/plugin-html-redirect 2 | 3 | [![NPM version](https://img.shields.io/npm/v/@vuepress/plugin-html-redirect.svg?style=flat)](https://npmjs.com/package/@vuepress/plugin-html-redirect) [![NPM downloads](https://img.shields.io/npm/dm/@vuepress/plugin-html-redirect.svg?style=flat)](https://npmjs.com/package/@vuepress/plugin-html-redirect) ![Node.js CI](https://github.com/vuepressjs/vuepress-plugin-html-redirect/workflows/Node.js%20CI/badge.svg) 4 | 5 | ## Feature 6 | 7 | - Support virtual URLs as source URLs. 8 | - Support `countdown`. 9 | - Work with static `base` and [dynamic base](https://github.com/vuepressjs/vuepress-plugin-dynamic-base). 10 | 11 | ## Motivation 12 | 13 | In the site development of vuepress, a small directory structure adjustment will invalidate some URLs, but these URLs may have been published. With this plugin, you can keep those disappeared URLs forever. 14 | 15 | ## Install 16 | 17 | ```bash 18 | yarn add -D @vuepress/plugin-html-redirect 19 | # OR npm install -D @vuepress/plugin-html-redirect 20 | ``` 21 | 22 | ## Usage 23 | 24 | - Write redirects: 25 | 26 | The agreed file to write `redirects` config is `/path/to/.vuepress/redirects`, whose format is as follows: 27 | 28 | ``` 29 | [url] [redirect_url] 30 | [url] [redirect_url] 31 | [url] [redirect_url] 32 | ... 33 | ``` 34 | 35 | example: 36 | 37 | ``` 38 | /2020/03/27/webpack-5-module-federation/ /translations/2020/03/27/webpack-5-module-federation/ 39 | ``` 40 | 41 | - Simple usage: 42 | 43 | ```js 44 | // .vuepress/config.js 45 | module.exports = { 46 | plugins: [ 47 | '@vuepress/html-redirect', // OR full name: '@vuepress/plugin-html-redirect' 48 | ], 49 | } 50 | ``` 51 | 52 | - Disable `countdown`: 53 | 54 | 55 | ```js 56 | // .vuepress/config.js 57 | module.exports = { 58 | plugins: [ 59 | ['@vuepress/html-redirect', { 60 | countdown: 0, 61 | }], 62 | ], 63 | } 64 | ``` 65 | 66 | It means that the publc path will be different acccording to the NEV you set, and the router base will be `'/'` when the host is `hostA`, and `'/blog/'` when the host is `hostB`. 67 | 68 | ## Options 69 | 70 | ### countdown 71 | 72 | - Type: `string` 73 | - Description: Control how many seconds the page will be redirected, defaults to `3`. 74 | 75 | ### enableHistoryRedirect 76 | 77 | - Type: `boolean` 78 | - Description: Enable redirects when navigation is based on History API, defaults to `false`. 79 | 80 | Single Page Applications (SPA) typically only utilise one index file that is accessible by web browsers: usually index.html. Navigation in the application is then commonly handled using JavaScript with the help of the [HTML5 History API](http://www.w3.org/html/wg/drafts/html/master/single-page.html#the-history-interface), in VuePress we leverage [vue-router](https://v2.vuejs.org/v2/guide/routing.html) under the hood. 81 | 82 | ## TODO 83 | 84 | - Support directory redirects. 85 | 86 | PR welcome! 87 | 88 | ## Contributing 89 | 90 | 1. Fork it! 91 | 2. Create your feature branch: `git checkout -b my-new-feature` 92 | 3. Commit your changes: `git commit -am 'Add some feature'` 93 | 4. Push to the branch: `git push origin my-new-feature` 94 | 5. Submit a pull request :D 95 | 96 | ## Author 97 | 98 | **@vuepress/plugin-html-redirect** © [ULIVZ](https://github.com/ulivz), Released under the [MIT](./LICENSE) License.
99 | 100 | > [github.com/ulivz](https://github.com/ulivz) · Twitter [@_ulivz](https://twitter.com/_ulivz) 101 | 102 | 103 | -------------------------------------------------------------------------------- /lib/client.js: -------------------------------------------------------------------------------- 1 | /* global ENABLE_HISTORY_REDIRECT */ 2 | 3 | import { HTML_DIRECTS } from "@dynamic/html-redirects"; 4 | import { findRedirect } from "./shared"; 5 | 6 | export default ({ 7 | Vue, // the version of Vue being used in the VuePress app 8 | options, // the options for the root Vue instance 9 | router, // the router instance for the app 10 | siteData, // site metadata 11 | isServer, // is this enhancement applied in server-rendering or client 12 | }) => { 13 | if (!isServer && ENABLE_HISTORY_REDIRECT) { 14 | router.beforeEach((to, from, next) => { 15 | const redirectUrl = findRedirect(HTML_DIRECTS, to.path); 16 | if(redirectUrl){ 17 | next(redirectUrl) 18 | } else { 19 | next() 20 | } 21 | }) 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const { 2 | ensureLeadingSlash, 3 | ensureEndingSlash, 4 | compose, 5 | logger, 6 | chalk, 7 | path, 8 | fs, 9 | } = require('@vuepress/shared-utils') 10 | const { findRedirect } = require('./shared') 11 | 12 | const isInternalUrl = url => url.startsWith('/'); 13 | 14 | function onlyInternalUrl(fn) { 15 | return function (url) { 16 | if (isInternalUrl(url)) { 17 | return fn(url); 18 | } 19 | return url; 20 | } 21 | } 22 | 23 | module.exports = (options = {}, ctx) => { 24 | const redirectsFile = path.join(ctx.vuepressDir, 'redirects') 25 | if (!fs.existsSync(redirectsFile)) { 26 | return 27 | } 28 | 29 | const { countdown = 3, enableHistoryRedirect = false } = options 30 | 31 | const normalize = compose( 32 | onlyInternalUrl(ensureEndingSlash), 33 | onlyInternalUrl(ensureLeadingSlash), 34 | v => v.trim(), 35 | v => decodeURIComponent(v), 36 | ) 37 | 38 | const redirects = fs.readFileSync(path.join(ctx.vuepressDir, 'redirects'), 'utf-8') 39 | .trim() 40 | .split('\n') 41 | .filter(v => v) 42 | .map(line => line.split(' ').map(normalize)) 43 | 44 | const plugin = {} 45 | 46 | async function renderPage(url, redirectUrl) { 47 | const pagePath = decodeURIComponent(url) 48 | const filename = pagePath.replace(/\/$/, '/index.html').replace(/^\//, '') 49 | const filePath = path.resolve(ctx.outDir, filename) 50 | await fs.ensureDir(path.dirname(filePath)) 51 | const html = getRedirectHtml({ url, redirectUrl, countdown, routerBase: ctx.base }) 52 | await fs.writeFile(filePath, html) 53 | logger.info(`[redirect] Generated redirect page: ${chalk.cyan(filename)}:`) 54 | logger.info(`[redirect] ${chalk.gray(url)} -> ${chalk.gray(redirectUrl)}`) 55 | } 56 | 57 | if (ctx.isProd) { 58 | plugin.generated = async () => { 59 | await Promise.all(redirects.map(r => renderPage(r[0], r[1]))) 60 | } 61 | } else { 62 | plugin.beforeDevServer = app => { 63 | app.use((req, res, next) => { 64 | const redirectUrl = findRedirect(redirects, normalize(req.url)) 65 | if (redirectUrl) { 66 | res.redirect(redirectUrl) 67 | } else { 68 | next() 69 | } 70 | }) 71 | } 72 | } 73 | 74 | plugin.define = { 75 | ENABLE_HISTORY_REDIRECT: enableHistoryRedirect 76 | } 77 | 78 | plugin.clientDynamicModules = () => { 79 | return { 80 | name: 'html-redirects.js', 81 | content: `export const HTML_DIRECTS = ${JSON.stringify(redirects)}` 82 | } 83 | } 84 | 85 | plugin.enhanceAppFiles = path.resolve(__dirname, 'client.js') 86 | 87 | return plugin 88 | } 89 | 90 | /** 91 | * Get redirect html content. 92 | */ 93 | function getRedirectHtml({ /* url, */redirectUrl, countdown, routerBase = '/' } = {}) { 94 | 95 | return ` 96 | 97 | 98 | 99 | 100 | 101 | ${countdown > 0 102 | ? ` 103 |

104 | Redirect after ${countdown}s 105 |

106 |

107 | Powered by vuepress-plugin-redirect 108 |

`.trim() 109 | : '' 110 | } 111 | 140 | 141 | ` 142 | } 143 | -------------------------------------------------------------------------------- /lib/shared.js: -------------------------------------------------------------------------------- 1 | exports.findRedirect = function (redirects, url) { 2 | const conf = redirects.find((r) => r[0] === url); 3 | return conf && conf[1]; 4 | }; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vuepress/plugin-html-redirect", 3 | "version": "0.2.1", 4 | "description": "A VuePress plugin to handle \"redirection\" with HTML. ", 5 | "main": "lib/index.js", 6 | "files": [ 7 | "lib" 8 | ], 9 | "keywords": [ 10 | "redrect", 11 | "redrection", 12 | "vuepress", 13 | "plugin" 14 | ], 15 | "scripts": { 16 | "test": "jest --passWithNoTests", 17 | "cov": "jest --passWithNoTests --coverage", 18 | "lint": "eslint --fix lib", 19 | "prepublishOnly": "conventional-changelog -p angular -r 2 -i CHANGELOG.md -s", 20 | "depcost": "depcost --record --npm-client=npm", 21 | "postpublish": "npm run depcost" 22 | }, 23 | "repository": { 24 | "url": "https://github.com/vuepressjs/vuepress-plugin-html-redirect", 25 | "type": "git" 26 | }, 27 | "author": "ULIVZ ", 28 | "license": "MIT", 29 | "dependencies": {}, 30 | "devDependencies": { 31 | "@vuepress/shared-utils": "^1.0.0", 32 | "conventional-changelog-cli": "^2.0.31", 33 | "jest-cli": "^24.8.0", 34 | "depcost": "^0.2.1" 35 | }, 36 | "publishConfig": { 37 | "access": "public" 38 | } 39 | } 40 | --------------------------------------------------------------------------------