Skip to content

Commit 1d192ae

Browse files
authored
add support for optional chaining (#412)
Co-authored-by: sanex3339 <yarabotayuvyandex3339>
1 parent e8e2e81 commit 1d192ae

6 files changed

+224
-12
lines changed

escodegen.js

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,12 @@
9999
Await: 14,
100100
Unary: 14,
101101
Postfix: 15,
102-
Call: 16,
103-
New: 17,
104-
TaggedTemplate: 18,
105-
Member: 19,
106-
Primary: 20
102+
OptionalChaining: 16,
103+
Call: 17,
104+
New: 18,
105+
TaggedTemplate: 19,
106+
Member: 20,
107+
Primary: 21
107108
};
108109

109110
BinaryPrecedence = {
@@ -1871,8 +1872,14 @@
18711872

18721873
CallExpression: function (expr, precedence, flags) {
18731874
var result, i, iz;
1875+
18741876
// F_ALLOW_UNPARATH_NEW becomes false.
18751877
result = [this.generateExpression(expr.callee, Precedence.Call, E_TTF)];
1878+
1879+
if (expr.optional) {
1880+
result.push('?.');
1881+
}
1882+
18761883
result.push('(');
18771884
for (i = 0, iz = expr['arguments'].length; i < iz; ++i) {
18781885
result.push(this.generateExpression(expr['arguments'][i], Precedence.Assignment, E_TTT));
@@ -1885,9 +1892,20 @@
18851892
if (!(flags & F_ALLOW_CALL)) {
18861893
return ['(', result, ')'];
18871894
}
1895+
18881896
return parenthesize(result, Precedence.Call, precedence);
18891897
},
18901898

1899+
ChainExpression: function (expr, precedence, flags) {
1900+
if (Precedence.OptionalChaining < precedence) {
1901+
flags |= F_ALLOW_CALL;
1902+
}
1903+
1904+
var result = this.generateExpression(expr.expression, Precedence.OptionalChaining, flags);
1905+
1906+
return parenthesize(result, Precedence.OptionalChaining, precedence);
1907+
},
1908+
18911909
NewExpression: function (expr, precedence, flags) {
18921910
var result, length, i, iz, itemFlags;
18931911
length = expr['arguments'].length;
@@ -1922,11 +1940,15 @@
19221940
result = [this.generateExpression(expr.object, Precedence.Call, (flags & F_ALLOW_CALL) ? E_TTF : E_TFF)];
19231941

19241942
if (expr.computed) {
1943+
if (expr.optional) {
1944+
result.push('?.');
1945+
}
1946+
19251947
result.push('[');
19261948
result.push(this.generateExpression(expr.property, Precedence.Sequence, flags & F_ALLOW_CALL ? E_TTT : E_TFT));
19271949
result.push(']');
19281950
} else {
1929-
if (expr.object.type === Syntax.Literal && typeof expr.object.value === 'number') {
1951+
if (!expr.optional && expr.object.type === Syntax.Literal && typeof expr.object.value === 'number') {
19301952
fragment = toSourceNodeWhenNeeded(result).toString();
19311953
// When the following conditions are all true,
19321954
// 1. No floating point
@@ -1943,7 +1965,7 @@
19431965
result.push(' ');
19441966
}
19451967
}
1946-
result.push('.');
1968+
result.push(expr.optional ? '?.' : '.');
19471969
result.push(generateIdentifier(expr.property));
19481970
}
19491971

@@ -2457,8 +2479,7 @@
24572479
this.generateExpression(expr.source, Precedence.Assignment, E_TTT),
24582480
')'
24592481
], Precedence.Call, precedence);
2460-
},
2461-
2482+
}
24622483
};
24632484

