11import * as ts from 'typescript' ;
22
3- /*
4- Transforms:
5- (const | let | var) <name> = <styled>`` ->
6- */
7-
83/** Detects that a node represents a styled function
94 * Recognizes the following patterns:
5+ *
106 * styled.tag
117 * Component.extend
128 * styled(Component)
139 * styledFunction.attrs(attributes)
1410*/
1511function isStyledFunction ( node : ts . Node ) : boolean {
16- if ( node . kind === ts . SyntaxKind . PropertyAccessExpression
17- && ( node as ts . PropertyAccessExpression ) . expression . kind === ts . SyntaxKind . Identifier ) {
18- const propertyName = ( node as ts . PropertyAccessExpression ) . name . text ;
19- const objectName = ( ( node as ts . PropertyAccessExpression ) . expression as ts . Identifier ) . text ;
20-
21- return propertyName === 'extend'
22- ? objectName [ 0 ] === objectName [ 0 ] . toUpperCase ( )
23- : objectName === 'styled' ;
12+ if ( node . kind === ts . SyntaxKind . PropertyAccessExpression ) {
13+ if ( isStyledObject ( ( node as ts . PropertyAccessExpression ) . expression ) ) {
14+ return true ;
15+ }
16+
17+ if ( ( node as ts . PropertyAccessExpression ) . name . text === 'extend'
18+ && isValidComponent ( ( node as ts . PropertyAccessExpression ) . expression ) ) {
19+
20+ return true ;
21+ }
22+
23+ return false ;
24+ }
25+
26+ if ( node . kind === ts . SyntaxKind . CallExpression
27+ && ( node as ts . CallExpression ) . arguments . length === 1 ) {
28+
29+ if ( isStyledObject ( ( node as ts . CallExpression ) . expression ) ) {
30+ return true ;
31+ }
32+
33+ if ( isStyledAttrs ( ( node as ts . CallExpression ) . expression ) ) {
34+ return true ;
35+ }
2436 }
2537
2638 return false ;
2739}
2840
41+ function isStyledObject ( node : ts . Node ) {
42+ return node && node . kind === ts . SyntaxKind . Identifier && ( node as ts . Identifier ) . text === 'styled' ;
43+ }
44+
45+ function isValidComponent ( node : ts . Node ) {
46+ return node && node . kind === ts . SyntaxKind . Identifier && isValidComponentName ( ( node as ts . Identifier ) . text ) ;
47+ }
48+
49+ function isValidTagName ( name : string ) {
50+ return name [ 0 ] === name [ 0 ] . toLowerCase ( ) ;
51+ }
52+
53+ function isValidComponentName ( name : string ) {
54+ return name [ 0 ] === name [ 0 ] . toUpperCase ( ) ;
55+ }
56+
57+ function isStyledAttrs ( node : ts . Node ) {
58+ return node && node . kind === ts . SyntaxKind . PropertyAccessExpression
59+ && ( node as ts . PropertyAccessExpression ) . name . text === 'attrs'
60+ && isStyledFunction ( ( node as ts . PropertyAccessExpression ) . expression ) ;
61+ }
62+
63+ /**
64+ * Infers display name of a styled component.
65+ * Recognizes the following patterns:
66+ *
67+ * (const|var|let) ComponentName = styled...
68+ * export default styled...
69+ */
2970function getDisplayName ( node : ts . Node ) : string | undefined {
3071 if ( node . kind === ts . SyntaxKind . VariableDeclaration
3172 && ( node as ts . VariableDeclaration ) . name . kind === ts . SyntaxKind . Identifier ) {
3273 return ( ( node as ts . VariableDeclaration ) . name as ts . Identifier ) . text ;
3374 }
3475
3576 if ( node . kind === ts . SyntaxKind . ExportAssignment ) {
36-
77+ // todo: infer display name from file name
3778 }
3879
3980 return undefined ;
@@ -58,6 +99,11 @@ const transformer: ts.TransformerFactory<ts.SourceFile> = (context) => {
5899 }
59100 }
60101
102+ ts . forEachChild ( node , n => {
103+ if ( ! n . parent )
104+ n . parent = node ;
105+ } ) ;
106+
61107 return ts . visitEachChild ( node , visitor , context ) ;
62108 }
63109
0 commit comments