Skip to content

Commit 04c8487

Browse files
committed
Revert "Use () for optional params instead of ?"
This reverts commit a4af7a6.
1 parent c03f06c commit 04c8487

File tree

2 files changed

+61
-87
lines changed

2 files changed

+61
-87
lines changed

modules/PathUtils.js

Lines changed: 45 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,34 @@
1-
/* jshint -W084 */
21
var invariant = require('react/lib/invariant');
32
var assign = require('object-assign');
43
var qs = require('qs');
54

5+
var paramCompileMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$]*)|[*.()\[\]\\+|{}^$]/g;
6+
var paramInjectMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$?]*[?]?)|[*]/g;
7+
var paramInjectTrailingSlashMatcher = /\/\/\?|\/\?\/|\/\?/g;
68
var queryMatcher = /\?(.*)$/;
79

8-
function escapeRegExp(string) {
9-
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10-
}
11-
12-
function _compilePattern(pattern) {
13-
var escapedSource = '';
14-
var paramNames = [];
15-
var tokens = [];
16-
17-
var match, lastIndex = 0, matcher = /:([a-zA-Z_$][a-zA-Z0-9_$]*)|\*|\(|\)/g;
18-
while (match = matcher.exec(pattern)) {
19-
if (match.index !== lastIndex) {
20-
tokens.push(pattern.slice(lastIndex, match.index));
21-
escapedSource += escapeRegExp(pattern.slice(lastIndex, match.index));
22-
}
23-
24-
if (match[1]) {
25-
escapedSource += '([^/?#]+)';
26-
paramNames.push(match[1]);
27-
} else if (match[0] === '*') {
28-
escapedSource += '(.*?)';
29-
paramNames.push('splat');
30-
} else if (match[0] === '(') {
31-
escapedSource += '(?:';
32-
} else if (match[0] === ')') {
33-
escapedSource += ')?';
34-
}
35-
36-
tokens.push(match[0]);
37-
38-
lastIndex = matcher.lastIndex;
39-
}
40-
41-
if (lastIndex !== pattern.length) {
42-
tokens.push(pattern.slice(lastIndex, pattern.length));
43-
escapedSource += escapeRegExp(pattern.slice(lastIndex, pattern.length));
44-
}
45-
46-
return {
47-
pattern,
48-
escapedSource,
49-
paramNames,
50-
tokens
51-
};
52-
}
53-
5410
var _compiledPatterns = {};
5511

