diff --git a/lib/util/named-group-regexp.js b/lib/util/named-group-regexp.js index 3eaeb78..dc7372b 100644 --- a/lib/util/named-group-regexp.js +++ b/lib/util/named-group-regexp.js @@ -14,7 +14,7 @@ const pattern = [ '[>\']', // Get everything up to the end of the capture group: this is the RegExp used // when matching URLs to this route, which we can use for validation purposes. - '([^\\)]*(\\))?)\\??', + '((?:[^)(]|\\((?:[^)(]|\\((?:[^)(]|\\([^)(]*\\))*\\))*\\))*)', // Capture group end '\\)', ].join( '' ); diff --git a/lib/util/split-path.js b/lib/util/split-path.js index 42fcf3a..6d5341a 100644 --- a/lib/util/split-path.js +++ b/lib/util/split-path.js @@ -3,20 +3,7 @@ */ 'use strict'; -const namedGroupPattern = require( './named-group-regexp' ).pattern; - -// Convert capture groups to non-matching groups, because all capture groups -// are included in the resulting array when an RE is passed to `.split()` -// (We re-use the existing named group's capture pattern instead of creating -// a new RegExp just for this purpose) -const patternWithoutSubgroups = namedGroupPattern - .replace( /([^\\])\(([^?])/g, '$1(?:$2' ); - -// Make a new RegExp using the same pattern as one single unified capture group, -// so the match as a whole will be preserved after `.split()`. Permit non-slash -// characters before or after the named capture group, although those components -// will not yield functioning setters. -const namedGroupRE = new RegExp( '([^/]*' + patternWithoutSubgroups + '[^/]*)' ); +const namedGroupRE = require( './named-group-regexp' ).namedGroupRE; /** * Divide a route string up into hierarchical components by breaking it apart @@ -29,14 +16,24 @@ const namedGroupRE = new RegExp( '([^/]*' + patternWithoutSubgroups + '[^/]*)' ) * @param {String} pathStr A route path string to break into components * @returns {String[]} An array of route component strings */ -module.exports = pathStr => pathStr - // Divide a string like "/some/path/(?P<with_named_groups>)/etc" into an +module.exports = pathStr => { + let parts = [pathStr]; + // Find the named group. + const namedGroupMatch = pathStr.match(namedGroupRE); + if (namedGroupMatch) { + const namedGroup = namedGroupMatch[0]; + // Split the string into the parts surrounding the named group. + parts = pathStr.split(namedGroup); + // Add the named group into the array. + parts.splice(1, 0, namedGroup); + } + // This divides a string like "/some/path/(?P<with_named_groups>)/etc" into an // array `[ "/some/path/", "(?P<with_named_groups>)", "/etc" ]`. - .split( namedGroupRE ) + // Then, reduce through the array of parts, splitting any non-capture-group // parts on forward slashes and discarding empty strings to create the final // array of path components. - .reduce( ( components, part ) => { + return parts.reduce( ( components, part ) => { if ( ! part ) { // Ignore empty strings parts return components; @@ -50,3 +47,4 @@ module.exports = pathStr => pathStr // Split the part on / and filter out empty strings return components.concat( part.split( '/' ).filter( Boolean ) ); }, [] ); +}