// @flow import { createMacro } from 'babel-plugin-macros' import { addDefault, addNamed } from '@babel/helper-module-imports' import { transformExpressionWithStyles, getStyledOptions } from './utils' export let createStyledMacro = ({ importPath, originalImportPath = importPath, isWeb }: { importPath: string, originalImportPath?: string, isWeb: boolean }) => createMacro(({ references, state, babel, isEmotionCall }) => { if (!isEmotionCall) { state.emotionSourceMap = true } const t = babel.types if (references.default && references.default.length) { let _styledIdentifier let getStyledIdentifier = () => { if (_styledIdentifier === undefined) { _styledIdentifier = addDefault(state.file.path, importPath, { nameHint: 'styled' }) } return t.cloneDeep(_styledIdentifier) } let originalImportPathStyledIdentifier let getOriginalImportPathStyledIdentifier = () => { if (originalImportPathStyledIdentifier === undefined) { originalImportPathStyledIdentifier = addDefault( state.file.path, originalImportPath, { nameHint: 'styled' } ) } return t.cloneDeep(originalImportPathStyledIdentifier) } if (importPath === originalImportPath) { getOriginalImportPathStyledIdentifier = getStyledIdentifier } references.default.forEach(reference => { let isCall = false if ( t.isMemberExpression(reference.parent) && reference.parent.computed === false ) { isCall = true if ( // checks if the first character is lowercase // becasue we don't want to transform the member expression if // it's in primitives/native reference.parent.property.name.charCodeAt(0) > 96 ) { reference.parentPath.replaceWith( t.callExpression(getStyledIdentifier(), [ t.stringLiteral(reference.parent.property.name) ]) ) } else { reference.replaceWith(getStyledIdentifier()) } } else if ( reference.parentPath && reference.parentPath.parentPath && t.isCallExpression(reference.parentPath) && reference.parent.callee === reference.node ) { isCall = true reference.replaceWith(getStyledIdentifier()) } else { reference.replaceWith(getOriginalImportPathStyledIdentifier()) } if (reference.parentPath && reference.parentPath.parentPath) { const styledCallPath = reference.parentPath.parentPath let node = transformExpressionWithStyles({ path: styledCallPath, state, babel, shouldLabel: false }) if (node && isWeb) { // we know the argument length will be 1 since that's the only time we will have a node since it will be static styledCallPath.node.arguments[0] = node } } if (isCall) { reference.addComment('leading', '#__PURE__') if (isWeb) { reference.parentPath.node.arguments[1] = getStyledOptions( t, reference.parentPath, state ) } } }) } Object.keys(references) .filter(x => x !== 'default') .forEach(referenceKey => { let runtimeNode = addNamed(state.file.path, referenceKey, importPath) references[referenceKey].reverse().forEach(reference => { reference.replaceWith(t.cloneDeep(runtimeNode)) }) }) })