-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
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 class.hasInstance proposal (Stage 1) support #13959
Changes from all commits
23a0380
0e190fa
54d8fbe
e352fe1
e7b7f2b
118f6a1
27cc74e
b49580c
7bf5484
dba8628
8068917
669c512
c7d09ae
b8666a3
5df87fa
2070c6e
61220d3
173cb35
2f9e7f4
a3a5b72
2cfdd98
8adfc8b
c0c2599
d58414d
168fa02
4e73912
966889d
d32b606
e8ca303
9bc5e71
f82f9fd
c32e0c7
8c4ba97
c115075
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
class Range { | ||
equals(range) { | ||
class.hasInstance(range); | ||
class/* 1 */./* 2 */hasInstance/* 3 */(range); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"plugins": ["proposal-class-brand-check","transform-runtime"] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
class Range { | ||
equals(range) { | ||
/* 1 */ | ||
|
||
/* 2 */ | ||
|
||
/* 3 */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The output here is incorrect. It seems to me the comments are registered as |
||
class.hasInstance(range); | ||
class.hasInstance(range); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -229,8 +229,10 @@ export default (superClass: Class<Parser>): Class<Parser> => | |
): T { | ||
const type = isStatement ? "ClassDeclaration" : "ClassExpression"; | ||
|
||
this.next(); | ||
this.takeDecorators(node); | ||
if (this.eat(tt._class)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With this change we will incorrectly allow class class A {} because I suggest we remove |
||
this.takeDecorators(node); | ||
} | ||
|
||
const oldStrict = this.state.strict; | ||
|
||
const placeholder = this.parsePlaceholder("Identifier"); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class Foo { | ||
bar(obj) { | ||
class.hasInstance(obj) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"throws": "This experimental syntax requires enabling the parser plugin: \"classBrandCheck\". (3:14)", | ||
"plugins": [] | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class Foo { | ||
bar() { | ||
class.hasInstance() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"throws": "SyntaxError: 'hasInstance' expression must have exactly one parameter. (3:26)", | ||
"plugins": ["classBrandCheck"] | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class Foo { | ||
bar(foo) { | ||
class.hasInstance(...foo) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"throws": "SyntaxError: 'hasInstance' expression must have exactly one parameter. (3:32)", | ||
"plugins": ["classBrandCheck"] | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class Foo { | ||
bar(foo,bar) { | ||
class.hasInstance(foo,...bar) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"throws": "SyntaxError: 'hasInstance' expression must have exactly one parameter. (3:36)", | ||
"plugins": ["classBrandCheck"] | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class Foo { | ||
bar(foo,bar) { | ||
class.hasInstance(foo,bar) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"throws": "SyntaxError: 'hasInstance' expression must have exactly one parameter. (3:33)", | ||
"plugins": ["classBrandCheck"] | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
temp/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
{ | ||
"name": "@babel/plugin-proposal-class-brand-check", | ||
"version": "7.16.0", | ||
"description": "", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/babel/babel.git", | ||
"directory": "packages/babel-plugin-proposal-class-brand-check" | ||
}, | ||
"license": "MIT", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"main": "./lib/index.js", | ||
"exports": { | ||
".": "./lib/index.js", | ||
"./package.json": "./package.json" | ||
}, | ||
"keywords": [ | ||
"babel-plugin" | ||
], | ||
"dependencies": { | ||
"@babel/helper-plugin-test-runner": "workspace:^", | ||
"@babel/helper-plugin-utils": "workspace:^", | ||
"@babel/parser": "workspace:^", | ||
"@babel/traverse": "workspace:^", | ||
"@babel/types": "workspace:^" | ||
}, | ||
"peerDependencies": { | ||
"@babel/core": "^7.0.0-0" | ||
}, | ||
"conditions": { | ||
"BABEL_8_BREAKING": [ | ||
null, | ||
{ | ||
"exports": null | ||
} | ||
] | ||
}, | ||
"engines": { | ||
"node": ">=13.0.0" | ||
}, | ||
"author": "The Babel Team (https://babel.dev/team)" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { declare } from "@babel/helper-plugin-utils"; | ||
import * as t from "@babel/types"; | ||
export default declare(api => { | ||
api.assertVersion(7); | ||
|
||
return { | ||
name: "proposal-class-brand-check", | ||
manipulateOptions(opts, parserOpts) { | ||
parserOpts.plugins.push("classBrandCheck"); | ||
}, | ||
visitor: { | ||
ClassHasInstanceExpression(path) { | ||
const key = "set"; | ||
const node = path.node; | ||
const instanceItem = node.instance; | ||
if (!instanceItem) { | ||
throw new Error("Expected 1 arguments, but got " + instanceItem); | ||
} | ||
let setID = path.scope.generateUidIdentifier(key); | ||
path.findParent(path => { | ||
if (path.type === "ClassDeclaration") { | ||
// unique map | ||
const uniqueID = path.scope.parent.globals[key]; | ||
if (uniqueID) { | ||
setID = uniqueID; | ||
} else { | ||
const id = t.identifier(setID.name); | ||
const weakSet = t.identifier("WeakSet"); | ||
const expression = t.newExpression(weakSet, []); | ||
const declarator = t.variableDeclarator(id, expression); | ||
path.insertBefore(t.variableDeclaration("var", [declarator])); | ||
path.scope.parent.globals[key] = setID; | ||
|
||
const body = path.node.body; | ||
if (body.type === "ClassBody") { | ||
const bodyList = body.body; | ||
const consFlag = bodyList.some( | ||
node => node.kind === "constructor", | ||
); | ||
if (!consFlag) { | ||
const key = "constructor"; | ||
const body = t.blockStatement([], []); | ||
const constructorMethod = t.classMethod( | ||
key, | ||
t.identifier(key), | ||
[], | ||
body, | ||
false, | ||
false, | ||
false, | ||
false, | ||
); | ||
bodyList.unshift(constructorMethod); | ||
} | ||
|
||
bodyList.forEach(node => { | ||
if (node.kind === "constructor") { | ||
const consBody = node.body; | ||
const expressionList = consBody.body; | ||
|
||
const memberExpression = t.memberExpression( | ||
t.identifier(setID.name), | ||
t.identifier("add"), | ||
false, | ||
); | ||
|
||
const callExpression = t.callExpression(memberExpression, [ | ||
t.thisExpression(), | ||
]); | ||
const addExpression = t.expressionStatement(callExpression); | ||
|
||
const returnFlag = expressionList.findIndex( | ||
expression => expression.type === "ReturnStatement", | ||
); | ||
if (returnFlag === -1) { | ||
expressionList.push(addExpression); | ||
} else if (returnFlag === 0) { | ||
expressionList.unshift(addExpression); | ||
} else { | ||
expressionList.splice(returnFlag, 0, addExpression); | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
}); | ||
const memberExpression = t.memberExpression( | ||
t.identifier(setID.name), | ||
t.identifier("has"), | ||
false, | ||
); | ||
const callExpression = t.callExpression(memberExpression, [ | ||
instanceItem, | ||
]); | ||
path.replaceWith(callExpression); | ||
}, | ||
}, | ||
}; | ||
}); |
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.
We should ensure
@babel/generator
prints the code according to the AST:Please also add a test case to
babel-generator/test/fixtures/class-brand-check
: