11import { toSimpleType } from "ts-simple-type" ;
2- import { BinaryExpression , ExpressionStatement , Node , ReturnStatement } from "typescript" ;
3- import { AnalyzerDeclarationVisitContext , ComponentMember , getJsDoc , } from "web-component-analyzer" ;
4- import { getMemberVisibilityFromNode , getModifiersFromNode , hasModifier , isNamePrivate } from "./ast-util" ;
2+ import {
3+ BinaryExpression ,
4+ ExpressionStatement ,
5+ Node ,
6+ ReturnStatement ,
7+ } from "typescript" ;
8+ import {
9+ AnalyzerDeclarationVisitContext ,
10+ ComponentMember ,
11+ getJsDoc ,
12+ } from "web-component-analyzer" ;
13+ import {
14+ getMemberVisibilityFromNode ,
15+ getModifiersFromNode ,
16+ hasModifier ,
17+ isNamePrivate ,
18+ } from "./ast-util" ;
519import { lazy } from "./lazy" ;
620import { resolveNodeValue } from "./resolve-node-value" ;
721import { relaxType } from "./type-util" ;
@@ -11,143 +25,186 @@ import { relaxType } from "./type-util";
1125 * @param node
1226 * @param context
1327 */
14- export function discoverMembers ( node : Node , context : AnalyzerDeclarationVisitContext ) : ComponentMember [ ] | undefined {
15- const { ts, checker } = context ;
16-
17- // Never pick up members not declared directly on the declaration node being traversed
18- if ( node . parent !== context . declarationNode ) {
19- return undefined ;
20- }
21-
22- const jsDoc = getJsDoc ( node , ts ) ;
23- const found = jsDoc ?. tags ?. find ( ( tag ) => tag . tag === "nsProperty" ) ;
24- if ( ! found ) return undefined ;
25-
26- // static get observedAttributes() { return ['c', 'l']; }
27- if ( ts . isGetAccessor ( node ) && hasModifier ( node , ts . SyntaxKind . StaticKeyword , ts ) ) {
28- if ( node . name . getText ( ) === "observedAttributes" && node . body != null ) {
29- const members : ComponentMember [ ] = [ ] ;
30-
31- // Find either the first "return" statement or the first "array literal expression"
32- const arrayLiteralExpression =
33- ( node . body . statements . find ( statement => ts . isReturnStatement ( statement ) ) as ReturnStatement | undefined ) ?. expression ??
34- node . body . statements . find ( statement => ts . isArrayLiteralExpression ( statement ) ) ;
35-
36- if ( arrayLiteralExpression != null && ts . isArrayLiteralExpression ( arrayLiteralExpression ) ) {
37- // Emit an attribute for each string literal in the array.
38- for ( const attrNameNode of arrayLiteralExpression . elements ) {
39- const attrName = ts . isStringLiteralLike ( attrNameNode ) ? attrNameNode . text : undefined ;
40- if ( attrName == null ) continue ;
41-
42- members . push ( {
43- priority : "medium" ,
44- node : attrNameNode ,
45- jsDoc : getJsDoc ( attrNameNode , ts ) ,
46- kind : "attribute" ,
47- attrName,
48- type : undefined // () => ({ kind: "ANY" } as SimpleType),
49- } ) ;
50- }
51- }
52-
53- return members ;
54- }
55- }
56-
57- // class { myProp = "hello"; }
58- else if ( ts . isPropertyDeclaration ( node ) || ts . isPropertySignature ( node ) ) {
59- const { name, initializer } = ( ( ) => {
60- if ( ts . isPropertySignature ( node ) ) {
61- return { name : node . name , initializer : undefined } ;
62- }
63- return node ;
64- } ) ( ) ;
65-
66- if ( ts . isIdentifier ( name ) || ts . isStringLiteralLike ( name ) ) {
67- // Always ignore the "prototype" property
68- if ( name . text === "prototype" ) {
69- return undefined ;
70- }
71-
72- // Find default value based on initializer
73- const resolvedDefaultValue = initializer != null ? resolveNodeValue ( initializer , context ) : undefined ;
74- const def = resolvedDefaultValue != null ? resolvedDefaultValue . value : initializer ?. getText ( ) ;
75-
76- return [
77- {
78- priority : "high" ,
79- node,
80- kind : "property" ,
81- jsDoc : getJsDoc ( node , ts ) ,
82- propName : name . text ,
83- type : lazy ( ( ) => checker . getTypeAtLocation ( node ) ) ,
84- default : def ,
85- visibility : getMemberVisibilityFromNode ( node , ts ) ,
86- modifiers : getModifiersFromNode ( node , ts )
87- //required: isPropertyRequired(node, context.checker),
88- }
89- ] ;
90- }
91- }
92-
93- // class { set myProp(value: string) { ... } }
94- else if ( ts . isSetAccessor ( node ) || ts . isGetAccessor ( node ) ) {
95- const { name, parameters } = node ;
96-
97- if ( ts . isIdentifier ( name ) ) {
98- const parameter = ts . isSetAccessor ( node ) != null && parameters ?. length > 0 ? parameters [ 0 ] : undefined ;
99-
100- return [
101- {
102- priority : "high" ,
103- node,
104- jsDoc : getJsDoc ( node , ts ) ,
105- kind : "property" ,
106- propName : name . text ,
107- type : lazy ( ( ) => ( parameter == null ? context . checker . getTypeAtLocation ( node ) : context . checker . getTypeAtLocation ( parameter ) ) ) ,
108- visibility : getMemberVisibilityFromNode ( node , ts ) ,
109- modifiers : getModifiersFromNode ( node , ts )
110- }
111- ] ;
112- }
113- }
114-
115- // constructor { super(); this.title = "Hello"; }
116- else if ( ts . isConstructorDeclaration ( node ) ) {
117- if ( node . body != null ) {
118- const assignments = node . body . statements
119- . filter ( ( stmt ) : stmt is ExpressionStatement => ts . isExpressionStatement ( stmt ) )
120- . map ( stmt => stmt . expression )
121- . filter ( ( exp ) : exp is BinaryExpression => ts . isBinaryExpression ( exp ) ) ;
122-
123- const members : ComponentMember [ ] = [ ] ;
124- for ( const assignment of assignments ) {
125- const { left, right } = assignment ;
126-
127- if ( ts . isPropertyAccessExpression ( left ) ) {
128- if ( left . expression . kind === ts . SyntaxKind . ThisKeyword ) {
129- const propName = left . name . getText ( ) ;
130-
131- const resolvedInitializer = resolveNodeValue ( right , context ) ;
132- const def = resolvedInitializer != null ? resolvedInitializer . value : undefined ; //right.getText();
133-
134- members . push ( {
135- priority : "low" ,
136- node,
137- kind : "property" ,
138- propName,
139- default : def ,
140- type : ( ) => relaxType ( toSimpleType ( checker . getTypeAtLocation ( right ) , checker ) ) ,
141- jsDoc : getJsDoc ( assignment . parent , ts ) ,
142- visibility : isNamePrivate ( propName ) ? "private" : undefined
143- } ) ;
144- }
145- }
146- }
147-
148- return members ;
149- }
150- }
151-
152- return undefined ;
153- }
28+ export function discoverMembers (
29+ node : Node ,
30+ context : AnalyzerDeclarationVisitContext
31+ ) : ComponentMember [ ] | undefined {
32+ const { ts, checker } = context ;
33+
34+ // Never pick up members not declared directly on the declaration node being traversed
35+ if ( node . parent !== context . declarationNode ) {
36+ return undefined ;
37+ }
38+
39+ const jsDoc = getJsDoc ( node , ts ) ;
40+ const found = jsDoc ?. tags ?. find ( ( tag ) => tag . tag === "nsProperty" ) ;
41+
42+ if (
43+ ! found &&
44+ ( ! globalThis . LEGACY_MODE || node . parent . getText ( ) . includes ( "@nsView" ) )
45+ )
46+ return undefined ;
47+
48+ // static get observedAttributes() { return ['c', 'l']; }
49+ if (
50+ ts . isGetAccessor ( node ) &&
51+ hasModifier ( node , ts . SyntaxKind . StaticKeyword , ts )
52+ ) {
53+ if ( node . name . getText ( ) === "observedAttributes" && node . body != null ) {
54+ const members : ComponentMember [ ] = [ ] ;
55+
56+ // Find either the first "return" statement or the first "array literal expression"
57+ const arrayLiteralExpression =
58+ (
59+ node . body . statements . find ( ( statement ) =>
60+ ts . isReturnStatement ( statement )
61+ ) as ReturnStatement | undefined
62+ ) ?. expression ??
63+ node . body . statements . find ( ( statement ) =>
64+ ts . isArrayLiteralExpression ( statement )
65+ ) ;
66+
67+ if (
68+ arrayLiteralExpression != null &&
69+ ts . isArrayLiteralExpression ( arrayLiteralExpression )
70+ ) {
71+ // Emit an attribute for each string literal in the array.
72+ for ( const attrNameNode of arrayLiteralExpression . elements ) {
73+ const attrName = ts . isStringLiteralLike ( attrNameNode )
74+ ? attrNameNode . text
75+ : undefined ;
76+ if ( attrName == null ) continue ;
77+
78+ members . push ( {
79+ priority : "medium" ,
80+ node : attrNameNode ,
81+ jsDoc : getJsDoc ( attrNameNode , ts ) ,
82+ kind : "attribute" ,
83+ attrName,
84+ type : undefined , // () => ({ kind: "ANY" } as SimpleType),
85+ } ) ;
86+ }
87+ }
88+
89+ return members ;
90+ }
91+ }
92+
93+ // class { myProp = "hello"; }
94+ else if ( ts . isPropertyDeclaration ( node ) || ts . isPropertySignature ( node ) ) {
95+ const { name, initializer } = ( ( ) => {
96+ if ( ts . isPropertySignature ( node ) ) {
97+ return { name : node . name , initializer : undefined } ;
98+ }
99+ return node ;
100+ } ) ( ) ;
101+
102+ if ( ts . isIdentifier ( name ) || ts . isStringLiteralLike ( name ) ) {
103+ // Always ignore the "prototype" property
104+ if ( name . text === "prototype" ) {
105+ return undefined ;
106+ }
107+
108+ // Find default value based on initializer
109+ const resolvedDefaultValue =
110+ initializer != null
111+ ? resolveNodeValue ( initializer , context )
112+ : undefined ;
113+ const def =
114+ resolvedDefaultValue != null
115+ ? resolvedDefaultValue . value
116+ : initializer ?. getText ( ) ;
117+
118+ return [
119+ {
120+ priority : "high" ,
121+ node,
122+ kind : "property" ,
123+ jsDoc : getJsDoc ( node , ts ) ,
124+ propName : name . text ,
125+ type : lazy ( ( ) => checker . getTypeAtLocation ( node ) ) ,
126+ default : def ,
127+ visibility : getMemberVisibilityFromNode ( node , ts ) ,
128+ modifiers : getModifiersFromNode ( node , ts ) ,
129+ //required: isPropertyRequired(node, context.checker),
130+ } ,
131+ ] ;
132+ }
133+ }
134+
135+ // class { set myProp(value: string) { ... } }
136+ else if ( ts . isSetAccessor ( node ) || ts . isGetAccessor ( node ) ) {
137+ const { name, parameters } = node ;
138+
139+ if ( ts . isIdentifier ( name ) ) {
140+ const parameter =
141+ ts . isSetAccessor ( node ) != null && parameters ?. length > 0
142+ ? parameters [ 0 ]
143+ : undefined ;
144+
145+ return [
146+ {
147+ priority : "high" ,
148+ node,
149+ jsDoc : getJsDoc ( node , ts ) ,
150+ kind : "property" ,
151+ propName : name . text ,
152+ type : lazy ( ( ) =>
153+ parameter == null
154+ ? context . checker . getTypeAtLocation ( node )
155+ : context . checker . getTypeAtLocation ( parameter )
156+ ) ,
157+ visibility : getMemberVisibilityFromNode ( node , ts ) ,
158+ modifiers : getModifiersFromNode ( node , ts ) ,
159+ } ,
160+ ] ;
161+ }
162+ }
163+
164+ // constructor { super(); this.title = "Hello"; }
165+ else if ( ts . isConstructorDeclaration ( node ) ) {
166+ if ( node . body != null ) {
167+ const assignments = node . body . statements
168+ . filter ( ( stmt ) : stmt is ExpressionStatement =>
169+ ts . isExpressionStatement ( stmt )
170+ )
171+ . map ( ( stmt ) => stmt . expression )
172+ . filter ( ( exp ) : exp is BinaryExpression => ts . isBinaryExpression ( exp ) ) ;
173+
174+ const members : ComponentMember [ ] = [ ] ;
175+ for ( const assignment of assignments ) {
176+ const { left, right } = assignment ;
177+
178+ if ( ts . isPropertyAccessExpression ( left ) ) {
179+ if ( left . expression . kind === ts . SyntaxKind . ThisKeyword ) {
180+ const propName = left . name . getText ( ) ;
181+
182+ const resolvedInitializer = resolveNodeValue ( right , context ) ;
183+ const def =
184+ resolvedInitializer != null
185+ ? resolvedInitializer . value
186+ : undefined ; //right.getText();
187+
188+ members . push ( {
189+ priority : "low" ,
190+ node,
191+ kind : "property" ,
192+ propName,
193+ default : def ,
194+ type : ( ) =>
195+ relaxType (
196+ toSimpleType ( checker . getTypeAtLocation ( right ) , checker )
197+ ) ,
198+ jsDoc : getJsDoc ( assignment . parent , ts ) ,
199+ visibility : isNamePrivate ( propName ) ? "private" : undefined ,
200+ } ) ;
201+ }
202+ }
203+ }
204+
205+ return members ;
206+ }
207+ }
208+
209+ return undefined ;
210+ }
0 commit comments