import type { NodePath } from '@babel/core' ; import * as t from '@babel/types' ; import { transformCss } from '@compiled/css' ; import type { Metadata } from '../types' ; import { buildCodeFrameError , pickFunctionBody } from '../utils/ast' ; import { compiledTemplate } from '../utils/build-compiled-component' ; import { buildCssVariables } from '../utils/build-css-variables' ; import { buildCss , getItemCss } from '../utils/css-builders' ; import { resolveIdentifierComingFromDestructuring } from '../utils/resolve-binding' ; import type { CSSOutput } from '../utils/types' ; param variables param path const handleStyleProp = ( variables , path ) => { const styleValue = variables . length ? t . objectExpression ( buildCssVariables ( variables ) ) : t . identifier ( 'undefined' ) ; path . replaceWith ( styleValue ) ; } ; param path const extractStyles = ( path ) : t . Expression [ ] | t . Expression | undefined => { if ( t . isCallExpression ( path . node ) && t . isIdentifier ( path . node . callee ) && path . node . callee . name === 'css' && t . isExpression ( path . node . arguments [ 0 ] ) ) { const styles = path . node . arguments as t . Expression [ ] ; return styles ; } if ( t . isCallExpression ( path . node ) && t . isIdentifier ( path . node . callee ) && t . isExpression ( path . node . arguments [ 0 ] ) && path . scope . hasOwnBinding ( path . node . callee . name ) ) { const binding = path . scope . getBinding ( path . node . callee . name ) ?. path . node ; if ( ! ! resolveIdentifierComingFromDestructuring ( { name : 'css' , node : binding as t . Expression } ) ) { const styles = path . node . arguments as t . Expression [ ] ; return styles ; } } if ( t . isCallExpression ( path . node ) && t . isMemberExpression ( path . node . callee ) ) { if ( t . isIdentifier ( path . node . callee . property ) && path . node . callee . property . name === 'css' && t . isExpression ( path . node . arguments [ 0 ] ) ) { const styles = path . node . arguments as t . Expression [ ] ; return styles ; } } if ( t . isTaggedTemplateExpression ( path . node ) ) { const styles = path . node . quasi ; return styles ; } return undefined ; } ; param path const getJsxChildrenAsFunction = ( path ) => { const children = path . node . children . find ( ( node ) => t . isJSXExpressionContainer ( node ) ) ; if ( t . isJSXExpressionContainer ( children ) && t . isFunction ( children . expression ) ) { return children . expression ; } throw buildCodeFrameError ( `ClassNames children should be a function
E.g: <ClassNames>{props => <div />}</ClassNames>` , path . node , path ) ; } ; param path { NodePath } param meta { Metadata } export const visitClassNamesPath = ( path , meta ) : void => { if ( t . isJSXIdentifier ( path . node . openingElement . name ) && path . node . openingElement . name . name !== meta . state . compiledImports ?. ClassNames ) { return ; } const collectedVariables = [ ] ; const collectedSheets = [ ] ; path . traverse ( { Expression ( path ) { const styles = extractStyles ( path ) ; if ( ! styles ) { return ; } const builtCss = buildCss ( styles , meta ) ; const { sheets , classNames } = transformCss ( builtCss . css . map ( ( x ) => getItemCss ( x ) ) . join ( '' ) ) ; collectedVariables . push ( ... builtCss . variables ) ; collectedSheets . push ( ... sheets ) ; path . replaceWith ( t . stringLiteral ( classNames . join ( ' ' ) ) ) ; } , } ) ; path . traverse ( { Expression ( path ) { if ( t . isIdentifier ( path . node ) ) { if ( path . parentPath . isProperty ( ) ) { return ; } if ( path . node . name === 'style' && path . scope . hasOwnBinding ( 'style' ) ) { handleStyleProp ( collectedVariables , path ) ; } if ( path . scope . hasOwnBinding ( path . node . name ) ) { const binding = path . scope . getBinding ( path . node . name ) ?. path . node ; if ( ! ! resolveIdentifierComingFromDestructuring ( { name : 'style' , node : binding as t . Expression , } ) ) { handleStyleProp ( collectedVariables , path ) ; } } } else if ( t . isMemberExpression ( path . node ) ) { if ( t . isIdentifier ( path . node . object ) && ! path . scope . hasOwnBinding ( path . node . object . name ) ) { return ; } if ( t . isIdentifier ( path . node . property ) && path . node . property . name === 'style' ) { handleStyleProp ( collectedVariables , path ) ; } } } , } ) ; const children = getJsxChildrenAsFunction ( path ) ; const body = pickFunctionBody ( children ) ; path . replaceWith ( compiledTemplate ( body , collectedSheets , meta ) ) ; } ;