You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

199 lines
4.2 KiB

3 years ago
// @flow
import {
transformExpressionWithStyles,
createTransformerMacro,
getSourceMap,
addImport
} from './utils'
export const transformCssCallExpression = ({
state,
babel,
path,
sourceMap,
annotateAsPure = true
}: {
state: *,
babel: *,
path: *,
sourceMap?: string,
annotateAsPure?: boolean
}) => {
let node = transformExpressionWithStyles({
babel,
state,
path,
shouldLabel: true,
sourceMap
})
if (node) {
path.replaceWith(node)
path.hoist()
} else if (annotateAsPure && path.isCallExpression()) {
path.addComment('leading', '#__PURE__')
}
}
export const transformCsslessArrayExpression = ({
state,
babel,
path
}: {
babel: *,
state: *,
path: *
}) => {
let t = babel.types
let expressionPath = path.get('value.expression')
let sourceMap =
state.emotionSourceMap && path.node.loc !== undefined
? getSourceMap(path.node.loc.start, state)
: ''
expressionPath.replaceWith(
t.callExpression(
// the name of this identifier doesn't really matter at all
// it'll never appear in generated code
t.identifier('___shouldNeverAppearCSS'),
path.node.value.expression.elements
)
)
transformCssCallExpression({
babel,
state,
path: expressionPath,
sourceMap,
annotateAsPure: false
})
if (t.isCallExpression(expressionPath)) {
expressionPath.replaceWith(t.arrayExpression(expressionPath.node.arguments))
}
}
export const transformCsslessObjectExpression = ({
state,
babel,
path,
cssImport
}: {
babel: *,
state: *,
path: *,
cssImport: { importSource: string, cssExport: string }
}) => {
let t = babel.types
let expressionPath = path.get('value.expression')
let sourceMap =
state.emotionSourceMap && path.node.loc !== undefined
? getSourceMap(path.node.loc.start, state)
: ''
expressionPath.replaceWith(
t.callExpression(
// the name of this identifier doesn't really matter at all
// it'll never appear in generated code
t.identifier('___shouldNeverAppearCSS'),
[path.node.value.expression]
)
)
transformCssCallExpression({
babel,
state,
path: expressionPath,
sourceMap
})
if (t.isCallExpression(expressionPath)) {
expressionPath
.get('callee')
.replaceWith(
addImport(state, cssImport.importSource, cssImport.cssExport, 'css')
)
}
}
let cssTransformer = ({
state,
babel,
reference
}: {
state: any,
babel: any,
reference: any
}) => {
transformCssCallExpression({ babel, state, path: reference.parentPath })
}
let globalTransformer = ({
state,
babel,
reference,
importSource,
options
}: {
state: any,
babel: any,
reference: any,
importSource: string,
options: { cssExport?: string }
}) => {
const t = babel.types
if (
!t.isJSXIdentifier(reference.node) ||
!t.isJSXOpeningElement(reference.parentPath.node)
) {
return
}
const stylesPropPath = reference.parentPath
.get('attributes')
.find(p => t.isJSXAttribute(p.node) && p.node.name.name === 'styles')
if (!stylesPropPath) {
return
}
if (t.isJSXExpressionContainer(stylesPropPath.node.value)) {
if (t.isArrayExpression(stylesPropPath.node.value.expression)) {
transformCsslessArrayExpression({
state,
babel,
path: stylesPropPath
})
} else if (t.isObjectExpression(stylesPropPath.node.value.expression)) {
transformCsslessObjectExpression({
state,
babel,
path: stylesPropPath,
cssImport:
options.cssExport !== undefined
? {
importSource,
cssExport: options.cssExport
}
: {
importSource: '@emotion/react',
cssExport: 'css'
}
})
}
}
}
export const transformers = {
// this is an empty function because this transformer is never called
// we don't run any transforms on `jsx` directly
// instead we use it as a hint to enable css prop optimization
jsx: () => {},
css: cssTransformer,
Global: globalTransformer
}
export default createTransformerMacro(transformers, {
importSource: '@emotion/react'
})