import traverse , { NodePath , Visitor } from '@babel/traverse' ; import * as t from '@babel/types' ; import type { StaticConfigParsed , TamaguiInternalConfig } from '@tamagui/core' ; import * as CoreNode from '@tamagui/core-node' ; import { stylePropsTransform } from '@tamagui/helpers' ; import { difference , pick } from 'lodash' ; import { ExtractedAttr , ExtractedAttrAttr , ExtractorParseProps , Ternary } from '../types' ; import { createEvaluator , createSafeEvaluator } from './createEvaluator' ; import { evaluateAstNode } from './evaluateAstNode' ; import { attrStr , findComponentName , isInsideTamagui , isPresent , objToStr } from './extractHelpers' ; import { findTopmostFunction } from './findTopmostFunction' ; import { getStaticBindingsForScope } from './getStaticBindingsForScope' ; import { literalToAst } from './literalToAst' ; import { loadTamagui } from './loadTamagui' ; import { logLines } from './logLines' ; import { normalizeTernaries } from './normalizeTernaries' ; import { removeUnusedHooks } from './removeUnusedHooks' ; const { mediaQueryConfig , postProcessStyles , pseudos } = CoreNode ; export const FAILED_EVAL = Symbol ( 'failed_style_eval' ) ; const UNTOUCHED_PROPS = { key : true , style : true , className : true , } ; const isAttr = ( x ) : x is ExtractedAttrAttr => x . type === 'attr' ; const validHooks = { useMedia : true , useTheme : true , } ; export type Extractor = ReturnType < typeof createExtractor > ; const createTernary = ( x ) => x ; export function createExtractor ( ) { const shouldAddDebugProp = ! process . env . npm_package_dependencies_next && process . env . TAMAGUI_TARGET !== 'native' && process . env . IDENTIFY_TAGS !== 'false' && ( process . env . NODE_ENV === 'development' || process . env . DEBUG || process . env . IDENTIFY_TAGS ) ; require ( 'esbuild-register/dist/node' ) . register ( { target : 'es2019' , format : 'cjs' , } ) ; let loadedTamaguiConfig ; let hasLogged = false ; return { getTamagui ( ) { return loadedTamaguiConfig ; } , parse : ( fileOrPath , { config = 'tamagui.config.ts' , importsWhitelist = [ 'constants.js' ] , evaluateVars = true , shouldPrintDebug = false , sourcePath = '' , onExtractTag , getFlattenedNode , disableExtraction , disableExtractInlineMedia , disableExtractVariables , disableDebugAttr , ... props } ) => { if ( sourcePath === '' ) { throw new Error ( `Must provide a source file name` ) ; } if ( ! Array . isArray ( props . components ) ) { throw new Error ( `Must provide components array with list of Tamagui component modules` ) ; } const { components , tamaguiConfig } = loadTamagui ( { config , components : props . components || [ 'tamagui' ] , } ) ; loadedTamaguiConfig = tamaguiConfig ; const defaultTheme = tamaguiConfig . themes [ Object . keys ( tamaguiConfig . themes ) [ 0 ] ] ; const body = fileOrPath . type === 'Program' ? fileOrPath . get ( 'body' ) : fileOrPath . program . body ; /**
             * Step 1: Determine if importing any statically extractable components
             */ const isInternalImport = ( importStr ) => { return isInsideTamagui ( sourcePath ) && importStr [ 0 ] === '.' ; } ; const validComponents = Object . keys ( components ) . filter ( ( key ) => key [ 0 ] . toUpperCase ( ) === key [ 0 ] && ! ! components [ key ] ?. staticConfig ) . reduce ( ( obj , name ) => { obj [ name ] = components [ name ] ; return obj ; } , { } ) ; let doesUseValidImport = false ; for ( const bodyPath of body ) { if ( bodyPath . type !== 'ImportDeclaration' ) continue ; const node = ( 'node' in bodyPath ? bodyPath . node : bodyPath ) as any ; const from = node . source . value ; if ( props . components . includes ( from ) || isInternalImport ( from ) ) { if ( node . specifiers . some ( ( specifier ) => { const name = specifier . local . name ; return validComponents [ name ] || validHooks [ name ] ; } ) ) { doesUseValidImport = true ; break ; } } } if ( shouldPrintDebug ) { console . log ( sourcePath , { doesUseValidImport } ) ; } if ( ! doesUseValidImport ) { return null ; } let couldntParse = false ; const modifiedComponents = new Set < NodePath < any > > ( ) ; const bindingCache = { } ; const callTraverse = ( a ) => { return fileOrPath . type === 'File' ? traverse ( fileOrPath , a ) : fileOrPath . traverse ( a ) ; } ; /**
             * Step 2: Statically extract from JSX < /> nodes
             */ let programPath ; const res = { flattened : 0 , optimized : 0 , modified : 0 , } ; callTraverse ( { Program : { enter ( path ) { programPath = path ; } , } , JSXElement ( traversePath ) { const node = traversePath . node . openingElement ; const ogAttributes = node . attributes ; const componentName = findComponentName ( traversePath . scope ) ; const closingElement = traversePath . node . closingElement ; if ( t . isJSXMemberExpression ( closingElement ?. name ) || ! t . isJSXIdentifier ( node . name ) ) { return ; } const binding = traversePath . scope . getBinding ( node . name . name ) ; if ( binding ) { if ( ! t . isImportDeclaration ( binding . path . parent ) ) { return ; } const source = binding . path . parent . source ; if ( ! props . components . includes ( source . value ) && ! isInternalImport ( source . value ) ) { return ; } if ( ! validComponents [ binding . identifier . name ] ) { return ; } } const component = validComponents [ node . name . name ] as { staticConfig ? : StaticConfigParsed ; } ; if ( ! component || ! component . staticConfig ) { return ; } const originalNodeName = node . name . name ; if ( shouldPrintDebug ) { console . log ( `\n<${ originalNodeName } />` ) ; } const filePath = sourcePath . replace ( process . cwd ( ) , '.' ) ; const lineNumbers = node . loc ? node . loc . start . line + ( node . loc . start . line !== node . loc . end . line ? `-${ node . loc . end . line }` : '' ) : '' ; if ( shouldAddDebugProp && ! disableDebugAttr ) { const preName = componentName ? `${ componentName }:` : '' ; res . modified ++ ; node . attributes . unshift ( t . jsxAttribute ( t . jsxIdentifier ( 'data-is' ) , t . stringLiteral ( `  ${ preName }${ node . name . name }   ${ filePath . replace ( './' , '' ) }:${ lineNumbers }  ` ) ) ) ; } if ( disableExtraction ) { if ( ! hasLogged ) { console . log ( '🥚 Tamagui disableExtraction set: no CSS or optimizations will be run' ) ; hasLogged = true ; } return ; } const { staticConfig } = component ; const isTextView = staticConfig . isText || false ; const validStyles = staticConfig ?. validStyles ?? { } ; let tagName = staticConfig . defaultProps ?. tag ?? ( isTextView ? 'span' : 'div' ) ; traversePath . get ( 'openingElement' ) . get ( 'attributes' ) . forEach ( ( path ) => { const attr = path . node ; if ( t . isJSXSpreadAttribute ( attr ) ) return ; if ( attr . name . name !== 'tag' ) return ; const val = attr . value ; if ( ! t . isStringLiteral ( val ) ) return ; tagName = val . value ; } ) ; const flatNode = getFlattenedNode ( { isTextView , tag : tagName } ) ; const deoptProps = new Set ( [ ... ( props . deoptProps ?? [ ] ) , ... ( staticConfig . deoptProps ?? [ ] ) , ] ) ; const excludeProps = new Set ( props . excludeProps ?? [ ] ) ; const isExcludedProp = ( name ) => { const res = excludeProps . has ( name ) ; if ( res && shouldPrintDebug ) console . log ( `  excluding ${ name }` ) ; return res ; } ; const isDeoptedProp = ( name ) => { const res = deoptProps . has ( name ) ; if ( res && shouldPrintDebug ) console . log ( `  deopting ${ name }` ) ; return res ; } ; const staticNamespace = getStaticBindingsForScope ( traversePath . scope , importsWhitelist , sourcePath , bindingCache , shouldPrintDebug ) ; const attemptEval = ! evaluateVars ? evaluateAstNode : createEvaluator ( { tamaguiConfig , staticNamespace , sourcePath , traversePath , shouldPrintDebug , } ) ; const attemptEvalSafe = createSafeEvaluator ( attemptEval ) ; if ( shouldPrintDebug ) { console . log ( '  staticNamespace' , Object . keys ( staticNamespace ) . join ( ', ' ) ) ; } const hasDeopt = ( obj ) => { return Object . keys ( obj ) . some ( isDeoptedProp ) ; } ; const flattenedAttrs = [ ] ; traversePath . get ( 'openingElement' ) . get ( 'attributes' ) . forEach ( ( path ) => { const attr = path . node ; if ( ! t . isJSXSpreadAttribute ( attr ) ) { flattenedAttrs . push ( attr ) ; return ; } let arg ; try { arg = attemptEval ( attr . argument ) ; } catch ( e ) { if ( shouldPrintDebug ) { console . log ( '  couldnt parse spread' , e . message ) ; } flattenedAttrs . push ( attr ) ; return ; } if ( typeof arg !== 'undefined' ) { try { if ( typeof arg !== 'object' || arg == null ) { if ( shouldPrintDebug ) { console . log ( '  non object or null arg' , arg ) ; } flattenedAttrs . push ( attr ) ; } else { for ( const k in arg ) { const value = arg [ k ] ; if ( ! value && typeof value === 'object' ) { console . log ( 'shouldnt we handle this?' , k , value , arg ) ; continue ; } flattenedAttrs . push ( t . jsxAttribute ( t . jsxIdentifier ( k ) , t . jsxExpressionContainer ( literalToAst ( value ) ) ) ) ; } } } catch ( err ) { console . warn ( 'cant parse spread, caught err' , err ) ; couldntParse = true ; } } } ) ; if ( couldntParse ) { return ; } node . attributes = flattenedAttrs ; if ( staticConfig . defaultProps ) { for ( const key in staticConfig . defaultProps ) { const serialize = require ( 'babel-literal-to-ast' ) ; const val = staticConfig . defaultProps [ key ] ; node . attributes . unshift ( t . jsxAttribute ( t . jsxIdentifier ( key ) , typeof val === 'string' ? t . stringLiteral ( val ) : t . jsxExpressionContainer ( serialize ( val ) ) ) ) ; } } let attrs = [ ] ; let shouldDeopt = false ; let inlinePropCount = 0 ; let isFlattened = false ; attrs = traversePath . get ( 'openingElement' ) . get ( 'attributes' ) . flatMap ( ( path ) => { try { const res = evaluateAttribute ( path ) ; if ( ! res ) { path . remove ( ) ; } return res ; } catch ( err ) { console . log ( 'Error extracting attribute' , err . message , err . stack ) ; console . log ( 'node' , path . node ) ; return { type : 'attr' , value : path . node , } as const ; } } ) . flat ( 4 ) . filter ( isPresent ) ; function isStaticAttributeName ( name ) { return ! ! ( ! ! validStyles [ name ] || staticConfig . validPropsExtra ?. [ name ] || ! ! pseudos [ name ] || staticConfig . variants ?. [ name ] || tamaguiConfig . shorthands [ name ] || ( name [ 0 ] === '$' ? ! ! mediaQueryConfig [ name . slice ( 1 ) ] : false ) ) ; } function isExtractable ( obj ) { return ( t . isObjectExpression ( obj ) && obj . properties . every ( ( prop ) => { if ( ! t . isObjectProperty ( prop ) ) { console . log ( 'not object prop' , prop ) ; return false ; } const propName = prop . key [ 'name' ] ; if ( ! isStaticAttributeName ( propName ) && propName !== 'tag' ) { if ( shouldPrintDebug ) { console . log ( '  not a valid style prop!' , propName ) ; } return false ; } return true ; } ) ) ; } function createTernariesFromObjectProperties ( test , side , ternaryPartial = { } ) { if ( ! side ) { return null ; } if ( ! isExtractable ( side ) ) { throw new Error ( 'not extractable' ) ; } return side . properties . flatMap ( ( property ) => { if ( ! t . isObjectProperty ( property ) ) { throw new Error ( 'expected object property' ) ; } if ( t . isConditionalExpression ( property . value ) ) { const [ truthy , falsy ] = [ t . objectExpression ( [ t . objectProperty ( property . key , property . value . consequent ) ] ) , t . objectExpression ( [ t . objectProperty ( property . key , property . value . alternate ) ] ) , ] . map ( ( x ) => attemptEval ( x ) ) ; return [ createTernary ( { remove ( ) { } , ... ternaryPartial , test : t . logicalExpression ( '&&' , test , property . value . test ) , consequent : truthy , alternate : null , } ) , createTernary ( { ... ternaryPartial , test : t . logicalExpression ( '&&' , test , t . unaryExpression ( '!' , property . value . test ) ) , consequent : falsy , alternate : null , remove ( ) { } , } ) , ] ; } const obj = t . objectExpression ( [ t . objectProperty ( property . key , property . value ) ] ) ; const consequent = attemptEval ( obj ) ; return createTernary ( { remove ( ) { } , ... ternaryPartial , test , consequent , alternate : null , } ) ; } ) ; } function evaluateAttribute ( path ) { const attribute = path . node ; const attr = { type : 'attr' , value : attribute } ; if ( t . isJSXSpreadAttribute ( attribute ) ) { const arg = attribute . argument ; const conditional = t . isConditionalExpression ( arg ) ? ( [ arg . test , arg . consequent , arg . alternate ] as const ) : t . isLogicalExpression ( arg ) && arg . operator === '&&' ? ( [ arg . left , arg . right , null ] as const ) : null ; if ( conditional ) { const [ test , alt , cons ] = conditional ; if ( ! test ) throw new Error ( `no test` ) ; if ( [ alt , cons ] . some ( ( side ) => side && ! isExtractable ( side ) ) ) { if ( shouldPrintDebug ) { console . log ( 'not extractable' , alt , cons ) ; } return attr ; } return [ ... ( createTernariesFromObjectProperties ( test , alt ) || [ ] ) , ... ( ( cons && createTernariesFromObjectProperties ( t . unaryExpression ( '!' , test ) , cons ) ) || [ ] ) , ] . map ( ( ternary ) => ( { type : 'ternary' , value : ternary , } ) ) ; } } if ( t . isJSXSpreadAttribute ( attribute ) || ! attribute . name || typeof attribute . name . name !== 'string' ) { inlinePropCount ++ ; return attr ; } const name = attribute . name . name ; if ( isExcludedProp ( name ) ) { return null ; } if ( isDeoptedProp ( name ) ) { if ( shouldPrintDebug ) { console . log ( '  ! inlining, deopt prop' , name ) ; } inlinePropCount ++ ; return attr ; } if ( UNTOUCHED_PROPS [ name ] ) { return attr ; } if ( name [ 0 ] === '$' && t . isJSXExpressionContainer ( attribute ?. value ) ) { if ( disableExtractInlineMedia ) { return attr ; } const shortname = name . slice ( 1 ) ; if ( mediaQueryConfig [ shortname ] ) { const expression = attribute . value . expression ; if ( ! t . isJSXEmptyExpression ( expression ) ) { const ternaries = createTernariesFromObjectProperties ( t . stringLiteral ( shortname ) , expression , { inlineMediaQuery : shortname , } ) ; if ( ternaries ) { return ternaries . map ( ( value ) => ( { type : 'ternary' , value , } ) ) ; } } } } const [ value , valuePath ] = ( ( ) => { if ( t . isJSXExpressionContainer ( attribute ?. value ) ) { return [ attribute . value . expression ! , path . get ( 'value' ) ! ] as const ; } else { return [ attribute . value ! , path . get ( 'value' ) ! ] as const ; } } ) ( ) ; const remove = ( ) => { Array . isArray ( valuePath ) ? valuePath . map ( ( p ) => p . remove ( ) ) : valuePath . remove ( ) ; } ; if ( name === 'ref' ) { if ( shouldPrintDebug ) { console . log ( '  ! inlining, ref' , name ) ; } inlinePropCount ++ ; return attr ; } if ( name === 'tag' ) { return { type : 'attr' , value : path . node , } ; } if ( disableExtractVariables ) { if ( value ) { if ( value . type === 'StringLiteral' && value . value [ 0 ] === '$' ) { if ( shouldPrintDebug ) { console . log ( '  native, disable extract var' , value . value ) ; } inlinePropCount ++ ; return attr ; } } } const styleValue = attemptEvalSafe ( value ) ; if ( ! isStaticAttributeName ( name ) ) { let keys = [ name ] ; if ( staticConfig . propMapper ) { const out = staticConfig . propMapper ( name , styleValue , defaultTheme , staticConfig . defaultProps ) ; if ( out ) { keys = Object . keys ( out ) ; } } if ( keys . some ( ( k ) => ! isStaticAttributeName ( k ) && k !== 'tag' ) ) { if ( shouldPrintDebug ) { console . log ( '  ! inlining, not static attribute name' , name ) ; } inlinePropCount ++ ; return attr ; } } if ( styleValue !== FAILED_EVAL ) { return { type : 'style' , value : { [ name ] : styleValue } , name , attr : path . node , } ; } if ( t . isBinaryExpression ( value ) ) { const { operator , left , right } = value ; const lVal = attemptEvalSafe ( left ) ; const rVal = attemptEvalSafe ( right ) ; if ( shouldPrintDebug ) { console . log ( `  evalBinaryExpression lVal ${ String ( lVal ) }, rVal ${ String ( rVal ) }` ) ; } if ( lVal !== FAILED_EVAL && t . isConditionalExpression ( right ) ) { const ternary = addBinaryConditional ( operator , left , right ) ; if ( ternary ) return ternary ; } if ( rVal !== FAILED_EVAL && t . isConditionalExpression ( left ) ) { const ternary = addBinaryConditional ( operator , right , left ) ; if ( ternary ) return ternary ; } if ( shouldPrintDebug ) { console . log ( `  evalBinaryExpression cant extract` ) ; } inlinePropCount ++ ; return attr ; } const staticConditional = getStaticConditional ( value ) ; if ( staticConditional ) { return { type : 'ternary' , value : staticConditional } ; } const staticLogical = getStaticLogical ( value ) ; if ( staticLogical ) { return { type : 'ternary' , value : staticLogical } ; } if ( shouldPrintDebug ) { console . log ( '  ! inline prop via no match' , name , value . type ) ; } inlinePropCount ++ ; return attr ; function addBinaryConditional ( operator , staticExpr , cond ) { if ( getStaticConditional ( cond ) ) { const alt = attemptEval ( t . binaryExpression ( operator , staticExpr , cond . alternate ) ) ; const cons = attemptEval ( t . binaryExpression ( operator , staticExpr , cond . consequent ) ) ; if ( shouldPrintDebug ) { console . log ( '  binaryConditional' , cond . test , cons , alt ) ; } return { type : 'ternary' , value : { test : cond . test , remove , alternate : { [ name ] : alt } , consequent : { [ name ] : cons } , } , } ; } return null ; } function getStaticConditional ( value ) { if ( t . isConditionalExpression ( value ) ) { try { if ( shouldPrintDebug ) { console . log ( 'attempt' , value . alternate , value . consequent ) ; } const aVal = attemptEval ( value . alternate ) ; const cVal = attemptEval ( value . consequent ) ; if ( shouldPrintDebug ) { const type = value . test . type ; console . log ( '      static ternary' , type , cVal , aVal ) ; } return { test : value . test , remove , consequent : { [ name ] : cVal } , alternate : { [ name ] : aVal } , } ; } catch ( err ) { if ( shouldPrintDebug ) { console . log ( '       cant eval ternary' , err . message ) ; } } } return null ; } function getStaticLogical ( value ) { if ( t . isLogicalExpression ( value ) ) { if ( value . operator === '&&' ) { try { const val = attemptEval ( value . right ) ; if ( shouldPrintDebug ) { console . log ( '  staticLogical' , value . left , name , val ) ; } return { test : value . left , remove , consequent : { [ name ] : val } , alternate : null , } ; } catch ( err ) { if ( shouldPrintDebug ) { console . log ( '  cant static eval logical' , err ) ; } } } } return null ; } } if ( shouldPrintDebug ) { console . log ( '  - attrs (before):\n' , logLines ( attrs . map ( attrStr ) . join ( ', ' ) ) ) ; } node . attributes = attrs . filter ( isAttr ) . map ( ( x ) => x . value ) ; if ( couldntParse ) { if ( shouldPrintDebug ) { console . log ( `  cancel:` , { couldntParse , shouldDeopt } ) ; } node . attributes = ogAttributes ; return ; } const parentFn = findTopmostFunction ( traversePath ) ; if ( parentFn ) { modifiedComponents . add ( parentFn ) ; } let ternaries = [ ] ; attrs = attrs . reduce < ( ExtractedAttr | ExtractedAttr [ ] ) [ ] > ( ( out , cur ) => { const next = attrs [ attrs . indexOf ( cur ) + 1 ] ; if ( cur . type === 'ternary' ) { ternaries . push ( cur . value ) ; } if ( ( ! next || next . type !== 'ternary' ) && ternaries . length ) { const normalized = normalizeTernaries ( ternaries ) . map ( ( { alternate , consequent , ... rest } ) => { return { type : 'ternary' as const , value : { ... rest , alternate : alternate || null , consequent : consequent || null , } , } ; } ) ; try { return [ ... out , ... normalized ] ; } finally { if ( shouldPrintDebug ) { console . log ( `    normalizeTernaries (${ ternaries . length } => ${ normalized . length })` ) ; } ternaries = [ ] ; } } if ( cur . type === 'ternary' ) { return out ; } out . push ( cur ) ; return out ; } , [ ] ) . flat ( ) ; const completeStaticProps = { ... Object . keys ( attrs ) . reduce ( ( acc , index ) => { const cur = attrs [ index ] as ExtractedAttr ; if ( cur . type === 'style' ) { Object . assign ( acc , cur . value ) ; } if ( cur . type === 'attr' ) { if ( t . isJSXSpreadAttribute ( cur . value ) ) { return acc ; } if ( ! t . isJSXIdentifier ( cur . value . name ) ) { return acc ; } const key = cur . value . name . name ; const value = attemptEvalSafe ( cur . value . value || t . booleanLiteral ( true ) ) ; if ( value === FAILED_EVAL ) { return acc ; } acc [ key ] = value ; } return acc ; } , { } ) , } ; const hasSpread = node . attributes . some ( ( x ) => t . isJSXSpreadAttribute ( x ) ) ; const hasOnlyStringChildren = ! hasSpread && ( node . selfClosing || ( traversePath . node . children && traversePath . node . children . every ( ( x ) => x . type === 'JSXText' ) ) ) ; const shouldFlatten = ! shouldDeopt && inlinePropCount === 0 && ! hasSpread && staticConfig . neverFlatten !== true && ( staticConfig . neverFlatten === 'jsx' ? hasOnlyStringChildren : true ) ; if ( ! shouldFlatten ) { attrs = attrs . reduce < ExtractedAttr [ ] > ( ( acc , cur ) => { if ( cur . type === 'style' ) { for ( const key in cur . value ) { const shouldEnsureOverridden = ! ! staticConfig . ensureOverriddenProp ?. [ key ] ; const isSetInAttrsAlready = attrs . some ( ( x ) => x . type === 'attr' && x . value . type === 'JSXAttribute' && x . value . name . name === key ) ; if ( ! isSetInAttrsAlready ) { const isVariant = ! ! staticConfig . variants ?. [ cur . name || '' ] ; if ( isVariant || shouldEnsureOverridden ) { acc . push ( { type : 'attr' , value : cur . attr || t . jsxAttribute ( t . jsxIdentifier ( key ) , t . jsxExpressionContainer ( t . nullLiteral ( ) ) ) , } ) ; } } } } acc . push ( cur ) ; return acc ; } , [ ] ) ; } if ( shouldPrintDebug ) { console . log ( '  - attrs (flattened): \n' , logLines ( attrs . map ( attrStr ) . join ( ', ' ) ) ) ; } attrs = attrs . reduce < ExtractedAttr [ ] > ( ( acc , cur ) => { if ( cur . type !== 'attr' || ! t . isJSXAttribute ( cur . value ) || typeof cur . value . name . name !== 'string' ) { if ( cur . type === 'style' ) { const key = Object . keys ( cur . value ) [ 0 ] ; if ( ! validStyles [ key ] && ! pseudos [ key ] ) { if ( shouldPrintDebug ) { console . log ( ' ❌ excluding' , key ) ; } return acc ; } } } acc . push ( cur ) ; return acc ; } , [ ] ) ; if ( shouldPrintDebug ) { console . log ( '  - attrs (evaluated styles): \n' , logLines ( attrs . map ( attrStr ) . join ( ', ' ) ) ) ; } let prev = null ; attrs = attrs . reduce < ExtractedAttr [ ] > ( ( acc , cur ) => { if ( cur . type === 'style' ) { if ( prev ?. type === 'style' ) { Object . assign ( prev . value , cur . value ) ; return acc ; } } acc . push ( cur ) ; prev = cur ; return acc ; } , [ ] ) ; if ( shouldPrintDebug ) { console . log ( '  - attrs (combined 🔀): \n' , logLines ( attrs . map ( attrStr ) . join ( ', ' ) ) ) ; } const getStyles = ( props ) => { if ( ! props ) return ; if ( ! ! excludeProps . size ) { for ( const key in props ) { if ( isExcludedProp ( key ) ) delete props [ key ] ; } } const out = postProcessStyles ( props , staticConfig , defaultTheme ) ; const next = out ?. style ?? props ; if ( shouldPrintDebug ) { console . log ( '  getStyles props >>\n' , logLines ( objToStr ( props ) ) ) ; console . log ( '  getStyles next  >>\n' , logLines ( objToStr ( next ) ) ) ; console . log ( '  getStyles style >>\n' , logLines ( objToStr ( out . style ) ) ) ; console . log ( '  getStyles viewp >>\n' , logLines ( objToStr ( out . viewProps ) ) ) ; } if ( staticConfig . validStyles ) { for ( const key in next ) { if ( ! staticConfig . validStyles [ key ] && ! pseudos [ key ] ) { delete next [ key ] ; } } } return next ; } ; const completeStylesProcessed = getStyles ( { ... staticConfig . defaultProps , ... completeStaticProps , } ) ; const stylesToAddToInitialGroup = difference ( Object . keys ( completeStylesProcessed ) , Object . keys ( completeStaticProps ) ) ; if ( stylesToAddToInitialGroup . length ) { const toAdd = pick ( completeStylesProcessed , ... stylesToAddToInitialGroup ) ; const firstGroup = attrs . find ( ( x ) => x . type === 'style' ) ; if ( shouldPrintDebug ) { console . log ( '    stylesToAddToInitialGroup' , stylesToAddToInitialGroup . join ( ', ' ) ) ; console . log ( '    toAdd' , objToStr ( toAdd ) ) ; } if ( ! firstGroup ) { attrs . unshift ( { type : 'style' , value : toAdd } ) ; } else { Object . assign ( firstGroup . value , toAdd ) ; } } if ( shouldPrintDebug ) { console . log ( '   completeStaticProps\n' , logLines ( objToStr ( completeStaticProps ) ) ) ; console . log ( '   completeStylesProcessed\n' , logLines ( objToStr ( completeStylesProcessed ) ) ) ; } for ( const attr of attrs ) { try { switch ( attr . type ) { case 'ternary' : const a = getStyles ( attr . value . alternate ) ; const c = getStyles ( attr . value . consequent ) ; attr . value . alternate = a ; attr . value . consequent = c ; if ( shouldPrintDebug ) { console . log ( '     => tern ' , attrStr ( attr ) ) ; } break ; case 'style' : const next = { } ; for ( const key in attr . value ) { if ( key in stylePropsTransform ) { next [ 'transform' ] = completeStylesProcessed [ 'transform' ] ; } else { next [ key ] = completeStylesProcessed [ key ] ?? attr . value [ key ] ; } } attr . value = next ; break ; } } catch ( err ) { if ( shouldPrintDebug ) { console . log ( ' postprocessing error, deopt' , err ) ; node . attributes = ogAttributes ; return node ; } } } if ( shouldPrintDebug ) { console . log ( '  - attrs (after):\n' , logLines ( attrs . map ( attrStr ) . join ( ', ' ) ) ) ; } if ( shouldFlatten ) { if ( shouldPrintDebug ) { console . log ( '  [✅] flattening' , originalNodeName , flatNode ) ; } isFlattened = true ; node . name . name = flatNode ; res . flattened ++ ; if ( closingElement ) { closingElement . name . name = flatNode ; } } if ( shouldPrintDebug ) { console . log ( '  [❊] inline props ' , inlinePropCount , shouldDeopt ? ' deopted' : '' , hasSpread ? ' spread' : '' , '!flatten' , staticConfig . neverFlatten ) ; console . log ( '  - attrs (end):\n' , logLines ( attrs . map ( attrStr ) . join ( ', ' ) ) ) ; } res . optimized ++ ; onExtractTag ( { attrs , node , lineNumbers , filePath , attemptEval , jsxPath : traversePath , originalNodeName , isFlattened , programPath , } ) ; } , } ) ; /**
             * Step 3: Remove dead code from removed media query / theme hooks
             */ if ( modifiedComponents . size ) { const all = Array . from ( modifiedComponents ) ; if ( shouldPrintDebug ) { console . log ( '  [🪝] hook check' , all . length ) ; } for ( const comp of all ) { removeUnusedHooks ( comp , shouldPrintDebug ) ; } } return res ; } , } ; }