diff --git a/src/babel.js b/src/babel.js
index 431cc3c..40deaf9 100644
--- a/src/babel.js
+++ b/src/babel.js
@@ -31,161 +31,177 @@ export default function ({types: t}) {
expr.value
)
+ const makeStyledJsxTag = transformedCss => (
+ t.JSXElement(
+ t.JSXOpeningElement(
+ t.JSXIdentifier(STYLE_COMPONENT),
+ [t.JSXAttribute(
+ t.JSXIdentifier(STYLE_COMPONENT_CSS),
+ t.JSXExpressionContainer(t.stringLiteral(transformedCss))
+ )],
+ true
+ ),
+ null,
+ []
+ )
+ )
+
return {
inherits: jsx,
visitor: {
JSXOpeningElement(path, state) {
- if (state.hasJSXStyle) {
- if (state.ignoreClosing == null) {
- // we keep a counter of elements inside so that we
- // can keep track of when we exit the parent to reset state
- // note: if we wished to add an option to turn off
- // selectors to reach parent elements, it would suffice to
- // set this to `1` and do an early return instead
- state.ignoreClosing = 0
- }
+ if (!state.hasJSXStyle) {
+ return
+ }
+
+ if (state.ignoreClosing === null) {
+ // we keep a counter of elements inside so that we
+ // can keep track of when we exit the parent to reset state
+ // note: if we wished to add an option to turn off
+ // selectors to reach parent elements, it would suffice to
+ // set this to `1` and do an early return instead
+ state.ignoreClosing = 0
+ }
- const el = path.node
+ const el = path.node
- if (el.name && el.name.name !== 'style') {
- for (const attr of el.attributes) {
- if (attr.name === MARKUP_ATTRIBUTE) {
- // avoid double attributes
- return
- }
+ if (el.name && el.name.name !== 'style') {
+ for (const attr of el.attributes) {
+ if (attr.name === MARKUP_ATTRIBUTE) {
+ // avoid double attributes
+ return
}
-
- const attr = t.jSXAttribute(
- t.JSXIdentifier(MARKUP_ATTRIBUTE),
- t.JSXExpressionContainer(t.stringLiteral(state.jsxId))
- )
- el.attributes.push(attr)
}
- state.ignoreClosing++
- // next visit will be: JSXElement exit()
+ const attr = t.jSXAttribute(
+ t.JSXIdentifier(MARKUP_ATTRIBUTE),
+ t.JSXExpressionContainer(t.stringLiteral(state.jsxId))
+ )
+ el.attributes.push(attr)
}
+
+ state.ignoreClosing++
+ // next visit will be: JSXElement exit()
},
JSXElement: {
enter(path, state) {
- if (state.hasJSXStyle == null) {
- const styles = findStyles(path.node.children)
-
- if (styles.length > 0) {
- state.jsxId = ''
- state.styles = []
-
- for (const style of styles) {
- // compute children excluding whitespace
- const children = style.children.filter(c => (
- t.isJSXExpressionContainer(c) ||
- // ignore whitespace around the expression container
- (t.isJSXText(c) && c.value.trim() !== '')
- ))
-
- if (children.length !== 1) {
- throw path.buildCodeFrameError(`Expected one child under ` +
- `JSX Style tag, but got ${style.children.length} ` +
- `(eg: )`)
- }
-
- const child = children[0]
-
- if (!t.isJSXExpressionContainer(child)) {
- throw path.buildCodeFrameError(`Expected a child of ` +
- `type JSXExpressionContainer under JSX Style tag ` +
- `(eg: ), got ${child.type}`)
- }
-
- const expression = child.expression
-
- if (!t.isTemplateLiteral(child.expression) &&
- !t.isStringLiteral(child.expression)) {
- throw path.buildCodeFrameError(`Expected a template ` +
- `literal or String literal as the child of the ` +
- `JSX Style tag (eg: ),` +
- ` but got ${expression.type}`)
- }
-
- const styleText = getExpressionText(expression)
- const styleId = String(hash(styleText))
-
- state.styles.push([
- styleId,
- styleText,
- expression.loc
- ])
- }
-
- state.jsxId += hash(state.styles.map(s => s[1]).join(''))
- state.hasJSXStyle = true
- state.file.hasJSXStyle = true
- // next visit will be: JSXOpeningElement
- } else {
- state.hasJSXStyle = false
+ if (state.hasJSXStyle !== null) {
+ return
+ }
+
+ const styles = findStyles(path.node.children)
+
+ if (styles.length === 0) {
+ state.hasJSXStyle = false
+ return
+ }
+
+ state.jsxId = ''
+ state.styles = []
+
+ for (const style of styles) {
+ // compute children excluding whitespace
+ const children = style.children.filter(c => (
+ t.isJSXExpressionContainer(c) ||
+ // ignore whitespace around the expression container
+ (t.isJSXText(c) && c.value.trim() !== '')
+ ))
+
+ if (children.length !== 1) {
+ throw path.buildCodeFrameError(`Expected one child under ` +
+ `JSX Style tag, but got ${style.children.length} ` +
+ `(eg: )`)
}
- } else if (state.hasJSXStyle) {
- const el = path.node.openingElement
-
- if (el.name && el.name.name === 'style') {
- // we replace styles with the function call
- const [id, css, loc] = state.styles.shift()
-
- const skipTransform = el.attributes.some(attr => (
- attr.name.name === GLOBAL_ATTRIBUTE
- ))
-
- const useSourceMaps = Boolean(state.file.opts.sourceMaps)
- let transformedCss = css
-
- if (!skipTransform) {
- if (useSourceMaps) {
- const filename = state.file.opts.sourceFileName
- const generator = new SourceMapGenerator({
- file: filename,
- sourceRoot: state.file.opts.sourceRoot
- })
- generator.setSourceContent(filename, state.file.code)
- transformedCss = [
- transform(id, css, generator, loc.start, filename),
- convert
- .fromObject(generator)
- .toComment({multiline: true}),
- `/*@ sourceURL=${filename} */`
- ].join('\n')
- } else {
- transformedCss = transform(id, css)
- }
- }
-
- path.replaceWith(
- t.JSXElement(
- t.JSXOpeningElement(
- t.JSXIdentifier(STYLE_COMPONENT),
- [
- t.JSXAttribute(
- t.JSXIdentifier(STYLE_COMPONENT_CSS),
- t.JSXExpressionContainer(t.stringLiteral(transformedCss))
- )
- ],
- true
- ),
- null,
- []
- )
- )
+
+ const child = children[0]
+
+ if (!t.isJSXExpressionContainer(child)) {
+ throw path.buildCodeFrameError(`Expected a child of ` +
+ `type JSXExpressionContainer under JSX Style tag ` +
+ `(eg: ), got ${child.type}`)
}
+
+ const expression = child.expression
+
+ if (!t.isTemplateLiteral(child.expression) &&
+ !t.isStringLiteral(child.expression)) {
+ throw path.buildCodeFrameError(`Expected a template ` +
+ `literal or String literal as the child of the ` +
+ `JSX Style tag (eg: ),` +
+ ` but got ${expression.type}`)
+ }
+
+ const styleText = getExpressionText(expression)
+ const styleId = String(hash(styleText))
+
+ state.styles.push([
+ styleId,
+ styleText,
+ expression.loc
+ ])
}
+
+ state.jsxId += hash(state.styles.map(s => s[1]).join(''))
+ state.hasJSXStyle = true
+ state.file.hasJSXStyle = true
+ // next visit will be: JSXOpeningElement
},
exit(path, state) {
if (state.hasJSXStyle && !--state.ignoreClosing) {
state.hasJSXStyle = null
state.skipTransform = false
}
+
+ if (!state.hasJSXStyle) {
+ return
+ }
+
+ const el = path.node.openingElement
+
+ if (!el.name || el.name.name !== 'style') {
+ return
+ }
+
+ // we replace styles with the function call
+ const [id, css, loc] = state.styles.shift()
+
+ const skipTransform = el.attributes.some(attr => (
+ attr.name.name === GLOBAL_ATTRIBUTE
+ ))
+
+ if (skipTransform) {
+ path.replaceWith(makeStyledJsxTag(css))
+ return
+ }
+
+ const useSourceMaps = Boolean(state.file.opts.sourceMaps)
+ let transformedCss
+
+ if (useSourceMaps) {
+ const filename = state.file.opts.sourceFileName
+ const generator = new SourceMapGenerator({
+ file: filename,
+ sourceRoot: state.file.opts.sourceRoot
+ })
+ generator.setSourceContent(filename, state.file.code)
+ transformedCss = [
+ transform(id, css, generator, loc.start, filename),
+ convert
+ .fromObject(generator)
+ .toComment({multiline: true}),
+ `/*@ sourceURL=${filename} */`
+ ].join('\n')
+ } else {
+ transformedCss = transform(id, css)
+ }
+
+ path.replaceWith(makeStyledJsxTag(transformedCss))
}
},
Program: {
enter(path, state) {
+ state.hasJSXStyle = null
+ state.ignoreClosing = null
state.file.hasJSXStyle = false
},
exit({node, scope}, state) {