Skip to content

Commit

Permalink
[BugFix] : OptionalChaining Bug fixes (#7288)
Browse files Browse the repository at this point in the history
* Added optionalExpression types to babylon and babel-types

* OptionalChain transforms bug fix

* Added OptionalExpressions to babel-generator. Fixed OptionalChain Bugs

* Removed 'optionalChain' from newExpression and added test cases

* Added test cases for optionalChain

* Update index.js
  • Loading branch information
nveenjain authored and jridgewell committed Feb 8, 2018
1 parent dd0337c commit a3ad518
Show file tree
Hide file tree
Showing 35 changed files with 1,276 additions and 616 deletions.
45 changes: 38 additions & 7 deletions packages/babel-generator/src/generators/expressions.js
Expand Up @@ -95,7 +95,34 @@ export function Decorator(node: Object) {
this.newline();
}

export function CallExpression(node: Object) {
export function OptionalMemberExpression(node: Object) {
this.print(node.object, node);

if (!node.computed && t.isMemberExpression(node.property)) {
throw new TypeError("Got a MemberExpression for MemberExpression property");
}

let computed = node.computed;
if (t.isLiteral(node.property) && typeof node.property.value === "number") {
computed = true;
}
if (node.optional) {
this.token("?.");
}

if (computed) {
this.token("[");
this.print(node.property, node);
this.token("]");
} else {
if (!node.optional) {
this.token(".");
}
this.print(node.property, node);
}
}

export function OptionalCallExpression(node: Object) {
this.print(node.callee, node);

this.print(node.typeParameters, node); // TS
Expand All @@ -108,6 +135,15 @@ export function CallExpression(node: Object) {
this.token(")");
}

export function CallExpression(node: Object) {
this.print(node.callee, node);

this.print(node.typeParameters, node); // TS
this.token("(");
this.printList(node.arguments, node);
this.token(")");
}

export function Import() {
this.word("import");
}
Expand Down Expand Up @@ -203,17 +239,12 @@ export function MemberExpression(node: Object) {
computed = true;
}

if (node.optional) {
this.token("?.");
}
if (computed) {
this.token("[");
this.print(node.property, node);
this.token("]");
} else {
if (!node.optional) {
this.token(".");
}
this.token(".");
this.print(node.property, node);
}
}
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

26 changes: 10 additions & 16 deletions packages/babel-plugin-proposal-optional-chaining/src/index.js
Expand Up @@ -9,15 +9,20 @@ export default function(api, options) {
const optionals = [];

let objectPath = path;
while (objectPath.isMemberExpression() || objectPath.isCallExpression()) {
while (
objectPath.isOptionalMemberExpression() ||
objectPath.isOptionalCallExpression()
) {
const { node } = objectPath;
if (node.optional) {
optionals.push(node);
}

if (objectPath.isMemberExpression()) {
if (objectPath.isOptionalMemberExpression()) {
objectPath.node.type = "MemberExpression";
objectPath = objectPath.get("object");
} else {
objectPath.node.type = "CallExpression";
objectPath = objectPath.get("callee");
}
}
Expand Down Expand Up @@ -101,21 +106,10 @@ export default function(api, options) {
return path.find(path => {
const { parentPath } = path;

if (path.key == "left" && parentPath.isAssignmentExpression()) {
throw path.buildCodeFrameError(
"Illegal optional chain in assignment expression",
);
}
if (path.key == "argument" && parentPath.isUpdateExpression()) {
throw path.buildCodeFrameError(
"Illegal optional chain in update expression",
);
}

if (path.key == "object" && parentPath.isMemberExpression()) {
if (path.key == "object" && parentPath.isOptionalMemberExpression()) {
return false;
}
if (path.key == "callee" && parentPath.isCallExpression()) {
if (path.key == "callee" && parentPath.isOptionalCallExpression()) {
return false;
}
if (
Expand All @@ -133,7 +127,7 @@ export default function(api, options) {
inherits: syntaxOptionalChaining,

visitor: {
"MemberExpression|CallExpression"(path) {
"OptionalCallExpression|OptionalMemberExpression"(path) {
if (!path.node.optional) {
return;
}
Expand Down
@@ -1,3 +1,3 @@
{
"throws": "Illegal optional chain in assignment expression"
"throws": "Invalid left-hand side in assignment expression"
}
@@ -1,3 +1,3 @@
{
"throws": "Illegal optional chain in assignment expression"
"throws": "Invalid left-hand side in assignment expression"
}
@@ -1,3 +1,3 @@
{
"throws": "Illegal optional chain in update expression"
"throws": "Invalid left-hand side in postfix operation"
}
32 changes: 32 additions & 0 deletions packages/babel-types/README.md
Expand Up @@ -1590,6 +1590,38 @@ Aliases: `Flow`, `FlowDeclaration`, `Statement`, `Declaration`

---

### optionalCallExpression
```javascript
t.optionalCallExpression(callee, arguments, optional)
```

See also `t.isOptionalCallExpression(node, opts)` and `t.assertOptionalCallExpression(node, opts)`.

Aliases: `Expression`

- `callee`: `Expression` (required)
- `arguments`: `Array<Expression | SpreadElement | JSXNamespacedName>` (required)
- `optional`: `boolean` (required)
- `typeParameters`: `TypeParameterInstantiation | TSTypeParameterInstantiation` (default: `null`)

---

### optionalMemberExpression
```javascript
t.optionalMemberExpression(object, property, computed, optional)
```

See also `t.isOptionalMemberExpression(node, opts)` and `t.assertOptionalMemberExpression(node, opts)`.

Aliases: `Expression`

- `object`: `Expression` (required)
- `property`: `any` (required)
- `computed`: `boolean` (default: `false`)
- `optional`: `boolean` (required)

---

### parenthesizedExpression
```javascript
t.parenthesizedExpression(expression)
Expand Down
12 changes: 12 additions & 0 deletions packages/babel-types/src/asserts/generated/index.js
Expand Up @@ -651,6 +651,18 @@ export function assertBindExpression(node: Object, opts?: Object = {}): void {
export function assertClassProperty(node: Object, opts?: Object = {}): void {
assert("ClassProperty", node, opts);
}
export function assertOptionalMemberExpression(
node: Object,
opts?: Object = {},
): void {
assert("OptionalMemberExpression", node, opts);
}
export function assertOptionalCallExpression(
node: Object,
opts?: Object = {},
): void {
assert("OptionalCallExpression", node, opts);
}
export function assertImport(node: Object, opts?: Object = {}): void {
assert("Import", node, opts);
}
Expand Down
8 changes: 8 additions & 0 deletions packages/babel-types/src/builders/generated/index.js
Expand Up @@ -596,6 +596,14 @@ export function ClassProperty(...args: Array<any>): Object {
return builder("ClassProperty", ...args);
}
export { ClassProperty as classProperty };
export function OptionalMemberExpression(...args: Array<any>): Object {
return builder("OptionalMemberExpression", ...args);
}
export { OptionalMemberExpression as optionalMemberExpression };
export function OptionalCallExpression(...args: Array<any>): Object {
return builder("OptionalCallExpression", ...args);
}
export { OptionalCallExpression as optionalCallExpression };
export function Import(...args: Array<any>): Object {
return builder("Import", ...args);
}
Expand Down
57 changes: 57 additions & 0 deletions packages/babel-types/src/definitions/experimental.js
Expand Up @@ -54,6 +54,63 @@ defineType("ClassProperty", {
},
});

defineType("OptionalMemberExpression", {
builder: ["object", "property", "computed", "optional"],
visitor: ["object", "property"],
aliases: ["Expression"],
fields: {
object: {
validate: assertNodeType("Expression"),
},
property: {
validate: (function() {
const normal = assertNodeType("Identifier");
const computed = assertNodeType("Expression");

return function(node, key, val) {
const validator = node.computed ? computed : normal;
validator(node, key, val);
};
})(),
},
computed: {
default: false,
},
optional: {
validate: assertValueType("boolean"),
},
},
});

defineType("OptionalCallExpression", {
visitor: ["callee", "arguments", "typeParameters"],
builder: ["callee", "arguments", "optional"],
aliases: ["Expression"],
fields: {
callee: {
validate: assertNodeType("Expression"),
},
arguments: {
validate: chain(
assertValueType("array"),
assertEach(
assertNodeType("Expression", "SpreadElement", "JSXNamespacedName"),
),
),
},
optional: {
validate: assertValueType("boolean"),
},
typeParameters: {
validate: assertNodeType(
"TypeParameterInstantiation",
"TSTypeParameterInstantiation",
),
optional: true,
},
},
});

defineType("Import", {
aliases: ["Expression"],
});
Expand Down
9 changes: 9 additions & 0 deletions packages/babel-types/src/validators/generated/index.js
Expand Up @@ -485,6 +485,15 @@ export function isBindExpression(node: Object, opts?: Object): boolean {
export function isClassProperty(node: Object, opts?: Object): boolean {
return is("ClassProperty", node, opts);
}
export function isOptionalMemberExpression(
node: Object,
opts?: Object,
): boolean {
return is("OptionalMemberExpression", node, opts);
}
export function isOptionalCallExpression(node: Object, opts?: Object): boolean {
return is("OptionalCallExpression", node, opts);
}
export function isImport(node: Object, opts?: Object): boolean {
return is("Import", node, opts);
}
Expand Down

0 comments on commit a3ad518

Please sign in to comment.