New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add new.target transform #5906
Merged
Merged
Add new.target transform #5906
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
ebc99f5
Add new.target transform
jridgewell 83757ce
Catch new.target under only an arrow function
jridgewell 54b9c1e
More unsupported reflect.construct cases
jridgewell 208e88f
Fix node 4 test
jridgewell 2e9b75b
Do not transform Methods
jridgewell cbe8ed9
More tests
jridgewell ca4d07c
Properly setup function inheritance test
jridgewell 768a342
Tests tests tests
jridgewell 383079a
Fix ES6 class's new.target
jridgewell 8c6737e
Remove expected output thats supposed to throw.
jridgewell File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
src | ||
test | ||
*.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
# babel-plugin-transform-new-target | ||
|
||
This plugins allows babel to transform `new.target` meta property into a | ||
(correct in most cases) `this.constructor` expression. | ||
|
||
## Example | ||
|
||
```js | ||
function Foo() { | ||
console.log(new.target); | ||
} | ||
|
||
Foo(); // => undefined | ||
new Foo(); // => Foo | ||
``` | ||
|
||
```js | ||
class Foo { | ||
constructor() { | ||
console.log(new.target); | ||
} | ||
} | ||
|
||
class Bar extends Foo { | ||
} | ||
|
||
new Foo(); // => Foo | ||
new Bar(); // => Bar | ||
``` | ||
|
||
### Caveats | ||
|
||
This plugin relies on `this.constructor`, which means `super` must | ||
already have been called when using untransformed classes. | ||
|
||
```js | ||
class Foo {} | ||
|
||
class Bar extends Foo { | ||
constructor() { | ||
// This will be a problem if classes aren't transformed to ES5 | ||
new.target; | ||
super(); | ||
} | ||
} | ||
``` | ||
|
||
Additionally, this plugin cannot transform all `Reflect.construct` cases | ||
when using `newTarget` with ES5 function classes (transformed ES6 classes). | ||
|
||
```js | ||
function Foo() { | ||
console.log(new.target); | ||
} | ||
|
||
// Bar extends Foo in ES5 | ||
function Bar() { | ||
Foo.call(this); | ||
} | ||
Bar.prototype = Object.create(Foo.prototype); | ||
Bar.prototype.constructor = Bar; | ||
|
||
// Baz does not extend Foo | ||
function Baz() {} | ||
|
||
Reflect.construct(Foo, []); // => Foo (correct) | ||
Reflect.construct(Foo, [], Bar); // => Bar (correct) | ||
|
||
Reflect.construct(Bar, []); // => Bar (incorrect, though this is how ES5 | ||
// inheritience is commonly implemented.) | ||
Reflect.construct(Foo, [], Baz); // => undefined (incorrect) | ||
``` | ||
|
||
## Installation | ||
|
||
```sh | ||
npm install --save-dev babel-plugin-transform-new-target | ||
``` | ||
|
||
## Usage | ||
|
||
### Via `.babelrc` (Recommended) | ||
|
||
**.babelrc** | ||
|
||
```json | ||
{ | ||
"plugins": ["transform-new-target"] | ||
} | ||
``` | ||
|
||
### Via CLI | ||
|
||
```sh | ||
babel --plugins transform-new-target script.js | ||
``` | ||
|
||
### Via Node API | ||
|
||
```javascript | ||
require("babel-core").transform("code", { | ||
plugins: ["transform-new-target"] | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"name": "babel-plugin-transform-new-target", | ||
"version": "7.0.0-alpha.12", | ||
"description": "Transforms new.target meta property", | ||
"repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-new-target", | ||
"license": "MIT", | ||
"main": "lib/index.js", | ||
"keywords": [ | ||
"babel-plugin" | ||
], | ||
"dependencies": { | ||
}, | ||
"devDependencies": { | ||
"babel-helper-plugin-test-runner": "7.0.0-alpha.12", | ||
"babel-plugin-transform-class-properties": "7.0.0-alpha.12", | ||
"babel-plugin-transform-es2015-arrow-functions": "7.0.0-alpha.12" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
export default function({ types: t }) { | ||
return { | ||
name: "transform-new-target", | ||
|
||
visitor: { | ||
MetaProperty(path) { | ||
const meta = path.get("meta"); | ||
const property = path.get("property"); | ||
const { scope } = path; | ||
|
||
if ( | ||
meta.isIdentifier({ name: "new" }) && | ||
property.isIdentifier({ name: "target" }) | ||
) { | ||
const func = path.findParent(path => { | ||
if (path.isClass()) return true; | ||
if (path.isFunction() && !path.isArrowFunctionExpression()) { | ||
if (path.isClassMethod({ kind: "constructor" })) { | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
return false; | ||
}); | ||
|
||
if (!func) { | ||
throw path.buildCodeFrameError( | ||
"new.target must be under a (non-arrow) function or a class.", | ||
); | ||
} | ||
|
||
const { node } = func; | ||
if (!node.id) { | ||
if (func.isMethod()) { | ||
path.replaceWith(scope.buildUndefinedNode()); | ||
return; | ||
} | ||
|
||
node.id = scope.generateUidIdentifier("target"); | ||
} | ||
|
||
const constructor = t.memberExpression( | ||
t.thisExpression(), | ||
t.identifier("constructor"), | ||
); | ||
|
||
if (func.isClass()) { | ||
path.replaceWith(constructor); | ||
return; | ||
} | ||
|
||
path.replaceWith( | ||
t.conditionalExpression( | ||
t.binaryExpression("instanceof", t.thisExpression(), node.id), | ||
constructor, | ||
scope.buildUndefinedNode(), | ||
), | ||
); | ||
} | ||
}, | ||
}, | ||
}; | ||
} |
5 changes: 5 additions & 0 deletions
5
packages/babel-plugin-transform-new-target/test/fixtures/errors/new-target-arrow/actual.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
"use strict"; | ||
|
||
const a = () => { | ||
new.target; | ||
}; |
3 changes: 3 additions & 0 deletions
3
...ages/babel-plugin-transform-new-target/test/fixtures/errors/new-target-arrow/options.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"throws": "new.target must be under a (non-arrow) function or a class." | ||
} |
22 changes: 22 additions & 0 deletions
22
packages/babel-plugin-transform-new-target/test/fixtures/exec/class-extended.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
"use strict"; | ||
|
||
const targets = []; | ||
class Foo { | ||
constructor() { | ||
targets.push(new.target); | ||
} | ||
} | ||
|
||
class Bar extends Foo { | ||
constructor() { | ||
super(); | ||
targets.push(new.target); | ||
} | ||
} | ||
|
||
new Foo; | ||
new Bar; | ||
|
||
assert.equal(targets[0], Foo); | ||
assert.equal(targets[1], Bar); | ||
assert.equal(targets[2], Bar); |
12 changes: 12 additions & 0 deletions
12
packages/babel-plugin-transform-new-target/test/fixtures/exec/class.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
"use strict"; | ||
|
||
const targets = []; | ||
class Foo { | ||
constructor() { | ||
targets.push(new.target); | ||
} | ||
} | ||
|
||
new Foo; | ||
|
||
assert.equal(targets[0], Foo); |
16 changes: 16 additions & 0 deletions
16
packages/babel-plugin-transform-new-target/test/fixtures/exec/function-class-extended.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
"use strict"; | ||
|
||
const targets = []; | ||
function Foo() { | ||
targets.push(new.target); | ||
} | ||
|
||
function Bar() { | ||
Foo.call(this); | ||
} | ||
|
||
new Foo; | ||
new Bar(); | ||
|
||
assert.equal(targets[0], Foo); | ||
assert.equal(targets[1], undefined); |
10 changes: 10 additions & 0 deletions
10
packages/babel-plugin-transform-new-target/test/fixtures/exec/function-class.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
"use strict"; | ||
|
||
const targets = []; | ||
function Foo() { | ||
targets.push(new.target); | ||
} | ||
|
||
new Foo; | ||
|
||
assert.equal(targets[0], Foo); |
12 changes: 12 additions & 0 deletions
12
packages/babel-plugin-transform-new-target/test/fixtures/exec/function.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
"use strict"; | ||
|
||
const targets = []; | ||
function foo() { | ||
targets.push(new.target); | ||
} | ||
|
||
foo(); | ||
foo.call({}); | ||
|
||
assert.equal(targets[0], undefined); | ||
assert.equal(targets[1], undefined); |
27 changes: 27 additions & 0 deletions
27
packages/babel-plugin-transform-new-target/test/fixtures/exec/reflect-class/exec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
const targets = []; | ||
class Foo { | ||
constructor() { | ||
targets.push(new.target); | ||
} | ||
} | ||
|
||
class Bar extends Foo { | ||
} | ||
class Baz { | ||
} | ||
|
||
Reflect.construct(Foo, []); | ||
Reflect.construct(Foo, [], Bar); | ||
Reflect.construct(Bar, []); | ||
Reflect.construct(Bar, [], Baz); | ||
Reflect.construct(Foo, [], Baz); | ||
|
||
assert.equal(targets[0], Foo); | ||
|
||
assert.equal(targets[1], Bar); | ||
|
||
assert.equal(targets[2], Bar); | ||
|
||
assert.equal(targets[3], Baz); | ||
|
||
assert.equal(targets[4], Baz); |
3 changes: 3 additions & 0 deletions
3
packages/babel-plugin-transform-new-target/test/fixtures/exec/reflect-class/options.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"minNodeVersion": "6.0.0" | ||
} |
40 changes: 40 additions & 0 deletions
40
packages/babel-plugin-transform-new-target/test/fixtures/exec/reflect-function/exec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
const targets = []; | ||
function Foo() { | ||
targets.push(new.target); | ||
} | ||
|
||
function Bar() { | ||
Foo.call(this); | ||
} | ||
Bar.prototype = Object.create(Foo.prototype, { | ||
constructor: { | ||
value: Bar, | ||
writable: true, | ||
configurable: true, | ||
} | ||
}); | ||
|
||
function Baz() {} | ||
|
||
Reflect.construct(Foo, []); | ||
Reflect.construct(Foo, [], Bar); | ||
Reflect.construct(Bar, []); | ||
Reflect.construct(Bar, [], Baz); | ||
Reflect.construct(Foo, [], Baz); | ||
|
||
assert.equal(targets[0], Foo); | ||
|
||
assert.equal(targets[1], Bar); | ||
|
||
assert.throws(() => { | ||
// Wish we could support this... | ||
// Then again, this is what a transformed class does. | ||
assert.equal(targets[2], undefined); | ||
}); | ||
|
||
assert.equal(targets[3], undefined); | ||
|
||
assert.throws(() => { | ||
// Wish we could support this... | ||
assert.equal(targets[4], Baz); | ||
}); |
3 changes: 3 additions & 0 deletions
3
packages/babel-plugin-transform-new-target/test/fixtures/exec/reflect-function/options.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"minNodeVersion": "6.0.0" | ||
} |
13 changes: 13 additions & 0 deletions
13
packages/babel-plugin-transform-new-target/test/fixtures/general/arrow/actual.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
function Foo() { | ||
const a = () => { | ||
new.target; | ||
}; | ||
} | ||
|
||
class Bar { | ||
constructor() { | ||
const a = () => { | ||
new.target; | ||
}; | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
packages/babel-plugin-transform-new-target/test/fixtures/general/arrow/expected.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
function Foo() { | ||
var _newtarget = this instanceof Foo ? this.constructor : void 0; | ||
|
||
const a = function () { | ||
_newtarget; | ||
}; | ||
} | ||
|
||
class Bar { | ||
constructor() { | ||
var _newtarget2 = this.constructor; | ||
|
||
const a = function () { | ||
_newtarget2; | ||
}; | ||
} | ||
|
||
} |
3 changes: 3 additions & 0 deletions
3
packages/babel-plugin-transform-new-target/test/fixtures/general/arrow/options.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"plugins": ["transform-new-target", "transform-es2015-arrow-functions"] | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why this variable should have a
2
suffix?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's how babel
generateUidIdentifier
works, it's unique globally.