1+ import * as t from "@babel/types" ;
2+ import { traverse , NodePath } from "@babel/core" ;
3+ import { trimSafely } from "./text" ;
4+
5+ export function attributeExists ( path : NodePath < t . JSXElement > , name : string ) {
6+ const openingElement = path . get ( "openingElement" ) ;
7+ return openingElement . node . attributes . some ( ( attr ) => {
8+ if ( ! t . isJSXAttribute ( attr ) ) { return false ; }
9+ const result = t . isJSXIdentifier ( attr . name ) && attr . name . name === name ;
10+ return result ;
11+ } ) ;
12+ }
13+
14+ export function findImmediateJsxParent ( path : NodePath < t . Node > ) : NodePath < t . JSXElement | t . JSXFragment > | null {
15+ const parentPath = path . parentPath ;
16+ if ( parentPath ?. isJSXElement ( ) ) {
17+ return parentPath ;
18+ } else if ( parentPath ?. isJSXFragment ( ) ) {
19+ return parentPath ;
20+ } else {
21+ return null ;
22+ }
23+ }
24+
25+ export function hasJsxTextChildren ( jsxElement : NodePath < t . JSXElement | t . JSXFragment > ) : boolean {
26+ return jsxElement . node . children . some ( child => t . isJSXText ( child ) && trimSafely ( child . value ) . length > 0 ) ;
27+ }
28+
29+ export function getJsxParentLine ( path : NodePath < t . JSXElement | t . JSXFragment > , parentLine : NodePath < t . JSXElement | t . JSXFragment > [ ] = [ path ] ) : NodePath < t . JSXElement | t . JSXFragment > [ ] {
30+ const jsxElementParent = findImmediateJsxParent ( path ) ;
31+ if ( ! jsxElementParent ) { return parentLine ; }
32+
33+ return getJsxParentLine ( jsxElementParent , [ ...parentLine , jsxElementParent ] ) ;
34+ }
35+
36+ export function hasDirective ( ast : t . File , directive : string ) : boolean {
37+ let found = false ;
38+ traverse ( ast , {
39+ Program ( path ) {
40+ path . node . directives . forEach ( ( directiveNode ) => {
41+ if ( t . isDirective ( directiveNode ) && directiveNode . value . value === directive ) {
42+ found = true ;
43+ }
44+ } ) ;
45+ } ,
46+ } ) ;
47+ return found ;
48+ }
49+
50+ export function getImportName ( programNode : NodePath < t . Program > , libName : string , localName : string ) : string | null {
51+ let found = false ;
52+
53+ programNode . traverse ( {
54+ ImportDeclaration ( path ) {
55+ const source = path . node . source . value ;
56+ if ( source !== libName ) { return ; }
57+
58+ const specifier = path . node . specifiers . find ( ( specifier ) =>
59+ t . isImportSpecifier ( specifier ) &&
60+ t . isIdentifier ( specifier . local ) &&
61+ specifier . local . name === localName
62+ ) ;
63+
64+ if ( specifier ) {
65+ found = true ;
66+ }
67+ } ,
68+ } ) ;
69+
70+ return found ? localName : null ;
71+ }
72+
73+ export function injectImport ( programNode : NodePath < t . Program > , libName : string , localName : string ) : string {
74+ let libImportName = `_${ localName } ` ;
75+
76+ const helperName = getImportName ( programNode , libName , libImportName ) ;
77+ if ( helperName ) { return helperName ; }
78+
79+ let uniqueId = 0 ;
80+ while ( programNode . scope . hasBinding ( libImportName ) ) {
81+ libImportName = `${ libImportName } ${ ++ uniqueId } ` ;
82+ }
83+
84+ const importStatement = t . importDeclaration (
85+ [ t . importSpecifier ( t . identifier ( libImportName ) , t . identifier ( localName ) ) ] ,
86+ t . stringLiteral ( libName ) ,
87+ ) ;
88+ programNode . node . body . unshift ( importStatement ) ;
89+
90+ return libImportName ;
91+ }
92+
93+ export function findJsxParentForTheAttribute ( path : NodePath < t . JSXAttribute > ) : NodePath < t . JSXElement > | null {
94+ const openingElement = path . parentPath . isJSXOpeningElement ( ) ? path . parentPath : null ;
95+ if ( ! openingElement ) { return null ; }
96+
97+ const jsxElement = openingElement . parentPath . isJSXElement ( ) ? openingElement . parentPath : null ;
98+ return jsxElement ;
99+ }
100+
101+ export function parseMemberExpressionFromJsxMemberExpression (
102+ jsxMemberExpression : t . JSXMemberExpression ,
103+ ) : t . MemberExpression {
104+ if ( t . isJSXIdentifier ( jsxMemberExpression . object ) ) {
105+ return t . memberExpression (
106+ t . identifier ( jsxMemberExpression . object . name ) ,
107+ t . identifier ( jsxMemberExpression . property . name ) ,
108+ ) ;
109+ } else if ( t . isJSXMemberExpression ( jsxMemberExpression . object ) ) {
110+ return t . memberExpression (
111+ parseMemberExpressionFromJsxMemberExpression ( jsxMemberExpression . object ) ,
112+ t . identifier ( jsxMemberExpression . property . name ) ,
113+ ) ;
114+ } else {
115+ throw new Error ( `Unsupported JSXMemberExpression object type.` ) ;
116+ }
117+ }
118+
119+ export function getStringAttributeValue ( path : NodePath < t . JSXElement > , attributeName : string ) : string | null {
120+ const attribute = path . node . openingElement . attributes . find ( ( attr ) => {
121+ return t . isJSXAttribute ( attr ) && t . isJSXIdentifier ( attr . name ) && attr . name . name === attributeName ;
122+ } ) as t . JSXAttribute | undefined ;
123+
124+ if ( ! attribute ) { return null ; }
125+ if ( ! attribute . value ) { return null ; }
126+ if ( ! t . isStringLiteral ( attribute . value ) ) { return null ; }
127+
128+ return attribute . value . value ;
129+ }
130+
131+ export function getJsxElementName ( path : NodePath < t . JSXElement | t . JSXFragment > ) : string {
132+ if ( t . isJSXFragment ( path . node ) ) {
133+ return "Fragment" ;
134+ } else if ( t . isJSXIdentifier ( path . node . openingElement . name ) ) {
135+ return path . node . openingElement . name . name ;
136+ } else if ( t . isJSXMemberExpression ( path . node . openingElement . name ) ) {
137+ return path . node . openingElement . name . property . name ;
138+ } else {
139+ throw new Error ( `Unsupported JSXElement name type: ${ path . node . openingElement . name . type } ` ) ;
140+ }
141+ }
142+
143+ export function getJsxAttributeName ( path : NodePath < t . JSXAttribute > ) : string {
144+ if ( t . isJSXIdentifier ( path . node . name ) ) {
145+ return path . node . name . name ;
146+ } else {
147+ throw new Error ( `Unsupported JSXAttribute name type: ${ path . node . name . type } ` ) ;
148+ }
149+ }
0 commit comments