24642485
merge(CodeGenerator.prototype, CodeGenerator.Expression);

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"url": "http://github.com/estools/escodegen.git"
3131
},
3232
"dependencies": {
33-
"estraverse": "^4.2.0",
33+
"estraverse": "^5.2.0",
3434
"esutils": "^2.0.2",
3535
"esprima": "^4.0.1",
3636
"optionator": "^0.8.1"
@@ -39,10 +39,11 @@
3939
"source-map": "~0.6.1"
4040
},
4141
"devDependencies": {
42-
"acorn": "^7.1.0",
42+
"acorn": "^7.3.1",
4343
"bluebird": "^3.4.7",
4444
"bower-registry-client": "^1.0.0",
45-
"chai": "^3.5.0",
45+
"chai": "^4.2.0",
46+
"chai-exclude": "^2.0.2",
4647
"commonjs-everywhere": "^0.9.7",
4748
"gulp": "^3.8.10",
4849
"gulp-eslint": "^3.0.1",

test/compare-acorn-es2020.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
Copyright (C) 2012-2013 Yusuke Suzuki <utatane.tea@gmail.com>
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
7+
* Redistributions of source code must retain the above copyright
8+
notice, this list of conditions and the following disclaimer.
9+
* Redistributions in binary form must reproduce the above copyright
10+
notice, this list of conditions and the following disclaimer in the
11+
documentation and/or other materials provided with the distribution.
12+
13+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16+
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
17+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22+
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23+
*/
24+
25+
'use strict';
26+
27+
var fs = require('fs'),
28+
acorn = require('acorn'),
29+
escodegen = require('./loader'),
30+
chai = require('chai'),
31+
chaiExclude = require('chai-exclude'),
32+
expect = chai.expect;
33+
34+
chai.use(chaiExclude);
35+
36+
function test(code, expected) {
37+
var tree, actual, actualTree, options;
38+
39+
options = {
40+
ranges: false,
41+
locations: false,
42+
ecmaVersion: 11
43+
};
44+
45+
tree = acorn.parse(code, options);
46+
47+
// for UNIX text comment
48+
actual = escodegen.generate(tree);
49+
actualTree = acorn.parse(actual, options);
50+
51+
expect(actual).to.be.equal(expected);
52+
expect(tree).excludingEvery(['start', 'end']).to.deep.equal(actualTree);
53+
}
54+
55+
function testMin(code, expected) {
56+
var tree, actual, actualTree, options;
57+
58+
options = {
59+
ranges: false,
60+
locations: false,
61+
ecmaVersion: 11
62+
};
63+
64+
tree = acorn.parse(code, options);
65+
66+
// for UNIX text comment
67+
actual = escodegen.generate(tree, {
68+
format: escodegen.FORMAT_MINIFY,
69+
raw: false
70+
}).replace(/[\n\r]$/, '') + '\n';
71+
actualTree = acorn.parse(actual, options);
72+
73+
expect(actual).to.be.equal(expected);
74+
expect(tree).excludingEvery(['start', 'end']).to.deep.equal(actualTree);
75+
}
76+
77+
describe('compare acorn es2020 test', function () {
78+
fs.readdirSync(__dirname + '/compare-acorn-es2020').sort().forEach(function(file) {
79+
var code, expected, exp, min;
80+
if (/\.js$/.test(file) && !/expected\.js$/.test(file) && !/expected\.min\.js$/.test(file)) {
81+
it(file, function () {
82+
exp = file.replace(/\.js$/, '.expected.js');
83+
min = file.replace(/\.js$/, '.expected.min.js');
84+
code = fs.readFileSync(__dirname + '/compare-acorn-es2020/' + file, 'utf-8');
85+
expected = fs.readFileSync(__dirname + '/compare-acorn-es2020/' + exp, 'utf-8');
86+
test(code, expected);
87+
if (fs.existsSync(__dirname + '/compare-acorn-es2020/' + min)) {
88+
expected = fs.readFileSync(__dirname + '/compare-acorn-es2020/' + min, 'utf-8');
89+
testMin(code, expected);
90+
}
91+
});
92+
}
93+
});
94+
});
95+
/* vim: set sw=4 ts=4 et tw=80 : */
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
obj.aaa.bbb;
2+
obj.aaa?.bbb;
3+
obj?.aaa.bbb;
4+
obj?.aaa?.bbb;
5+
obj.aaa.bbb;
6+
obj.aaa?.bbb;
7+
(obj?.aaa).bbb;
8+
(obj?.aaa)?.bbb;
9+
(obj?.aaa).bbb.ccc.ddd;
10+
((obj?.aaa).bbb?.ccc).ddd;
11+
(obj?.aaa)?.bbb;
12+
obj[aaa][bbb];
13+
obj[aaa]?.[bbb];
14+
obj?.[aaa][bbb];
15+
obj?.[aaa]?.[bbb];
16+
obj[aaa][bbb];
17+
obj[aaa]?.[bbb];
18+
(obj?.[aaa])[bbb];
19+
(obj?.[aaa])?.[bbb];
20+
obj[aaa][bbb][ccc][ddd];
21+
((obj?.[aaa])[bbb]?.[ccc])[ddd];
22+
1?.a;
23+
obj()();
24+
obj()?.();
25+
obj?.()();
26+
obj?.()?.();
27+
obj()();
28+
obj()?.();
29+
(obj?.())();
30+
(obj?.())?.();
31+
obj()()()();
32+
((obj?.())()?.())();
33+
(a?.b)();
34+
a?.b();
35+
a?.b?.();
36+
(a?.b)?.();
37+
a?.().b;
38+
(a?.()).b;
39+
a?.b.c();
40+
(a?.b.c)();
41+
a.b?.().c;
42+
(a.b?.()).c;
43+
(a.b?.())?.c;
44+
new (a?.b().c)();
45+
new (a?.b())();
46+
new (a?.b().c)();
47+
new (a?.b())();
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
obj.aaa.bbb;obj.aaa?.bbb;obj?.aaa.bbb;obj?.aaa?.bbb;obj.aaa.bbb;obj.aaa?.bbb;(obj?.aaa).bbb;(obj?.aaa)?.bbb;(obj?.aaa).bbb.ccc.ddd;((obj?.aaa).bbb?.ccc).ddd;(obj?.aaa)?.bbb;obj[aaa][bbb];obj[aaa]?.[bbb];obj?.[aaa][bbb];obj?.[aaa]?.[bbb];obj[aaa][bbb];obj[aaa]?.[bbb];(obj?.[aaa])[bbb];(obj?.[aaa])?.[bbb];obj[aaa][bbb][ccc][ddd];((obj?.[aaa])[bbb]?.[ccc])[ddd];1?.a;obj()();obj()?.();obj?.()();obj?.()?.();obj()();obj()?.();(obj?.())();(obj?.())?.();obj()()()();((obj?.())()?.())();(a?.b)();a?.b();a?.b?.();(a?.b)?.();a?.().b;(a?.()).b;a?.b.c();(a?.b.c)();a.b?.().c;(a.b?.()).c;(a.b?.())?.c;new(a?.b().c);new(a?.b());new(a?.b().c);new(a?.b())
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
obj.aaa.bbb;
2+
obj.aaa?.bbb;
3+
obj?.aaa.bbb;
4+
obj?.aaa?.bbb;
5+
(obj.aaa).bbb;
6+
(obj.aaa)?.bbb;
7+
(obj?.aaa).bbb;
8+
(obj?.aaa)?.bbb;
9+
((obj?.aaa).bbb.ccc).ddd;
10+
((obj?.aaa).bbb?.ccc).ddd;
11+
(obj?.aaa)?.bbb;
12+
obj[aaa][bbb];
13+
obj[aaa]?.[bbb];
14+
obj?.[aaa][bbb];
15+
obj?.[aaa]?.[bbb];
16+
(obj[aaa])[bbb];
17+
(obj[aaa])?.[bbb];
18+
(obj?.[aaa])[bbb];
19+
(obj?.[aaa])?.[bbb];
20+
((obj[aaa])[bbb][ccc])[ddd];
21+
((obj?.[aaa])[bbb]?.[ccc])[ddd];
22+
1?.a;
23+
obj()();
24+
obj()?.();
25+
obj?.()();
26+
obj?.()?.();
27+
(obj())();
28+
(obj())?.();
29+
(obj?.())();
30+
(obj?.())?.();
31+
((obj())()())();
32+
((obj?.())()?.())();
33+
(a?.b)();
34+
a?.b();
35+
a?.b?.();
36+
(a?.b)?.();
37+
a?.().b;
38+
(a?.()).b;
39+
a?.b.c();
40+
(a?.b.c)();
41+
a.b?.().c;
42+
(a.b?.()).c;
43+
(a.b?.())?.c;
44+
new (a?.b().c);
45+
new (a?.b());
46+
new (a?.b().c)();
47+
new (a?.b())();

0 commit comments

Comments
 (0)