5612
function compilePattern(pattern) {
57-
if (!(pattern in _compiledPatterns))
58-
_compiledPatterns[pattern] = _compilePattern(pattern);
13+
if (!(pattern in _compiledPatterns)) {
14+
var paramNames = [];
15+
var source = pattern.replace(paramCompileMatcher, function (match, paramName) {
16+
if (paramName) {
17+
paramNames.push(paramName);
18+
return '([^/?#]+)';
19+
} else if (match === '*') {
20+
paramNames.push('splat');
21+
return '(.*?)';
22+
} else {
23+
return '\\' + match;
24+
}
25+
});
26+
27+
_compiledPatterns[pattern] = {
28+
matcher: new RegExp('^' + source + '$', 'i'),
29+
paramNames: paramNames
30+
};
31+
}
5932

6033
return _compiledPatterns[pattern];
6134
}
@@ -89,8 +62,7 @@ var PathUtils = {
8962
* pattern does not match the given path.
9063
*/
9164
extractParams: function (pattern, path) {
92-
var { escapedSource, paramNames } = compilePattern(pattern);
93-
var matcher = new RegExp('^' + escapedSource + '$', 'i');
65+
var { matcher, paramNames } = compilePattern(pattern);
9466
var match = path.match(matcher);
9567

9668
if (!match)
@@ -112,46 +84,40 @@ var PathUtils = {
11284
injectParams: function (pattern, params) {
11385
params = params || {};
11486

115-
var { tokens } = compilePattern(pattern);
116-
var parenCount = 0, pathname = '', splatIndex = 0;
87+
var splatIndex = 0;
11788

118-
var token, paramName, paramValue;
119-
for (var i = 0, len = tokens.length; i < len; ++i) {
120-
token = tokens[i];
89+
return pattern.replace(paramInjectMatcher, function (match, paramName) {
90+
paramName = paramName || 'splat';
12191

122-
if (token === '*') {
123-
paramValue = Array.isArray(params.splat) ? params.splat[splatIndex++] : params.splat;
92+
// If param is optional don't check for existence
93+
if (paramName.slice(-1) === '?') {
94+
paramName = paramName.slice(0, -1);
12495

96+
if (params[paramName] == null)
97+
return '';
98+
} else {
12599
invariant(
126-
paramValue != null || parenCount > 0,
127-
'Missing splat #%s for path "%s"',
128-
splatIndex, pattern
100+
params[paramName] != null,
101+
'Missing "%s" parameter for path "%s"',
102+
paramName, pattern
129103
);
104+
}
130105

131-
if (paramValue != null)
132-
pathname += paramValue;
133-
} else if (token === '(') {
134-
parenCount += 1;
135-
} else if (token === ')') {
136-
parenCount -= 1;
137-
} else if (token.charAt(0) === ':') {
138-
paramName = token.substring(1);
139-
paramValue = params[paramName];
106+
var segment;
107+
if (paramName === 'splat' && Array.isArray(params[paramName])) {
108+
segment = params[paramName][splatIndex++];
140109

141110
invariant(
142-
paramValue != null || parenCount > 0,
143-
'Missing "%s" parameter for path "%s"',
144-
paramName, pattern
111+
segment != null,
112+
'Missing splat # %s for path "%s"',
113+
splatIndex, pattern
145114
);
146-
147-
if (paramValue != null)
148-
pathname += paramValue;
149115
} else {
150-
pathname += token;
116+
segment = params[paramName];
151117
}
152-
}
153118

154-
return pathname.replace(/\/+/g, '/');
119+
return segment;
120+
}).replace(paramInjectTrailingSlashMatcher, '/');
155121
},
156122

157123
/**

modules/__tests__/PathUtils-test.js

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ describe('PathUtils.extractParams', function () {
4848
});
4949

5050
describe('and the pattern is optional', function () {
51-
var pattern = 'comments/(:id)/edit';
51+
var pattern = 'comments/:id?/edit';
5252

5353
describe('and the path matches with supplied param', function () {
5454
it('returns an object with the params', function () {
@@ -64,7 +64,7 @@ describe('PathUtils.extractParams', function () {
6464
});
6565

6666
describe('and the pattern and forward slash are optional', function () {
67-
var pattern = 'comments(/:id)/edit';
67+
var pattern = 'comments/:id?/?edit';
6868

6969
describe('and the path matches with supplied param', function () {
7070
it('returns an object with the params', function () {
@@ -140,13 +140,15 @@ describe('PathUtils.extractParams', function () {
140140
});
141141
});
142142

143-
describe('when a pattern has an optional group', function () {
144-
var pattern = '/archive(/:name)';
143+
describe('when a pattern has a ?', function () {
144+
var pattern = '/archive/?:name?';
145145

146146
describe('and the path matches', function () {
147147
it('returns an object with the params', function () {
148-
expect(PathUtils.extractParams(pattern, '/archive/foo')).toEqual({ name: 'foo' });
149148
expect(PathUtils.extractParams(pattern, '/archive')).toEqual({ name: undefined });
149+
expect(PathUtils.extractParams(pattern, '/archive/')).toEqual({ name: undefined });
150+
expect(PathUtils.extractParams(pattern, '/archive/foo')).toEqual({ name: 'foo' });
151+
expect(PathUtils.extractParams(pattern, '/archivefoo')).toEqual({ name: 'foo' });
150152
});
151153
});
152154

@@ -197,19 +199,19 @@ describe('PathUtils.injectParams', function () {
197199
});
198200

199201
describe('and a param is optional', function () {
200-
var pattern = 'comments/(:id)/edit';
202+
var pattern = 'comments/:id?/edit';
201203

202204
it('returns the correct path when param is supplied', function () {
203205
expect(PathUtils.injectParams(pattern, { id:'123' })).toEqual('comments/123/edit');
204206
});
205207

206208
it('returns the correct path when param is not supplied', function () {
207-
expect(PathUtils.injectParams(pattern, {})).toEqual('comments/edit');
209+
expect(PathUtils.injectParams(pattern, {})).toEqual('comments//edit');
208210
});
209211
});
210212

211213
describe('and a param and forward slash are optional', function () {
212-
var pattern = 'comments(/:id)/edit';
214+
var pattern = 'comments/:id?/?edit';
213215

214216
it('returns the correct path when param is supplied', function () {
215217
expect(PathUtils.injectParams(pattern, { id:'123' })).toEqual('comments/123/edit');
@@ -272,6 +274,12 @@ describe('PathUtils.injectParams', function () {
272274
expect(PathUtils.injectParams('/foo.bar.baz')).toEqual('/foo.bar.baz');
273275
});
274276
});
277+
278+
describe('when a pattern has optional slashes', function () {
279+
it('returns the correct path', function () {
280+
expect(PathUtils.injectParams('/foo/?/bar/?/baz/?')).toEqual('/foo/bar/baz/');
281+
});
282+
});
275283
});
276284

277285
describe('PathUtils.extractQuery', function () {

0 commit comments

Comments
 (0)