Skip to content

Commit

Permalink
feat(gatsby): ignore case option in create redirect (#29742)
Browse files Browse the repository at this point in the history
* Add ignoreCase option for createRedirect and support it in client side navigation

* Add typings for ignoreCase

* Update packages/gatsby/src/redux/actions/public.js

Co-authored-by: Michal Piechowiak <misiek.piechowiak@gmail.com>

* Prepare lowercased redirects at build time

* Add tests

* Remove only

* Switch to O(1) Maps

* Update navigate as well

* Update packages/gatsby/index.d.ts

Co-authored-by: Ward Peeters <ward@coding-tech.com>

* Update packages/gatsby/cache-dir/navigation.js

Co-authored-by: Ward Peeters <ward@coding-tech.com>

* Set ignoreCase to false by default

* Update snapshot

* toLowerCase not toLowercase fml

Co-authored-by: Michal Piechowiak <misiek.piechowiak@gmail.com>
Co-authored-by: Ward Peeters <ward@coding-tech.com>
  • Loading branch information
3 people committed Feb 25, 2021
1 parent 662fe41 commit 91b9d66
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 60 deletions.
12 changes: 12 additions & 0 deletions e2e-tests/production-runtime/cypress/integration/redirects.js
@@ -0,0 +1,12 @@
describe(`Redirects`, () => {
it(`are case insensitive when ignoreCase is set to true`, () => {
cy.visit(`/Longue-PAGE`, { failOnStatusCode: false }).waitForRouteChange()

cy.get(`h1`).invoke(`text`).should(`contain`, `Hi from the long page`)
})
it(`are case sensitive when ignoreCase is set to false`, () => {
cy.visit(`/PAGINA-larga`, { failOnStatusCode: false }).waitForRouteChange()

cy.get(`h1`).invoke(`text`).should(`contain`, `NOT FOUND`)
})
})
18 changes: 17 additions & 1 deletion e2e-tests/production-runtime/gatsby-node.js
Expand Up @@ -36,7 +36,7 @@ exports.sourceNodes = ({ actions, createNodeId }) => {
})
}

