diff --git a/integration-tests/functions/__tests__/__snapshots__/functions-dev.js.snap b/integration-tests/functions/__tests__/__snapshots__/functions-dev.js.snap index 8a67d275d8f84..ff56c6b025dde 100644 --- a/integration-tests/functions/__tests__/__snapshots__/functions-dev.js.snap +++ b/integration-tests/functions/__tests__/__snapshots__/functions-dev.js.snap @@ -1,5 +1,19 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`develop dynamic routes param routes 1`] = ` +Object { + "super": "additional", + "userId": "23", +} +`; + +exports[`develop dynamic routes unnamed wildcard routes 1`] = ` +Object { + "*": "super", + "0": "super", +} +`; + exports[`develop functions can parse different ways of sending data file in multipart/form 1`] = ` Array [ Object { @@ -90,35 +104,22 @@ Object { } `; -exports[`develop routing dynamic routes 1`] = ` -Object { - "super": "additional", - "userId": "23", -} -`; - -exports[`develop routing dynamic routes 2`] = ` -Object { - "0": "super", -} -`; - -exports[`develop routing routes with special characters 1`] = `"I-Am-Capitalized.js"`; +exports[`develop routes with special characters 1`] = `"I-Am-Capitalized.js"`; -exports[`develop routing routes with special characters 2`] = `"some whitespace.js"`; +exports[`develop routes with special characters 2`] = `"some whitespace.js"`; -exports[`develop routing routes with special characters 3`] = `"with-äöü-umlaut.js"`; +exports[`develop routes with special characters 3`] = `"with-äöü-umlaut.js"`; -exports[`develop routing routes with special characters 4`] = `"some-àè-french.js"`; +exports[`develop routes with special characters 4`] = `"some-àè-french.js"`; -exports[`develop routing routes with special characters 5`] = `"some-אודות.js"`; +exports[`develop routes with special characters 5`] = `"some-אודות.js"`; -exports[`develop routing secondary-level API 1`] = `"I am at a secondary-level"`; +exports[`develop secondary-level API 1`] = `"I am at a secondary-level"`; -exports[`develop routing secondary-level API 2`] = `"I am another sub-directory function"`; +exports[`develop secondary-level API 2`] = `"I am another sub-directory function"`; -exports[`develop routing secondary-level API with index.js 1`] = `"I am an index.js in a sub-directory!"`; +exports[`develop secondary-level API with index.js 1`] = `"I am an index.js in a sub-directory!"`; -exports[`develop routing top-level API 1`] = `"I am at the top-level"`; +exports[`develop top-level API 1`] = `"I am at the top-level"`; exports[`develop typescript typescript functions work 1`] = `"I am typescript"`; diff --git a/integration-tests/functions/__tests__/__snapshots__/functions-prod.js.snap b/integration-tests/functions/__tests__/__snapshots__/functions-prod.js.snap index d46bdd617bc45..72ee383760b68 100644 --- a/integration-tests/functions/__tests__/__snapshots__/functions-prod.js.snap +++ b/integration-tests/functions/__tests__/__snapshots__/functions-prod.js.snap @@ -1,5 +1,19 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`production dynamic routes param routes 1`] = ` +Object { + "super": "additional", + "userId": "23", +} +`; + +exports[`production dynamic routes unnamed wildcard routes 1`] = ` +Object { + "*": "super", + "0": "super", +} +`; + exports[`production functions can parse different ways of sending data file in multipart/form 1`] = ` Array [ Object { @@ -86,35 +100,22 @@ Object { } `; -exports[`production routing dynamic routes 1`] = ` -Object { - "super": "additional", - "userId": "23", -} -`; - -exports[`production routing dynamic routes 2`] = ` -Object { - "0": "super", -} -`; - -exports[`production routing routes with special characters 1`] = `"I-Am-Capitalized.js"`; +exports[`production routes with special characters 1`] = `"I-Am-Capitalized.js"`; -exports[`production routing routes with special characters 2`] = `"some whitespace.js"`; +exports[`production routes with special characters 2`] = `"some whitespace.js"`; -exports[`production routing routes with special characters 3`] = `"with-äöü-umlaut.js"`; +exports[`production routes with special characters 3`] = `"with-äöü-umlaut.js"`; -exports[`production routing routes with special characters 4`] = `"some-àè-french.js"`; +exports[`production routes with special characters 4`] = `"some-àè-french.js"`; -exports[`production routing routes with special characters 5`] = `"some-אודות.js"`; +exports[`production routes with special characters 5`] = `"some-אודות.js"`; -exports[`production routing secondary-level API 1`] = `"I am at a secondary-level"`; +exports[`production secondary-level API 1`] = `"I am at a secondary-level"`; -exports[`production routing secondary-level API 2`] = `"I am another sub-directory function"`; +exports[`production secondary-level API 2`] = `"I am another sub-directory function"`; -exports[`production routing secondary-level API with index.js 1`] = `"I am an index.js in a sub-directory!"`; +exports[`production secondary-level API with index.js 1`] = `"I am an index.js in a sub-directory!"`; -exports[`production routing top-level API 1`] = `"I am at the top-level"`; +exports[`production top-level API 1`] = `"I am at the top-level"`; exports[`production typescript typescript functions work 1`] = `"I am typescript"`; diff --git a/integration-tests/functions/src/api/named-wildcard/[...foo].js b/integration-tests/functions/src/api/named-wildcard/[...foo].js new file mode 100644 index 0000000000000..c585268295a4e --- /dev/null +++ b/integration-tests/functions/src/api/named-wildcard/[...foo].js @@ -0,0 +1,3 @@ +export default function (req, res) { + res.json(req.params) +} diff --git a/integration-tests/functions/test-helpers.js b/integration-tests/functions/test-helpers.js index 2951738b6f440..665697864460d 100644 --- a/integration-tests/functions/test-helpers.js +++ b/integration-tests/functions/test-helpers.js @@ -7,63 +7,79 @@ const FormData = require("form-data") export function runTests(env, host) { describe(env, () => { - describe(`routing`, () => { - test(`top-level API`, async () => { - const result = await fetch(`${host}/api/top-level`).then(res => - res.text() - ) + test(`top-level API`, async () => { + const result = await fetch(`${host}/api/top-level`).then(res => + res.text() + ) - expect(result).toMatchSnapshot() - }) - test(`secondary-level API`, async () => { - const result = await fetch( - `${host}/api/a-directory/function` - ).then(res => res.text()) + expect(result).toMatchSnapshot() + }) + test(`secondary-level API`, async () => { + const result = await fetch(`${host}/api/a-directory/function`).then(res => + res.text() + ) - expect(result).toMatchSnapshot() - }) - test(`secondary-level API with index.js`, async () => { - const result = await fetch(`${host}/api/a-directory`).then(res => - res.text() - ) + expect(result).toMatchSnapshot() + }) + test(`secondary-level API with index.js`, async () => { + const result = await fetch(`${host}/api/a-directory`).then(res => + res.text() + ) - expect(result).toMatchSnapshot() - }) - test(`secondary-level API`, async () => { - const result = await fetch(`${host}/api/dir/function`).then(res => - res.text() - ) + expect(result).toMatchSnapshot() + }) + test(`secondary-level API`, async () => { + const result = await fetch(`${host}/api/dir/function`).then(res => + res.text() + ) + + expect(result).toMatchSnapshot() + }) + + test(`routes with special characters`, async () => { + const routes = [ + `${host}/api/I-Am-Capitalized`, + `${host}/api/some whitespace`, + `${host}/api/with-äöü-umlaut`, + `${host}/api/some-àè-french`, + encodeURI(`${host}/api/some-אודות`), + ] + + for (const route of routes) { + const result = await fetch(route).then(res => res.text()) expect(result).toMatchSnapshot() - }) - test(`routes with special characters`, async () => { - const routes = [ - `${host}/api/I-Am-Capitalized`, - `${host}/api/some whitespace`, - `${host}/api/with-äöü-umlaut`, - `${host}/api/some-àè-french`, - encodeURI(`${host}/api/some-אודות`), - ] + } + }) + + describe(`dynamic routes`, () => { + test(`param routes`, async () => { + const routes = [`${host}/api/users/23/additional`] for (const route of routes) { - const result = await fetch(route).then(res => res.text()) + const result = await fetch(route).then(res => res.json()) expect(result).toMatchSnapshot() } }) - - test(`dynamic routes`, async () => { - const routes = [ - `${host}/api/users/23/additional`, - `${host}/api/dir/super`, - ] - + test(`unnamed wildcard routes`, async () => { + const routes = [`${host}/api/dir/super`] for (const route of routes) { const result = await fetch(route).then(res => res.json()) expect(result).toMatchSnapshot() } }) + test(`named wildcard routes`, async () => { + const route = `${host}/api/named-wildcard/super` + const result = await fetch(route).then(res => res.json()) + + expect(result).toMatchInlineSnapshot(` + Object { + "foo": "super", + } + `) + }) }) describe(`environment variables`, () => { diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 01f2c03124970..20e587990bc9b 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -118,7 +118,6 @@ "opentracing": "^0.14.4", "p-defer": "^3.0.0", "parseurl": "^1.3.3", - "path-to-regexp": "0.1.7", "physical-cpu-count": "^2.0.0", "platform": "^1.3.6", "postcss": "^8.3.5", diff --git a/packages/gatsby/src/commands/serve.ts b/packages/gatsby/src/commands/serve.ts index e764ca1c2b7f7..1027668536c90 100644 --- a/packages/gatsby/src/commands/serve.ts +++ b/packages/gatsby/src/commands/serve.ts @@ -8,7 +8,6 @@ import { match as reachMatch } from "@gatsbyjs/reach-router/lib/utils" import onExit from "signal-exit" import report from "gatsby-cli/lib/reporter" import multer from "multer" -import pathToRegexp from "path-to-regexp" import cookie from "cookie" import telemetry from "gatsby-telemetry" @@ -25,14 +24,6 @@ interface IMatchPath { matchPath: string } -interface IPathToRegexpKey { - name: string | number - prefix: string - suffix: string - pattern: string - modifier: string -} - interface IServeProgram extends IProgram { prefixPaths: boolean } @@ -169,25 +160,22 @@ module.exports = async (program: IServeProgram): Promise => { // Check if there's any matchPaths that match. // We loop until we find the first match. functions.some(f => { - let exp - const keys: Array = [] if (f.matchPath) { - exp = pathToRegexp(f.matchPath, keys) - } - if (exp && exp.exec(pathFragment) !== null) { - functionObj = f - // @ts-ignore - TS bug? https://stackoverflow.com/questions/50234481/typescript-2-8-3-type-must-have-a-symbol-iterator-method-that-returns-an-iterato - const matches = [...pathFragment.match(exp)].slice(1) - const newParams = {} - matches.forEach( - (match, index) => (newParams[keys[index].name] = match) - ) - req.params = newParams + const matchResult = reachMatch(f.matchPath, pathFragment) + if (matchResult) { + req.params = matchResult.params + if (req.params[`*`]) { + // Backwards compatability for v3 + // TODO remove in v5 + req.params[`0`] = req.params[`*`] + } + functionObj = f - return true - } else { - return false + return true + } } + + return false }) } diff --git a/packages/gatsby/src/internal-plugins/functions/gatsby-node.ts b/packages/gatsby/src/internal-plugins/functions/gatsby-node.ts index 64712c3b03a49..eafbe7f3333e1 100644 --- a/packages/gatsby/src/internal-plugins/functions/gatsby-node.ts +++ b/packages/gatsby/src/internal-plugins/functions/gatsby-node.ts @@ -10,9 +10,7 @@ import { CreateDevServerArgs, ParentSpanPluginArgs } from "gatsby" import formatWebpackMessages from "react-dev-utils/formatWebpackMessages" import dotenv from "dotenv" import chokidar from "chokidar" -// We use an ancient version of path-to-regexp as it has breaking changes to express v4 -// see: https://github.com/pillarjs/path-to-regexp/tree/77df63869075cfa5feda1988642080162c584427#compatibility-with-express--4x -import pathToRegexp from "path-to-regexp" +import { match } from "@gatsbyjs/reach-router/lib/utils" import cookie from "cookie" import { reportWebpackWarnings } from "../../utils/webpack-error-utils" import { internalActions } from "../../redux/actions" @@ -29,14 +27,6 @@ interface IGlobPattern { globPattern: string } -interface IPathToRegexpKey { - name: string | number - prefix: string - suffix: string - pattern: string - modifier: string -} - // During development, we lazily compile functions only when they're requested. // Here we keep track of which functions have been requested so are "active" const activeDevelopmentFunctions = new Set() @@ -496,24 +486,22 @@ export async function onCreateDevServer({ // Check if there's any matchPaths that match. // We loop until we find the first match. functions.some(f => { - let exp - const keys: Array = [] if (f.matchPath) { - exp = pathToRegexp(f.matchPath, keys) + const matchResult = match(f.matchPath, pathFragment) + if (matchResult) { + req.params = matchResult.params + if (req.params[`*`]) { + // Backwards compatability for v3 + // TODO remove in v5 + req.params[`0`] = req.params[`*`] + } + functionObj = f + + return true + } } - if (exp && exp.exec(pathFragment) !== null) { - functionObj = f - const matches = [...pathFragment.match(exp)].slice(1) - const newParams = {} - matches.forEach( - (match, index) => (newParams[keys[index].name] = match) - ) - req.params = newParams - return true - } else { - return false - } + return false }) }