Skip to content
This repository has been archived by the owner on May 19, 2018. It is now read-only.

Fix several issues with class properties (#157, #116) #158

Closed
wants to merge 10 commits into from
29 changes: 21 additions & 8 deletions src/parser/statement.js
Expand Up @@ -618,8 +618,12 @@ pp.parseClass = function (node, isStatement, optionalId) {
return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
};

pp.isClassProperty = function () {
return this.match(tt.eq) || this.isLineTerminator();
pp.isInitializedClassProperty = function () {
return this.match(tt.eq);
};

pp.isUninitializedClassProperty = function () {
return this.match(tt.semi) || (this.canInsertSemicolon() && !this.match(tt.parenL));
};

pp.isClassMutatorStarter = function () {
Expand Down Expand Up @@ -666,17 +670,21 @@ pp.parseClassBody = function (node) {

this.parsePropertyName(method);

method.static = isMaybeStatic && !this.match(tt.parenL);
let isMaybeGetSet = !method.computed && (method.key.name === "get" || method.key.name === "set");

method.static = isMaybeStatic && !this.match(tt.parenL) && !(this.match(tt.braceR) && this.hasPlugin("classProperties"));
if (method.static) {
if (isGenerator) this.unexpected();
isGenerator = this.eat(tt.star);
this.parsePropertyName(method);
}

if (!isGenerator) {
if (this.isClassProperty()) {
classBody.body.push(this.parseClassProperty(method));
continue;
if (this.isInitializedClassProperty() || (this.isUninitializedClassProperty() && !(isMaybeGetSet && this.match(tt.name)))) {
if (this.hasPlugin("classProperties") || this.hasPlugin("flow")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not involved in the project, so take this with a grain of salt, but nested ifs like this look strange to me - why not combine the two conditions?

classBody.body.push(this.parseClassProperty(method));
continue;
}
}

if (method.key.type === "Identifier" && !method.computed && this.hasPlugin("classConstructorCall") && method.key.name === "call" && this.match(tt.name) && this.state.value === "constructor") {
Expand All @@ -692,14 +700,17 @@ pp.parseClassBody = function (node) {
this.parsePropertyName(method);
}

// Do this again because we may have updated `method`
isMaybeGetSet = !method.computed && (method.key.name === "get" || method.key.name === "set");

method.kind = "method";

if (!method.computed) {
let { key } = method;

// handle get/set methods
// eg. class Foo { get bar() {} set bar() {} }
if (!isAsync && !isGenerator && !this.isClassMutatorStarter() && key.type === "Identifier" && !this.match(tt.parenL) && (key.name === "get" || key.name === "set")) {
if (!isAsync && !isGenerator && !this.isClassMutatorStarter() && key.type === "Identifier" && !this.match(tt.parenL) && isMaybeGetSet) {
isGetSet = true;
method.kind = key.name;
key = this.parsePropertyName(method);
Expand Down Expand Up @@ -775,7 +786,9 @@ pp.parseClassProperty = function (node) {
} else {
node.value = null;
}
this.semicolon();
if (!this.isLineTerminator()) {
this.semicolon();
}
return this.finishNode(node, "ClassProperty");
};

Expand Down
11 changes: 9 additions & 2 deletions src/plugins/flow.js
Expand Up @@ -979,8 +979,15 @@ export default function (instance) {
};
});

// determine whether or not we're currently in the position where a class property would appear
instance.extend("isClassProperty", function (inner) {
// determine whether or not we're currently in the position where an initialized class property would appear
instance.extend("isInitializedClassProperty", function (inner) {
return function () {
return this.match(tt.colon) || inner.call(this);
};
});

// determine whether or not we're currently in the position where an uninitialized class property would appear
instance.extend("isUninitializedClassProperty", function (inner) {
return function () {
return this.match(tt.colon) || inner.call(this);
};
Expand Down
@@ -0,0 +1 @@
class A { async }
119 changes: 119 additions & 0 deletions test/fixtures/experimental/class-properties/at-end-async/expected.json
@@ -0,0 +1,119 @@
{
"type": "File",
"start": 0,
"end": 17,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 17
}
},
"program": {
"type": "Program",
"start": 0,
"end": 17,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 17
}
},
"sourceType": "script",
"body": [
{
"type": "ClassDeclaration",
"start": 0,
"end": 17,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 17
}
},
"id": {
"type": "Identifier",
"start": 6,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 6
},
"end": {
"line": 1,
"column": 7
},
"identifierName": "A"
},
"name": "A"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start": 8,
"end": 17,
"loc": {
"start": {
"line": 1,
"column": 8
},
"end": {
"line": 1,
"column": 17
}
},
"body": [
{
"type": "ClassProperty",
"start": 10,
"end": 15,
"loc": {
"start": {
"line": 1,
"column": 10
},
"end": {
"line": 1,
"column": 15
}
},
"computed": false,
"key": {
"type": "Identifier",
"start": 10,
"end": 15,
"loc": {
"start": {
"line": 1,
"column": 10
},
"end": {
"line": 1,
"column": 15
},
"identifierName": "async"
},
"name": "async"
},
"static": false,
"value": null
}
]
}
}
],
"directives": []
}
}
@@ -0,0 +1 @@
class A { [Symbol.foo] }
@@ -0,0 +1,152 @@
{
"type": "File",
"start": 0,
"end": 24,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 24
}
},
"program": {
"type": "Program",
"start": 0,
"end": 24,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 24
}
},
"sourceType": "script",
"body": [
{
"type": "ClassDeclaration",
"start": 0,
"end": 24,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 24
}
},
"id": {
"type": "Identifier",
"start": 6,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 6
},
"end": {
"line": 1,
"column": 7
},
"identifierName": "A"
},
"name": "A"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start": 8,
"end": 24,
"loc": {
"start": {
"line": 1,
"column": 8
},
"end": {
"line": 1,
"column": 24
}
},
"body": [
{
"type": "ClassProperty",
"start": 10,
"end": 22,
"loc": {
"start": {
"line": 1,
"column": 10
},
"end": {
"line": 1,
"column": 22
}
},
"computed": true,
"key": {
"type": "MemberExpression",
"start": 11,
"end": 21,
"loc": {
"start": {
"line": 1,
"column": 11
},
"end": {
"line": 1,
"column": 21
}
},
"object": {
"type": "Identifier",
"start": 11,
"end": 17,
"loc": {
"start": {
"line": 1,
"column": 11
},
"end": {
"line": 1,
"column": 17
},
"identifierName": "Symbol"
},
"name": "Symbol"
},
"property": {
"type": "Identifier",
"start": 18,
"end": 21,
"loc": {
"start": {
"line": 1,
"column": 18
},
"end": {
"line": 1,
"column": 21
},
"identifierName": "foo"
},
"name": "foo"
},
"computed": false
},
"static": false,
"value": null
}
]
}
}
],
"directives": []
}
}
@@ -0,0 +1 @@
class A { get }