exports.createPages = ({ actions: { createPage } }) => {
exports.createPages = ({ actions: { createPage, createRedirect } }) => {
createPage({
path: `/안녕`,
component: path.resolve(`src/pages/page-2.js`),
Expand Down Expand Up @@ -124,6 +124,22 @@ exports.createPages = ({ actions: { createPage } }) => {
path: `/page-from-cache/`,
component: path.resolve(`./.cache/static-page-from-cache.js`),
})

createRedirect({
fromPath: "/pagina-larga",
toPath: "/long-page",
isPermanent: true,
redirectInBrowser: true,
ignoreCase: false,
})

createRedirect({
fromPath: "/Longue-Page",
toPath: "/long-page",
isPermanent: true,
redirectInBrowser: true,
ignoreCase: true,
})
}

exports.onCreatePage = ({ page, actions }) => {
Expand Down
25 changes: 19 additions & 6 deletions packages/gatsby/cache-dir/navigation.js
Expand Up @@ -10,13 +10,23 @@ import { globalHistory } from "@reach/router/lib/history"
import { parsePath } from "gatsby-link"

// Convert to a map for faster lookup in maybeRedirect()
const redirectMap = redirects.reduce((map, redirect) => {
map[redirect.fromPath] = redirect
return map
}, {})

const redirectMap = new Map()
const redirectIgnoreCaseMap = new Map()

redirects.forEach(redirect => {
if (redirect.ignoreCase) {
redirectIgnoreCaseMap.set(redirect.fromPath, redirect)
} else {
redirectMap.set(redirect.fromPath, redirect)
}
})

function maybeRedirect(pathname) {
const redirect = redirectMap[pathname]
let redirect = redirectMap.get(pathname)
if (!redirect) {
redirect = redirectIgnoreCaseMap.get(pathname.toLowerCase())
}

if (redirect != null) {
if (process.env.NODE_ENV !== `production`) {
Expand Down Expand Up @@ -62,7 +72,10 @@ const navigate = (to, options = {}) => {
}

let { pathname } = parsePath(to)
const redirect = redirectMap[pathname]
let redirect = redirectMap.get(pathname)
if (!redirect) {
redirect = redirectIgnoreCaseMap.get(pathname.toLowerCase())
}

// If we're redirecting, just replace the passed in pathname
// to the one we want to redirect to.
Expand Down
1 change: 1 addition & 0 deletions packages/gatsby/index.d.ts
Expand Up @@ -1256,6 +1256,7 @@ export interface Actions {
redirectInBrowser?: boolean
force?: boolean
statusCode?: number
ignoreCase?: boolean
[key: string]: unknown
},
plugin?: ActionPlugin
Expand Down
10 changes: 9 additions & 1 deletion packages/gatsby/src/bootstrap/redirects-writer.ts
Expand Up @@ -16,7 +16,15 @@ export const writeRedirects = async (): Promise<void> => {
const browserRedirects = redirects
.filter(r => r.redirectInBrowser)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.map(({ redirectInBrowser, isPermanent, ...rest }) => rest)
.map(
({ redirectInBrowser, isPermanent, ignoreCase, fromPath, ...rest }) => {
return {
fromPath: ignoreCase ? fromPath.toLowerCase() : fromPath,
ignoreCase,
...rest,
}
}
)

const newHash = crypto
.createHash(`md5`)
Expand Down
52 changes: 0 additions & 52 deletions packages/gatsby/src/query/redirects-writer.ts

This file was deleted.

Expand Up @@ -4,6 +4,7 @@ exports[`Add redirects allows you to add redirects 1`] = `
Object {
"payload": Object {
"fromPath": "/old/hello-world",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "/new/hello-world",
Expand All @@ -16,6 +17,7 @@ exports[`Add redirects create redirects as permanent 1`] = `
Object {
"payload": Object {
"fromPath": "/old/hello-world",
"ignoreCase": false,
"isPermanent": true,
"redirectInBrowser": false,
"toPath": "/new/hello-world",
Expand All @@ -28,6 +30,7 @@ exports[`Add redirects creates redirects from the URL starts with // 1`] = `
Object {
"payload": Object {
"fromPath": "//example.com",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "/new/hello-world-2",
Expand All @@ -40,6 +43,7 @@ exports[`Add redirects creates redirects from the URL starts with ftp 1`] = `
Object {
"payload": Object {
"fromPath": "ftp://example.com",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "/new/hello-world-3",
Expand All @@ -52,6 +56,7 @@ exports[`Add redirects creates redirects from the URL starts with http 1`] = `
Object {
"payload": Object {
"fromPath": "http://example.com",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "/new/hello-world-1",
Expand All @@ -64,6 +69,7 @@ exports[`Add redirects creates redirects from the URL starts with https 1`] = `
Object {
"payload": Object {
"fromPath": "https://example.com",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "/new/hello-world-0",
Expand All @@ -76,6 +82,7 @@ exports[`Add redirects creates redirects from the URL starts with mailto 1`] = `
Object {
"payload": Object {
"fromPath": "mailto:example@email.com",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "/new/hello-world-4",
Expand All @@ -88,6 +95,7 @@ exports[`Add redirects creates redirects to the URL starts with // 1`] = `
Object {
"payload": Object {
"fromPath": "/old/hello-world-2",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "//example.com",
Expand All @@ -100,6 +108,7 @@ exports[`Add redirects creates redirects to the URL starts with ftp 1`] = `
Object {
"payload": Object {
"fromPath": "/old/hello-world-3",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "ftp://example.com",
Expand All @@ -112,6 +121,7 @@ exports[`Add redirects creates redirects to the URL starts with http 1`] = `
Object {
"payload": Object {
"fromPath": "/old/hello-world-1",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "http://example.com",
Expand All @@ -124,6 +134,7 @@ exports[`Add redirects creates redirects to the URL starts with https 1`] = `
Object {
"payload": Object {
"fromPath": "/old/hello-world-0",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "https://example.com",
Expand All @@ -136,6 +147,7 @@ exports[`Add redirects creates redirects to the URL starts with mailto 1`] = `
Object {
"payload": Object {
"fromPath": "/old/hello-world-4",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "mailto:example@email.com",
Expand All @@ -148,6 +160,7 @@ exports[`Add redirects creates redirects with in-browser redirect option 1`] = `
Object {
"payload": Object {
"fromPath": "/old/hello-world",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": true,
"toPath": "/new/hello-world",
Expand All @@ -160,6 +173,7 @@ exports[`Add redirects with path prefixs allows you to add redirects 1`] = `
Object {
"payload": Object {
"fromPath": "/blog/old/hello-world",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "/blog/new/hello-world",
Expand All @@ -172,6 +186,7 @@ exports[`Add redirects with path prefixs create redirects as permanent 1`] = `
Object {
"payload": Object {
"fromPath": "/blog/old/hello-world",
"ignoreCase": false,
"isPermanent": true,
"redirectInBrowser": false,
"toPath": "/blog/new/hello-world",
Expand All @@ -184,6 +199,7 @@ exports[`Add redirects with path prefixs creates redirects from the URL starts w
Object {
"payload": Object {
"fromPath": "//example.com",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "/blog/new/hello-world-2",
Expand All @@ -196,6 +212,7 @@ exports[`Add redirects with path prefixs creates redirects from the URL starts w
Object {
"payload": Object {
"fromPath": "ftp://example.com",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "/blog/new/hello-world-3",
Expand All @@ -208,6 +225,7 @@ exports[`Add redirects with path prefixs creates redirects from the URL starts w
Object {
"payload": Object {
"fromPath": "http://example.com",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "/blog/new/hello-world-1",
Expand All @@ -220,6 +238,7 @@ exports[`Add redirects with path prefixs creates redirects from the URL starts w
Object {
"payload": Object {
"fromPath": "https://example.com",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "/blog/new/hello-world-0",
Expand All @@ -232,6 +251,7 @@ exports[`Add redirects with path prefixs creates redirects from the URL starts w
Object {
"payload": Object {
"fromPath": "mailto:example@email.com",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "/blog/new/hello-world-4",
Expand All @@ -244,6 +264,7 @@ exports[`Add redirects with path prefixs creates redirects to the URL starts wit
Object {
"payload": Object {
"fromPath": "/blog/old/hello-world-2",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "//example.com",
Expand All @@ -256,6 +277,7 @@ exports[`Add redirects with path prefixs creates redirects to the URL starts wit
Object {
"payload": Object {
"fromPath": "/blog/old/hello-world-3",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "ftp://example.com",
Expand All @@ -268,6 +290,7 @@ exports[`Add redirects with path prefixs creates redirects to the URL starts wit
Object {
"payload": Object {
"fromPath": "/blog/old/hello-world-1",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "http://example.com",
Expand All @@ -280,6 +303,7 @@ exports[`Add redirects with path prefixs creates redirects to the URL starts wit
Object {
"payload": Object {
"fromPath": "/blog/old/hello-world-0",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "https://example.com",
Expand All @@ -292,6 +316,7 @@ exports[`Add redirects with path prefixs creates redirects to the URL starts wit
Object {
"payload": Object {
"fromPath": "/blog/old/hello-world-4",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": false,
"toPath": "mailto:example@email.com",
Expand All @@ -304,6 +329,7 @@ exports[`Add redirects with path prefixs creates redirects with in-browser redir
Object {
"payload": Object {
"fromPath": "/blog/old/hello-world",
"ignoreCase": false,
"isPermanent": false,
"redirectInBrowser": true,
"toPath": "/blog/new/hello-world",
Expand Down
3 changes: 3 additions & 0 deletions packages/gatsby/src/redux/actions/public.js
Expand Up @@ -1299,6 +1299,7 @@ const maybeAddPathPrefix = (path, pathPrefix) => {
* @param {boolean} redirect.redirectInBrowser Redirects are generally for redirecting legacy URLs to their new configuration. If you can't update your UI for some reason, set `redirectInBrowser` to true and Gatsby will handle redirecting in the client as well.
* @param {boolean} redirect.force (Plugin-specific) Will trigger the redirect even if the `fromPath` matches a piece of content. This is not part of the Gatsby API, but implemented by (some) plugins that configure hosting provider redirects
* @param {number} redirect.statusCode (Plugin-specific) Manually set the HTTP status code. This allows you to create a rewrite (status code 200) or custom error page (status code 404). Note that this will override the `isPermanent` option which also sets the status code. This is not part of the Gatsby API, but implemented by (some) plugins that configure hosting provider redirects
* @param {boolean} redirect.ignoreCase (Plugin-specific) Ignore case when looking for redirects
* @example
* // Generally you create redirects while creating pages.
* exports.createPages = ({ graphql, actions }) => {
Expand All @@ -1314,6 +1315,7 @@ actions.createRedirect = ({
isPermanent = false,
redirectInBrowser = false,
toPath,
ignoreCase = false,
...rest
}) => {
let pathPrefix = ``
Expand All @@ -1326,6 +1328,7 @@ actions.createRedirect = ({
payload: {
fromPath: maybeAddPathPrefix(fromPath, pathPrefix),
isPermanent,
ignoreCase,
redirectInBrowser,
toPath: maybeAddPathPrefix(toPath, pathPrefix),
...rest,
Expand Down

0 comments on commit 91b9d66

Please sign in to comment.