diff --git a/@types/pcre-to-regexp/index.d.ts b/@types/pcre-to-regexp/index.d.ts new file mode 100644 index 000000000..8f6644697 --- /dev/null +++ b/@types/pcre-to-regexp/index.d.ts @@ -0,0 +1,3 @@ +declare module 'pcre-to-regexp' { + export default function (pattern: string, keys?: string[]): RegExp +} diff --git a/package.json b/package.json index 5e39d5aaa..df9fcd225 100644 --- a/package.json +++ b/package.json @@ -208,6 +208,7 @@ "npm-package-arg": "6.1.0", "nyc": "13.2.0", "ora": "1.3.0", + "pcre-to-regexp": "0.0.4", "pkg": "4.3.7", "pluralize": "7.0.0", "pre-commit": "1.2.2", diff --git a/src/commands/dev/lib/dev-router.ts b/src/commands/dev/lib/dev-router.ts index bf304a180..58451b87c 100644 --- a/src/commands/dev/lib/dev-router.ts +++ b/src/commands/dev/lib/dev-router.ts @@ -1,5 +1,6 @@ import url from 'url'; import qs from 'querystring'; +import PCRE from 'pcre-to-regexp'; import isURL from './is-url'; @@ -12,12 +13,39 @@ export default function(reqPath = '', routes?: RouteConfig[]): RouteResult { // try route match if (routes) { routes.find((routeConfig: RouteConfig, idx: number) => { - const matcher = new RegExp('^' + routeConfig.src + '$'); + let { src } = routeConfig; - if (matcher.test(reqPathname)) { - const destPath = routeConfig.dest - ? reqPathname.replace(matcher, routeConfig.dest) - : reqPathname; + if (!src.startsWith('^')) { + src = `^${src}`; + } + + if (!src.endsWith('$')) { + src = `${src}$`; + } + + const keys: string[] = []; + const matcher = PCRE(`%${src}%i`, keys); + const match = matcher.exec(reqPathname); + + if (match) { + let destPath: string = reqPathname; + if (routeConfig.dest) { + destPath = routeConfig.dest.replace( + /\$([1-9a-zA-Z]+)/g, + (_, param) => { + let matchIndex: number = keys.indexOf(param); + if (matchIndex === -1) { + // It's a number match, not a named capture + matchIndex = parseInt(param, 10); + } else { + // For named captures, add one to the `keys` index to + // match up with the RegExp group matches + matchIndex++; + } + return match[matchIndex]; + } + ); + } if (isURL(destPath)) { found = { diff --git a/src/commands/dev/lib/dev-server.ts b/src/commands/dev/lib/dev-server.ts index 9a51ca76d..ff0e0317d 100644 --- a/src/commands/dev/lib/dev-server.ts +++ b/src/commands/dev/lib/dev-server.ts @@ -316,10 +316,14 @@ export default class DevServer { } const body = await rawBody(req); + let path: string = dest; + if (Object.keys(uri_args || {}).length > 0) { + path += `?${qs.stringify(uri_args)}`; + } const payload: InvokePayload = { method: req.method || 'GET', - path: req.url || '/', + path, headers: req.headers, encoding: 'base64', body: body.toString('base64') diff --git a/test/dev-router.unit.js b/test/dev-router.unit.js index f5b98f99a..c0d8339b5 100644 --- a/test/dev-router.unit.js +++ b/test/dev-router.unit.js @@ -36,7 +36,7 @@ test('[dev-router] captured groups', t => { test('[dev-router] named groups', t => { const routesConfig = [ - { src: '/user/(?.+)', dest: '/user.js?id=$' } + { src: '/user/(?.+)', dest: '/user.js?id=$id' } ]; const result = devRouter('/user/123', routesConfig); diff --git a/yarn.lock b/yarn.lock index 4e0aadd9c..79954e59b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5448,6 +5448,11 @@ path-type@^3.0.0: dependencies: pify "^3.0.0" +pcre-to-regexp@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/pcre-to-regexp/-/pcre-to-regexp-0.0.4.tgz#a19c3fc6af540349f915367a4776c75c651dae05" + integrity sha1-oZw/xq9UA0n5FTZ6R3bHXGUdrgU= + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"