Skip to content

Commit ffed519

Browse files
committed
fix(stringToParts): fall back to legacy treatment for square brackets if square brackets contents aren't a number
Re: Automattic/mongoose#9640
1 parent 095573c commit ffed519

File tree

2 files changed

+34
-15
lines changed

2 files changed

+34
-15
lines changed

lib/stringToParts.js

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,45 @@ module.exports = function stringToParts(str) {
44
const result = [];
55

66
let curPropertyName = '';
7-
let inSquareBrackets = false;
7+
let state = 'DEFAULT';
88
for (let i = 0; i < str.length; ++i) {
9-
if (inSquareBrackets && !/\d/.test(str[i]) && str[i] !== ']') {
10-
throw new Error('Can only use numbers in square bracket path notation, got ' +
11-
'character "' + str[i] + '" in path "' + str + '"');
9+
// Fall back to treating as property name rather than bracket notation if
10+
// square brackets contains something other than a number.
11+
if (state === 'IN_SQUARE_BRACKETS' && !/\d/.test(str[i]) && str[i] !== ']') {
12+
state = 'DEFAULT';
13+
curPropertyName = result[result.length - 1] + '[' + curPropertyName;
14+
result.splice(result.length - 1, 1);
1215
}
1316

14-
if (str[i] === '.' || str[i] === '[' || str[i] === ']') {
15-
if (str[i] === '[') {
16-
inSquareBrackets = true;
17-
} else if (str[i] === ']') {
18-
inSquareBrackets = false;
17+
if (str[i] === '[') {
18+
if (state !== 'IMMEDIATELY_AFTER_SQUARE_BRACKETS') {
19+
result.push(curPropertyName);
20+
curPropertyName = '';
21+
}
22+
state = 'IN_SQUARE_BRACKETS';
23+
} else if (str[i] === ']') {
24+
if (state === 'IN_SQUARE_BRACKETS') {
25+
state = 'IMMEDIATELY_AFTER_SQUARE_BRACKETS';
26+
result.push(curPropertyName);
27+
curPropertyName = '';
28+
} else {
29+
state = 'DEFAULT';
30+
curPropertyName += str[i];
1931
}
20-
if (curPropertyName.length > 0) {
32+
} else if (str[i] === '.') {
33+
if (state !== 'IMMEDIATELY_AFTER_SQUARE_BRACKETS') {
2134
result.push(curPropertyName);
35+
curPropertyName = '';
2236
}
23-
curPropertyName = '';
37+
state = 'DEFAULT';
2438
} else {
2539
curPropertyName += str[i];
2640
}
2741
}
28-
result.push(curPropertyName);
42+
43+
if (state !== 'IMMEDIATELY_AFTER_SQUARE_BRACKETS') {
44+
result.push(curPropertyName);
45+
}
2946

3047
return result;
3148
};

test/stringToParts.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ describe('stringToParts', function() {
1111

1212
it('handles dot notation', function() {
1313
assert.deepEqual(stringToParts('a.b.c'), ['a', 'b', 'c']);
14-
assert.deepEqual(stringToParts('a..b.d'), ['a', 'b', 'd']);
14+
assert.deepEqual(stringToParts('a..b.d'), ['a', '', 'b', 'd']);
1515
});
1616

17-
it('throws for invalid numbers in square brackets', function() {
18-
assert.throws(() => stringToParts('foo[1mystring]'), /1mystring/);
17+
it('ignores invalid numbers in square brackets', function() {
18+
assert.deepEqual(stringToParts('foo[1mystring]'), ['foo[1mystring]']);
19+
assert.deepEqual(stringToParts('foo[1mystring].bar[1]'), ['foo[1mystring]', 'bar', '1']);
20+
assert.deepEqual(stringToParts('foo[1mystring][2]'), ['foo[1mystring]', '2']);
1921
});
2022

2123
it('handles empty string', function() {

0 commit comments

Comments
 (0)