1
1
/* eslint-disable no-shadow, no-param-reassign */
2
2
import path from 'path' ;
3
3
4
- import { oxcTraverse , isProperty } from '../utils/oxc-traverse.js' ;
5
-
6
4
import { trackDownIdentifierFromScope } from '../utils/track-down-identifier.js' ;
5
+ import {
6
+ expressionOf ,
7
+ isProperty ,
8
+ isSetter ,
9
+ isGetter ,
10
+ isStatic ,
11
+ nameOf ,
12
+ idOf ,
13
+ } from '../utils/ast-normalizations.js' ;
14
+ import { oxcTraverse } from '../utils/oxc-traverse.js' ;
7
15
import { Analyzer } from '../core/Analyzer.js' ;
8
16
17
+ import { isCustomElementsGet } from './find-customelements.js' ;
18
+
9
19
/**
10
20
* @typedef {import('@babel/types').File } File
11
21
* @typedef {import('@babel/types').ClassMethod } ClassMethod
@@ -16,22 +26,27 @@ import { Analyzer } from '../core/Analyzer.js';
16
26
* @typedef {import('../../../types/index.js').FindClassesAnalyzerEntry } FindClassesAnalyzerEntry
17
27
* @typedef {import('../../../types/index.js').FindClassesConfig } FindClassesConfig
18
28
* @typedef {import('../../../types/index.js').AnalyzerAst } AnalyzerAst
29
+ * @typedef {import("@swc/core").Node } SwcNode
19
30
*/
20
31
21
32
/**
22
33
* Finds import specifiers and sources
23
- * @param {File } babelAst
34
+ * @param {File } oxcAst
24
35
* @param {string } fullCurrentFilePath the file being currently processed
25
36
*/
26
- async function findMembersPerAstEntry ( babelAst , fullCurrentFilePath , projectPath ) {
37
+ async function findMembersPerAstEntry ( oxcAst , fullCurrentFilePath , projectPath ) {
27
38
// The transformed entry
28
39
const classesFound = [ ] ;
29
40
/**
30
41
* Detects private/publicness based on underscores. Checks '$' as well
31
42
* @param {string } name
32
- * @returns {'public'|'protected'|'private' }
43
+ * @returns {'public'|'protected'|'private'|'[n/a]' }
33
44
*/
34
45
function computeAccessType ( name ) {
46
+ if ( name === 'constructor' ) {
47
+ return '[n/a]' ;
48
+ }
49
+
35
50
if ( name . startsWith ( '_' ) || name . startsWith ( '$' ) ) {
36
51
// (at least) 2 prefixes
37
52
if ( name . startsWith ( '__' ) || name . startsWith ( '$$' ) ) {
@@ -47,7 +62,7 @@ async function findMembersPerAstEntry(babelAst, fullCurrentFilePath, projectPath
47
62
* @returns
48
63
*/
49
64
function isStaticProperties ( { node } ) {
50
- return node . static && node . kind === 'get' && node . key . name === 'properties' ;
65
+ return isStatic ( node ) && isGetter ( node ) && nameOf ( node . key ) === 'properties' ;
51
66
}
52
67
53
68
// function isBlacklisted({ node }) {
@@ -86,26 +101,47 @@ async function findMembersPerAstEntry(babelAst, fullCurrentFilePath, projectPath
86
101
// }
87
102
88
103
/**
89
- *
104
+ * @param {SwcNode|OxcNode } node
105
+ */
106
+ function isSuperClassAMixin ( superClassNode ) {
107
+ if ( ! superClassNode ) return false ;
108
+
109
+ const isCallExpression = superClassNode ?. type === 'CallExpression' ;
110
+ if ( ! isCallExpression ) return false ;
111
+ return ! isCustomElementsGet ( superClassNode . callee ) ;
112
+ }
113
+
114
+ /**
90
115
* @param {NodePath } astPath
91
116
* @param {{isMixin?:boolean} } opts
92
117
*/
93
118
async function traverseClass ( astPath , { isMixin = false } = { } ) {
94
119
const classRes = { } ;
95
- classRes . name = astPath . node . id && astPath . node . id . name ;
120
+ classRes . name = ( idOf ( astPath . node ) && nameOf ( idOf ( astPath . node ) ) ) || null ;
96
121
classRes . isMixin = Boolean ( isMixin ) ;
122
+
97
123
if ( astPath . node . superClass ) {
98
124
const superClasses = [ ] ;
99
125
100
126
// Add all Identifier names
101
127
let parent = astPath . node . superClass ;
102
- while ( parent . type === 'CallExpression' ) {
103
- superClasses . push ( { name : parent . callee . name , isMixin : true } ) ;
128
+ while ( isSuperClassAMixin ( parent ) ) {
129
+ superClasses . push ( { name : nameOf ( parent . callee ) , isMixin : true } ) ;
104
130
// As long as we are a CallExpression, we will have a parent
105
- [ parent ] = parent . arguments ;
131
+ [ parent ] = parent . arguments . map ( expressionOf ) ;
132
+ }
133
+
134
+ // At the end of the chain, we find type === Identifier or customElements.get directly.
135
+ if ( isCustomElementsGet ( parent . callee ) ) {
136
+ superClasses . push ( {
137
+ name : null ,
138
+ customElementsGetRef : nameOf ( parent . arguments ?. map ( expressionOf ) [ 0 ] ) ,
139
+ isMixin : false ,
140
+ } ) ;
141
+ } else {
142
+ // an identifier like 'MyClass'
143
+ superClasses . push ( { name : nameOf ( expressionOf ( parent ) ) , isMixin : false } ) ;
106
144
}
107
- // At the end of the chain, we find type === Identifier
108
- superClasses . push ( { name : parent . name , isMixin : false } ) ;
109
145
110
146
// For all found superclasses, track down their root location.
111
147
// This will either result in a local, relative astPath in the project,
@@ -160,19 +196,27 @@ async function findMembersPerAstEntry(babelAst, fullCurrentFilePath, projectPath
160
196
}
161
197
162
198
const methodRes = { } ;
163
- const { name } = astPath . node . key ;
199
+ const name = nameOf ( astPath . node . key ) ;
164
200
methodRes . name = name ;
165
201
methodRes . accessType = computeAccessType ( name ) ;
202
+ if (
203
+ [ 'constructor' , 'connectedCallback' , 'disconnectedCallback' , 'adoptedCallback' ] . includes (
204
+ name ,
205
+ )
206
+ ) {
207
+ methodRes . isPartOfPlatformLifeCycle = true ;
208
+ }
166
209
167
- if ( astPath . node . kind === 'set' || astPath . node . kind === 'get' ) {
168
- if ( astPath . node . static ) {
210
+ if ( isSetter ( astPath . node ) || isGetter ( astPath . node ) ) {
211
+ const setOrGet = isSetter ( astPath . node ) ? 'set' : 'get' ;
212
+ if ( isStatic ( astPath . node ) ) {
169
213
methodRes . static = true ;
170
214
}
171
- methodRes . kind = [ ...( methodRes . kind || [ ] ) , astPath . node . kind ] ;
215
+ methodRes . kind = [ ...( methodRes . kind || [ ] ) , setOrGet ] ;
172
216
// Merge getter/setters into one
173
- const found = classRes . members . props . find ( p => p . name === name ) ;
217
+ const found = classRes . members . props . find ( p => nameOf ( p ) === name ) ;
174
218
if ( found ) {
175
- found . kind = [ ...( found . kind || [ ] ) , astPath . node . kind ] ;
219
+ found . kind = [ ...( found . kind || [ ] ) , setOrGet ] ;
176
220
} else {
177
221
classRes . members . props . push ( methodRes ) ;
178
222
}
@@ -184,14 +228,16 @@ async function findMembersPerAstEntry(babelAst, fullCurrentFilePath, projectPath
184
228
astPath . traverse ( {
185
229
ClassMethod : handleMethodDefinitionOrClassMethod ,
186
230
MethodDefinition : handleMethodDefinitionOrClassMethod ,
231
+ // for swc
232
+ Constructor : handleMethodDefinitionOrClassMethod ,
187
233
} ) ;
188
234
189
235
classesFound . push ( classRes ) ;
190
236
}
191
237
192
238
const classesToTraverse = [ ] ;
193
239
194
- oxcTraverse ( babelAst , {
240
+ oxcTraverse ( oxcAst , {
195
241
ClassDeclaration ( astPath ) {
196
242
classesToTraverse . push ( { astPath, isMixin : false } ) ;
197
243
} ,
0 commit comments