Skip to content

Commit 05b8390

Browse files
ota-meshimysticatea
authored andcommitted
✨ supports for ES2022 class features (#20)
1 parent 03506f0 commit 05b8390

12 files changed

+536
-188
lines changed

docs/api/ast-utils.md

Lines changed: 114 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,50 @@ Get the proper location of a given function node to report.
9797
^^^^^^^^^^^^^^
9898
- `class A { static set foo(a) {} }`
9999
^^^^^^^^^^^^^^
100+
- `class A { #foo() {} }`
101+
^^^^
102+
- `class A { *#foo() {} }`
103+
^^^^^
104+
- `class A { async #foo() {} }`
105+
^^^^^^^^^^
106+
- `class A { get #foo() {} }`
107+
^^^^^^^^
108+
- `class A { set #foo(a) {} }`
109+
^^^^^^^^
110+
- `class A { static #foo() {} }`
111+
^^^^^^^^^^^
112+
- `class A { static *#foo() {} }`
113+
^^^^^^^^^^^^
114+
- `class A { static async #foo() {} }`
115+
^^^^^^^^^^^^^^^^^
116+
- `class A { static get #foo() {} }`
117+
^^^^^^^^^^^^^^^
118+
- `class A { static set #foo(a) {} }`
119+
^^^^^^^^^^^^^^^
120+
- `class A { foo = function() {} }`
121+
^^^^^^^^^^^^^^
122+
- `class A { foo = function*() {} }`
123+
^^^^^^^^^^^^^^^
124+
- `class A { foo = async function() {} }`
125+
^^^^^^^^^^^^^^^^^^^^
126+
- `class A { static foo = function() {} }`
127+
^^^^^^^^^^^^^^^^^^^^^
128+
- `class A { static foo = function*() {} }`
129+
^^^^^^^^^^^^^^^^^^^^^^
130+
- `class A { static foo = async function() {} }`
131+
^^^^^^^^^^^^^^^^^^^^^^^^^^^
132+
- `class A { #foo = function() {} }`
133+
^^^^^^^^^^^^^^^
134+
- `class A { #foo = function*() {} }`
135+
^^^^^^^^^^^^^^^^
136+
- `class A { #foo = async function() {} }`
137+
^^^^^^^^^^^^^^^^^^^^^
138+
- `class A { static #foo = function() {} }`
139+
^^^^^^^^^^^^^^^^^^^^^^
140+
- `class A { static #foo = function*() {} }`
141+
^^^^^^^^^^^^^^^^^^^^^^^
142+
- `class A { static #foo = async function() {} }`
143+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
100144
```
101145

102146
</details>
@@ -148,58 +192,74 @@ Get the name and kind of a given function node.
148192
<details><summary>Show the name and kind examples:</summary>
149193

150194
```
151-
- `function foo() {}` .................... `function 'foo'`
152-
- `(function foo() {})` .................. `function 'foo'`
153-
- `(function() {})` ...................... `function`
154-
- `function* foo() {}` ................... `generator function 'foo'`
155-
- `(function* foo() {})` ................. `generator function 'foo'`
156-
- `(function*() {})` ..................... `generator function`
157-
- `() => {}` ............................. `arrow function`
158-
- `async () => {}` ....................... `async arrow function`
159-
- `const foo = () => {}` ................. `arrow function 'foo'`
160-
- `const foo = async () => {}` ........... `async arrow function 'foo'`
161-
- `foo = () => {}` ....................... `arrow function 'foo'`
162-
- `foo = async () => {}` ................. `async arrow function 'foo'`
163-
- `const foo = function() {}` ............ `function 'foo'`
164-
- `const foo = function* () {}` .......... `generator function 'foo'`
165-
- `const foo = async function() {}` ...... `async function 'foo'`
166-
- `foo = function() {}` .................. `function 'foo'`
167-
- `foo = function* () {}` ................ `generator function 'foo'`
168-
- `foo = async function() {}` ............ `async function 'foo'`
169-
- `({ foo: function foo() {} })` ......... `method 'foo'`
170-
- `({ foo: function() {} })` ............. `method 'foo'`
171-
- `({ ['foo']: function() {} })` ......... `method 'foo'`
172-
- `({ [foo]: function() {} })` ........... `method`
173-
- `({ foo() {} })` ....................... `method 'foo'`
174-
- `({ foo: function* foo() {} })` ........ `generator method 'foo'`
175-
- `({ foo: function*() {} })` ............ `generator method 'foo'`
176-
- `({ ['foo']: function*() {} })` ........ `generator method 'foo'`
177-
- `({ [foo]: function*() {} })` .......... `generator method`
178-
- `({ *foo() {} })` ...................... `generator method 'foo'`
179-
- `({ foo: async function foo() {} })` ... `async method 'foo'`
180-
- `({ foo: async function() {} })` ....... `async method 'foo'`
181-
- `({ ['foo']: async function() {} })` ... `async method 'foo'`
182-
- `({ [foo]: async function() {} })` ..... `async method`
183-
- `({ async foo() {} })` ................. `async method 'foo'`
184-
- `({ get foo() {} })` ................... `getter 'foo'`
185-
- `({ set foo(a) {} })` .................. `setter 'foo'`
186-
- `class A { constructor() {} }` ......... `constructor`
187-
- `class A { foo() {} }` ................. `method 'foo'`
188-
- `class A { *foo() {} }` ................ `generator method 'foo'`
189-
- `class A { async foo() {} }` ........... `async method 'foo'`
190-
- `class A { ['foo']() {} }` ............. `method 'foo'`
191-
- `class A { *['foo']() {} }` ............ `generator method 'foo'`
192-
- `class A { async ['foo']() {} }` ....... `async method 'foo'`
193-
- `class A { [foo]() {} }` ............... `method`
194-
- `class A { *[foo]() {} }` .............. `generator method`
195-
- `class A { async [foo]() {} }` ......... `async method`
196-
- `class A { get foo() {} }` ............. `getter 'foo'`
197-
- `class A { set foo(a) {} }` ............ `setter 'foo'`
198-
- `class A { static foo() {} }` .......... `static method 'foo'`
199-
- `class A { static *foo() {} }` ......... `static generator method 'foo'`
200-
- `class A { static async foo() {} }` .... `static async method 'foo'`
201-
- `class A { static get foo() {} }` ...... `static getter 'foo'`
202-
- `class A { static set foo(a) {} }` ..... `static setter 'foo'`
195+
- `function foo() {}` ............................... `function 'foo'`
196+
- `(function foo() {})` ............................. `function 'foo'`
197+
- `(function() {})` ................................. `function`
198+
- `function* foo() {}` .............................. `generator function 'foo'`
199+
- `(function* foo() {})` ............................ `generator function 'foo'`
200+
- `(function*() {})` ................................ `generator function`
201+
- `() => {}` ........................................ `arrow function`
202+
- `async () => {}` .................................. `async arrow function`
203+
- `const foo = () => {}` ............................ `arrow function 'foo'`
204+
- `const foo = async () => {}` ...................... `async arrow function 'foo'`
205+
- `foo = () => {}` .................................. `arrow function 'foo'`
206+
- `foo = async () => {}` ............................ `async arrow function 'foo'`
207+
- `const foo = function() {}` ....................... `function 'foo'`
208+
- `const foo = function* () {}` ..................... `generator function 'foo'`
209+
- `const foo = async function() {}` ................. `async function 'foo'`
210+
- `foo = function() {}` ............................. `function 'foo'`
211+
- `foo = function* () {}` ........................... `generator function 'foo'`
212+
- `foo = async function() {}` ....................... `async function 'foo'`
213+
- `({ foo: function foo() {} })` .................... `method 'foo'`
214+
- `({ foo: function() {} })` ........................ `method 'foo'`
215+
- `({ ['foo']: function() {} })` .................... `method 'foo'`
216+
- `({ [foo]: function() {} })` ...................... `method`
217+
- `({ foo() {} })` .................................. `method 'foo'`
218+
- `({ foo: function* foo() {} })` ................... `generator method 'foo'`
219+
- `({ foo: function*() {} })` ....................... `generator method 'foo'`
220+
- `({ ['foo']: function*() {} })` ................... `generator method 'foo'`
221+
- `({ [foo]: function*() {} })` ..................... `generator method`
222+
- `({ *foo() {} })` ................................. `generator method 'foo'`
223+
- `({ foo: async function foo() {} })` .............. `async method 'foo'`
224+
- `({ foo: async function() {} })` .................. `async method 'foo'`
225+
- `({ ['foo']: async function() {} })` .............. `async method 'foo'`
226+
- `({ [foo]: async function() {} })` ................ `async method`
227+
- `({ async foo() {} })` ............................ `async method 'foo'`
228+
- `({ get foo() {} })` .............................. `getter 'foo'`
229+
- `({ set foo(a) {} })` ............................. `setter 'foo'`
230+
- `class A { constructor() {} }` .................... `constructor`
231+
- `class A { foo() {} }` ............................ `method 'foo'`
232+
- `class A { *foo() {} }` ........................... `generator method 'foo'`
233+
- `class A { async foo() {} }` ...................... `async method 'foo'`
234+
- `class A { ['foo']() {} }` ........................ `method 'foo'`
235+
- `class A { *['foo']() {} }` ....................... `generator method 'foo'`
236+
- `class A { async ['foo']() {} }` .................. `async method 'foo'`
237+
- `class A { [foo]() {} }` .......................... `method`
238+
- `class A { *[foo]() {} }` ......................... `generator method`
239+
- `class A { async [foo]() {} }` .................... `async method`
240+
- `class A { get foo() {} }` ........................ `getter 'foo'`
241+
- `class A { set foo(a) {} }` ....................... `setter 'foo'`
242+
- `class A { static foo() {} }` ..................... `static method 'foo'`
243+
- `class A { static *foo() {} }` .................... `static generator method 'foo'`
244+
- `class A { static async foo() {} }` ............... `static async method 'foo'`
245+
- `class A { static get foo() {} }` ................. `static getter 'foo'`
246+
- `class A { static set foo(a) {} }` ................ `static setter 'foo'`
247+
- `class A { #foo() {} }` ........................... `private method '#foo'`
248+
- `class A { *#foo() {} }` .......................... `private generator method '#foo'`
249+
- `class A { async #foo() {} }` ..................... `private async method '#foo'`
250+
- `class A { get #foo() {} }` ....................... `private getter '#foo'`
251+
- `class A { set #foo(a) {} }` ...................... `private setter '#foo'`
252+
- `class A { static #foo() {} }` .................... `private static method '#foo'`
253+
- `class A { static *#foo() {} }` ................... `private static generator method '#foo'`
254+
- `class A { static async #foo() {} }` .............. `private static async method '#foo'`
255+
- `class A { static get #foo() {} }` ................ `private static getter '#foo'`
256+
- `class A { static set #foo(a) {} }` ............... `private static setter '#foo'`
257+
- `class A { #foo = function() {} }` ................ `private method '#foo'"`
258+
- `class A { #foo = function*() {} }` ............... `private generator method '#foo'"`
259+
- `class A { #foo = async function() {} }` .......... `private async method '#foo'"`
260+
- `class A { static #foo = function() {} }` ......... `private static method '#foo'"`
261+
- `class A { static #foo = function*() {} }` ........ `private static generator method '#foo'"`
262+
- `class A { static #foo = async function() {} }` ... `private static async method '#foo'"`
203263
```
204264

205265
</details>
@@ -252,7 +312,7 @@ If the node is a computed property, this tries to compute the property name by t
252312

253313
Name | Type | Description
254314
:-----|:-----|:------------
255-
node | Node | The node to get that name. This shuld be any of `MemberExpression`, `Property`, and `MethodDefinition` node.
315+
node | Node | The node to get that name. This shuld be any of `MemberExpression`, `Property`, `MethodDefinition`, and `PropertyDefinition` node.
256316
initialScope | Scope or undefined | Optional. The scope object to find variables.
257317

258318
### Return value
@@ -385,6 +445,7 @@ The side effect means that it *may* modify a certain variable or object member.
385445
- `MemberExpression` (`[computed = true]`)
386446
- `MethodDefinition` (`[computed = true]`)
387447
- `Property` (`[computed = true]`)
448+
- `PropertyDefinition` (`[computed = true]`)
388449
- `UnaryExpression` (`[operator = "-" | "+" | "!" | "~"]`)
389450

390451
### Parameters

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"dot-prop": "^4.2.0",
2121
"eslint": "^7.24.0",
2222
"esm": "^3.2.25",
23-
"espree": "^7.3.1",
23+
"espree": "git+https://github.com/eslint/espree.git#class-fields",
2424
"mocha": "^6.2.2",
2525
"npm-run-all": "^4.1.5",
2626
"nyc": "^14.1.1",

src/get-function-head-location.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ export function getFunctionHeadLocation(node, sourceCode) {
3030
end = arrowToken.loc.end
3131
} else if (
3232
parent.type === "Property" ||
33-
parent.type === "MethodDefinition"
33+
parent.type === "MethodDefinition" ||
34+
parent.type === "PropertyDefinition"
3435
) {
3536
start = parent.loc.start
3637
end = getOpeningParenOfParams(node, sourceCode).loc.start

src/get-function-name-with-kind.js

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,18 @@ import { getPropertyName } from "./get-property-name"
88
export function getFunctionNameWithKind(node) {
99
const parent = node.parent
1010
const tokens = []
11-
12-
if (parent.type === "MethodDefinition" && parent.static) {
13-
tokens.push("static")
11+
const isFieldDefinition =
12+
parent.type === "MethodDefinition" ||
13+
parent.type === "PropertyDefinition"
14+
let privateName = null
15+
if (isFieldDefinition) {
16+
if (parent.key.type === "PrivateIdentifier") {
17+
privateName = `#${parent.key.name}`
18+
tokens.push("private")
19+
}
20+
if (parent.static) {
21+
tokens.push("static")
22+
}
1423
}
1524
if (node.async) {
1625
tokens.push("async")
@@ -21,10 +30,7 @@ export function getFunctionNameWithKind(node) {
2130

2231
if (node.type === "ArrowFunctionExpression") {
2332
tokens.push("arrow", "function")
24-
} else if (
25-
parent.type === "Property" ||
26-
parent.type === "MethodDefinition"
27-
) {
33+
} else if (parent.type === "Property" || isFieldDefinition) {
2834
if (parent.kind === "constructor") {
2935
return "constructor"
3036
}
@@ -42,7 +48,7 @@ export function getFunctionNameWithKind(node) {
4248
if (node.id) {
4349
tokens.push(`'${node.id.name}'`)
4450
} else {
45-
const name = getPropertyName(parent)
51+
const name = privateName || getPropertyName(parent)
4652

4753
if (name) {
4854
tokens.push(`'${name}'`)

src/get-property-name.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,23 @@ export function getPropertyName(node, initialScope) {
1212
if (node.computed) {
1313
return getStringIfConstant(node.property, initialScope)
1414
}
15+
if (node.property.type === "PrivateIdentifier") {
16+
return null
17+
}
1518
return node.property.name
1619

1720
case "Property":
1821
case "MethodDefinition":
22+
case "PropertyDefinition":
1923
if (node.computed) {
2024
return getStringIfConstant(node.key, initialScope)
2125
}
2226
if (node.key.type === "Literal") {
2327
return String(node.key.value)
2428
}
29+
if (node.key.type === "PrivateIdentifier") {
30+
return null
31+
}
2532
return node.key.name
2633

2734
// no default

src/get-static-value.js

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,9 @@ const operations = Object.freeze({
249249

250250
if (args != null) {
251251
if (calleeNode.type === "MemberExpression") {
252+
if (calleeNode.property.type === "PrivateIdentifier") {
253+
return null
254+
}
252255
const object = getStaticValueR(calleeNode.object, initialScope)
253256
if (object != null) {
254257
if (
@@ -257,9 +260,10 @@ const operations = Object.freeze({
257260
) {
258261
return { value: undefined, optional: true }
259262
}
260-
const property = calleeNode.computed
261-
? getStaticValueR(calleeNode.property, initialScope)
262-
: { value: calleeNode.property.name }
263+
const property = getStaticPropertyNameValue(
264+
calleeNode,
265+
initialScope
266+
)
263267

264268
if (property != null) {
265269
const receiver = object.value
@@ -366,14 +370,15 @@ const operations = Object.freeze({
366370
},
367371

368372
MemberExpression(node, initialScope) {
373+
if (node.property.type === "PrivateIdentifier") {
374+
return null
375+
}
369376
const object = getStaticValueR(node.object, initialScope)
370377
if (object != null) {
371378
if (object.value == null && (object.optional || node.optional)) {
372379
return { value: undefined, optional: true }
373380
}
374-
const property = node.computed
375-
? getStaticValueR(node.property, initialScope)
376-
: { value: node.property.name }
381+
const property = getStaticPropertyNameValue(node, initialScope)
377382

378383
if (property != null && !isGetter(object.value, property.value)) {
379384
return { value: object.value[property.value] }
@@ -412,9 +417,10 @@ const operations = Object.freeze({
412417
if (propertyNode.kind !== "init") {
413418
return null
414419
}
415-
const key = propertyNode.computed
416-
? getStaticValueR(propertyNode.key, initialScope)
417-
: { value: propertyNode.key.name }
420+
const key = getStaticPropertyNameValue(
421+
propertyNode,
422+
initialScope
423+
)
418424
const value = getStaticValueR(propertyNode.value, initialScope)
419425
if (key == null || value == null) {
420426
return null
@@ -522,6 +528,19 @@ function getStaticValueR(node, initialScope) {
522528
return null
523529
}
524530

531+
/**
532+
* Get the static value of property name from a MemberExpression node or a Property node.
533+
* @param {Node} node The node to get.
534+
* @param {Scope} [initialScope] The scope to start finding variable. Optional. If the node is a computed property node and this scope was given, this checks the computed property name by the `getStringIfConstant` function with the scope, and returns the value of it.
535+
* @returns {{value:any}|{value:undefined,optional?:true}|null} The static value of the property name of the node, or `null`.
536+
*/
537+
function getStaticPropertyNameValue(node, initialScope) {
538+
const propertyKey = node.type === "Property" ? node.key : node.property
539+
return node.computed
540+
? getStaticValueR(propertyKey, initialScope)
541+
: { value: propertyKey.name }
542+
}
543+
525544
/**
526545
* Get the value of a given node if it's a static value.
527546
* @param {Node} node The node to get.

src/has-side-effect.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,16 @@ const visitor = Object.freeze(
135135
}
136136
return this.$visitChildren(node, options, visitorKeys)
137137
},
138+
PropertyDefinition(node, options, visitorKeys) {
139+
if (
140+
options.considerImplicitTypeConversion &&
141+
node.computed &&
142+
node.key.type !== "Literal"
143+
) {
144+
return true
145+
}
146+
return this.$visitChildren(node, options, visitorKeys)
147+
},
138148
UnaryExpression(node, options, visitorKeys) {
139149
if (node.operator === "delete") {
140150
return true

0 commit comments

Comments
 (0)