├── index.js
├── .npmignore
├── .travis.yml
├── .prettierrc
├── getMetaRedirect.js
├── tests
├── __snapshots__
│ ├── gatsby-node.spec.js.snap
│ └── getMetaRedirect.spec.js.snap
├── getMetaRedirect.spec.js
└── gatsby-node.spec.js
├── .gitignore
├── package.json
├── LICENSE
├── gatsby-node.js
└── README.md
/index.js:
--------------------------------------------------------------------------------
1 | // noop
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | yarn.lock
3 | tests
4 | *.tgz
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | language: node_js
4 | node_js:
5 | - 8
6 | - 9
7 | - 10
8 | - 11
9 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": true,
3 | "singleQuote": true,
4 | "trailingComma": "none",
5 | "printWidth": 80
6 | }
7 |
--------------------------------------------------------------------------------
/getMetaRedirect.js:
--------------------------------------------------------------------------------
1 | module.exports = function getMetaRedirect(toPath) {
2 | let url = toPath.trim();
3 |
4 | const hasProtocol = url.includes('://');
5 | if (!hasProtocol) {
6 | const hasLeadingSlash = url.startsWith('/');
7 | if (!hasLeadingSlash) {
8 | url = `/${url}`;
9 | }
10 |
11 | const resemblesFile = url.includes('.');
12 | if (!resemblesFile) {
13 | url = `${url}/`.replace(/\/\/+/g, '/');
14 | }
15 | }
16 |
17 | return ``;
18 | };
19 |
--------------------------------------------------------------------------------
/tests/__snapshots__/gatsby-node.spec.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`onPostBuild handles external redirects 1`] = `""`;
4 |
5 | exports[`onPostBuild writes deep path redirects 1`] = `""`;
6 |
7 | exports[`onPostBuild writes redirects from root 1`] = `""`;
8 |
9 | exports[`onPostBuild writes redirects to root 1`] = `""`;
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # Dependency directories
21 | node_modules/
22 |
23 | # Optional npm cache directory
24 | .npm
25 |
26 | # Optional eslint cache
27 | .eslintcache
28 |
29 | # Optional REPL history
30 | .node_repl_history
31 |
32 | # Output of 'npm pack'
33 | *.tgz
34 |
35 | # Yarn Integrity file
36 | .yarn-integrity
37 |
38 |
39 | yarn.lock
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatsby-plugin-meta-redirect",
3 | "version": "1.1.1",
4 | "repository": "git@github.com:getchalk/gatsby-plugin-meta-redirect.git",
5 | "author": "Danny Wilson ",
6 | "license": "MIT",
7 | "main": "index.js",
8 | "engines": {
9 | "node": ">=8"
10 | },
11 | "keywords": [
12 | "gatsby",
13 | "gatsby-plugin"
14 | ],
15 | "scripts": {
16 | "clean": "git clean -xdf ./*.js",
17 | "test": "jest"
18 | },
19 | "dependencies": {
20 | "fs-extra": "^7.0.0"
21 | },
22 | "devDependencies": {
23 | "jest": "^22.1.4"
24 | },
25 | "jest": {
26 | "verbose": true,
27 | "testURL": "http://localhost/"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Get Chalk
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 |
--------------------------------------------------------------------------------
/gatsby-node.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { exists, writeFile, ensureDir } = require('fs-extra');
3 |
4 | const getMetaRedirect = require('./getMetaRedirect');
5 |
6 | async function writeRedirectsFile(redirects, folder, pathPrefix) {
7 | if (!redirects.length) return;
8 |
9 | for (const redirect of redirects) {
10 | const { fromPath, toPath } = redirect;
11 |
12 | const FILE_PATH = path.join(
13 | folder,
14 | fromPath.replace(pathPrefix, ''),
15 | 'index.html'
16 | );
17 |
18 | const fileExists = await exists(FILE_PATH);
19 | if (!fileExists) {
20 | try {
21 | await ensureDir(path.dirname(FILE_PATH));
22 | } catch (err) {
23 | // ignore if the directory already exists;
24 | }
25 |
26 | const data = getMetaRedirect(toPath);
27 | await writeFile(FILE_PATH, data);
28 | }
29 | }
30 | }
31 |
32 | exports.onPostBuild = ({ store }) => {
33 | const { redirects, program, config } = store.getState();
34 |
35 | let pathPrefix = '';
36 | if (program.prefixPaths) {
37 | pathPrefix = config.pathPrefix;
38 | }
39 |
40 | const folder = path.join(program.directory, 'public');
41 | return writeRedirectsFile(redirects, folder, pathPrefix);
42 | };
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [![Travis][build-badge]][build]
2 | [![npm package][npm-badge]][npm]
3 |
4 | # gatsby-plugin-meta-redirect
5 |
6 | Generates meta redirect html files for redirecting on any static file host.
7 |
8 | ## Install
9 |
10 | ```sh
11 | npm install --save gatsby-plugin-meta-redirect
12 | ```
13 |
14 | or
15 |
16 | ```sh
17 | yarn add gatsby-plugin-meta-redirect
18 | ```
19 |
20 | ## How to use
21 |
22 | ```js
23 | // In your gatsby-config.js
24 | plugins: [
25 | `gatsby-plugin-meta-redirect` // make sure to put last in the array
26 | ];
27 | ```
28 |
29 | ### Redirects
30 |
31 | You can create redirects using the [`createRedirect`](https://www.gatsbyjs.org/docs/bound-action-creators/#createRedirect) action.
32 |
33 | An example:
34 |
35 | ```js
36 | createRedirect({ fromPath: '/old-url', toPath: '/new-url', isPermanent: true });
37 | createRedirect({ fromPath: '/url', toPath: '/zn-CH/url', Language: 'zn' });
38 | ```
39 |
40 | That will generate the following html files:
41 |
42 | ### `/old-url/index.html`:
43 |
44 | ```html
45 |
46 | ```
47 |
48 | and
49 |
50 | ### `/url/index.html`:
51 |
52 | ```html
53 |
54 | ```
55 |
56 | [build-badge]: https://img.shields.io/travis/getchalk/gatsby-plugin-meta-redirect/master.png?style=flat-square
57 | [build]: https://travis-ci.org/getchalk/gatsby-plugin-meta-redirect
58 | [npm-badge]: https://img.shields.io/npm/v/gatsby-plugin-meta-redirect.png?style=flat-square
59 | [npm]: https://www.npmjs.org/package/gatsby-plugin-meta-redirect
60 |
--------------------------------------------------------------------------------
/tests/getMetaRedirect.spec.js:
--------------------------------------------------------------------------------
1 | const getMetaRedirect = require('../getMetaRedirect');
2 |
3 | describe('getMetaRedirect', () => {
4 | it('wraps path in forward slashes', () => {
5 | expect(getMetaRedirect('toPath')).toMatchSnapshot();
6 | });
7 |
8 | it('allows existing leading and trailing forward slashes', () => {
9 | expect(getMetaRedirect('/toPath/')).toMatchSnapshot();
10 | });
11 |
12 | it('trims leading and trailing whitespace', () => {
13 | expect(getMetaRedirect(' toPath ')).toMatchSnapshot();
14 | });
15 |
16 | it('handles deep paths', () => {
17 | expect(getMetaRedirect('a/b/c/d')).toMatchSnapshot();
18 | });
19 |
20 | it('handles offset wrapping forward slashes', () => {
21 | expect(getMetaRedirect('a/b/c/')).toMatchSnapshot();
22 | });
23 |
24 | it('replaces duplicate slashes with single slash', () => {
25 | expect(getMetaRedirect('topath//a')).toMatchSnapshot();
26 | });
27 |
28 | it('leaves full urls untouched', () => {
29 | expect(getMetaRedirect('http://example.com')).toMatchSnapshot();
30 | expect(getMetaRedirect('http://example.com/')).toMatchSnapshot();
31 | expect(getMetaRedirect('http://example.com/a/b/c')).toMatchSnapshot();
32 | });
33 |
34 | it('handles redirecting to root', () => {
35 | expect(getMetaRedirect('/')).toMatchSnapshot();
36 | });
37 |
38 | it('handles redirecting to a file', () => {
39 | expect(getMetaRedirect('/test.txt')).toMatchSnapshot();
40 | });
41 |
42 | it('handles redirecting to a file in a folder', () => {
43 | expect(getMetaRedirect('a/b/test.txt')).toMatchSnapshot();
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/tests/__snapshots__/getMetaRedirect.spec.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`getMetaRedirect allows existing leading and trailing forward slashes 1`] = `""`;
4 |
5 | exports[`getMetaRedirect handles deep paths 1`] = `""`;
6 |
7 | exports[`getMetaRedirect handles offset wrapping forward slashes 1`] = `""`;
8 |
9 | exports[`getMetaRedirect handles redirecting to a file 1`] = `""`;
10 |
11 | exports[`getMetaRedirect handles redirecting to a file in a folder 1`] = `""`;
12 |
13 | exports[`getMetaRedirect handles redirecting to root 1`] = `""`;
14 |
15 | exports[`getMetaRedirect leaves full urls untouched 1`] = `""`;
16 |
17 | exports[`getMetaRedirect leaves full urls untouched 2`] = `""`;
18 |
19 | exports[`getMetaRedirect leaves full urls untouched 3`] = `""`;
20 |
21 | exports[`getMetaRedirect replaces duplicate slashes with single slash 1`] = `""`;
22 |
23 | exports[`getMetaRedirect trims leading and trailing whitespace 1`] = `""`;
24 |
25 | exports[`getMetaRedirect wraps path in forward slashes 1`] = `""`;
26 |
--------------------------------------------------------------------------------
/tests/gatsby-node.spec.js:
--------------------------------------------------------------------------------
1 | const { exists, remove, readFile } = require('fs-extra');
2 | const { onPostBuild } = require('../gatsby-node');
3 |
4 | describe('onPostBuild', () => {
5 | const tempFolderPath = './public';
6 |
7 | const assertRedirectFile = async (redirects, expectedPath) => {
8 | await onPostBuild({
9 | store: {
10 | getState: () => ({
11 | redirects,
12 | program: {
13 | directory: './'
14 | }
15 | })
16 | }
17 | });
18 |
19 | expect(await exists(expectedPath)).toBe(true);
20 | expect(await readFile(expectedPath, 'utf-8')).toMatchSnapshot();
21 | };
22 |
23 | beforeEach(async () => {
24 | await remove(tempFolderPath);
25 | });
26 |
27 | // cleanup
28 | afterAll(async () => {
29 | await remove(tempFolderPath);
30 | });
31 |
32 | it('writes redirects from root', async () => {
33 | await assertRedirectFile(
34 | [
35 | {
36 | fromPath: '/',
37 | toPath: '/hello'
38 | }
39 | ],
40 | `${tempFolderPath}/index.html`
41 | );
42 | });
43 |
44 | it('writes redirects to root', async () => {
45 | await assertRedirectFile(
46 | [
47 | {
48 | fromPath: '/hello',
49 | toPath: '/'
50 | }
51 | ],
52 | `${tempFolderPath}/hello/index.html`
53 | );
54 | });
55 |
56 | it('writes deep path redirects', async () => {
57 | await assertRedirectFile(
58 | [
59 | {
60 | fromPath: '/a/b/c/d',
61 | toPath: '/x/y/z'
62 | }
63 | ],
64 | `${tempFolderPath}/a/b/c/d/index.html`
65 | );
66 | });
67 |
68 | it('handles external redirects', async () => {
69 | await assertRedirectFile(
70 | [
71 | {
72 | fromPath: '/a/b',
73 | toPath: 'http://example.com/'
74 | }
75 | ],
76 | `${tempFolderPath}/a/b/index.html`
77 | );
78 | });
79 | });
80 |
--------------------------------------------------------------------------------