Skip to content

Commit d233c6c

Browse files
author
David Sancho
authored
Merge pull request #16 from ephetic/feat_styled-components-support
[Feat] support styled-components
2 parents a2a82cb + 6fcce8d commit d233c6c

File tree

7 files changed

+261
-27
lines changed

7 files changed

+261
-27
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"babel-preset-es2015": "^6.24.1",
3131
"babel-preset-react": "^6.24.1",
3232
"babel-preset-stage-0": "^6.24.1",
33+
"babel-types": "^6.26.0",
3334
"husky": "^1.1.2",
3435
"mocha": "^3.1.2",
3536
"semantic-release": "^15.10.3",

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import checkValidOptions from './options'
2+
import TaggedTemplateExpression from './styled-components'
23

34
function isReactFragment (openingElement) {
45
return (
@@ -44,6 +45,7 @@ function functionBodyPushAttributes (t, path, options, componentName) {
4445
export default function ({types: t}) {
4546
return {
4647
visitor: {
48+
TaggedTemplateExpression,
4749
FunctionDeclaration (path, state) {
4850
if (!path.node.id || !path.node.id.name) return;
4951

src/styled-components.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import * as _path from 'path'
2+
import * as t from 'babel-types'
3+
4+
import checkValidOptions from './options'
5+
6+
export default function TaggedTemplateExpression(path, state) {
7+
const options = checkValidOptions(state)
8+
const scope = path.scope
9+
10+
try {
11+
// simple case
12+
if (isStyledPrefix(path.node.tag)) {
13+
const id = getIdFrom(path.parentPath)
14+
if (!id) return
15+
path.node.tag = insertBefore(path.node.tag, id)
16+
return
17+
}
18+
19+
// chained case. traverse until prefix found.
20+
// NB: styled-component chain api is always CallExpression+MemberExpression pairs.
21+
let node = path.node.tag
22+
while (true) {
23+
if (!node || !node.callee) break
24+
if (isStyledPrefix(node.callee.object)) {
25+
const id = getIdFrom(path.parentPath)
26+
if (!id) return
27+
node.callee.object = insertBefore(node.callee.object, id)
28+
break
29+
}
30+
node = node.callee.object
31+
}
32+
} catch (e) {}
33+
34+
return
35+
36+
// hoisted helpers in closure
37+
function insertBefore(node, id) {
38+
return t.callExpression(t.memberExpression(node, t.identifier('attrs')), [
39+
t.objectExpression([
40+
t.objectProperty(t.StringLiteral(options.attribute), t.StringLiteral(options.format(id)))
41+
])
42+
])
43+
}
44+
function isStyledPrefix(node) {
45+
// handle two forms: styled.div and styled(Comp)
46+
return (
47+
(t.isMemberExpression(node) && isStyledComponentsBinding(node.object)) ||
48+
(t.isCallExpression(node) && isStyledComponentsBinding(node.callee))
49+
)
50+
function isStyledComponentsBinding(node) {
51+
if (!t.isIdentifier(node)) return false
52+
const binding = scope.getBinding(node.name)
53+
if (!binding || binding.kind !== 'module') return false
54+
return binding.path.parent.source.value == 'styled-components'
55+
}
56+
}
57+
function getIdFrom(parentPath) {
58+
if (t.isVariableDeclarator(parentPath.node)) {
59+
return parentPath.node.id.name
60+
}
61+
if (t.isArrowFunctionExpression(parentPath.node)) {
62+
if (t.isVariableDeclarator(parentPath.parentPath.node)) {
63+
return parentPath.parentPath.node.id.name
64+
}
65+
}
66+
if (t.isExportDefaultDeclaration(parentPath.node)) {
67+
const path = state.file.opts.filename
68+
const filename = _path.parse(path).name
69+
if (filename === 'index') {
70+
const parent = _path.basename(_path.dirname(filename))
71+
return parent
72+
}
73+
return filename
74+
}
75+
}
76+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import React, { Component } from 'react'
2+
import styled from 'styled-components'
3+
4+
/* should be transformed */
5+
6+
const basic = styled.div`
7+
color: black;
8+
`
9+
10+
const withChain = styled.div.withConfig()`
11+
color: black;
12+
`
13+
14+
const withInterpolation = styled.div`
15+
color: ${Colors.black};
16+
`
17+
18+
const el = styled(El)`
19+
color: black;
20+
`
21+
22+
const arrowExpr = () => styled.div`
23+
color: black;
24+
`
25+
26+
// covered by VariableDeclaraion cases
27+
export const exported = styled.div`
28+
color: black;
29+
`
30+
31+
export default styled.div`
32+
color: black;
33+
`
34+
35+
/* should not be transformed */
36+
37+
let assigned
38+
assigned = styled.div`
39+
color: black;
40+
`
41+
42+
const arrowReturn = () => {
43+
return styled.div`
44+
color: black;
45+
`
46+
}
47+
48+
function fnReturn(){
49+
return styled.div`
50+
color: black;
51+
`
52+
}
53+
54+
const fnExprReturn = function(){
55+
return styled.div`
56+
color: black;
57+
`
58+
}
59+
60+
const namedFnExprReturn = function namedFnExpr(){
61+
return styled.div`
62+
color: black;
63+
`
64+
}
65+
66+
const ternary = cond ? styled.div`color: black;` : null
67+
68+
const conditional = cond && styled.div`color: black;`
69+
70+
const map = {
71+
comp: styled.div`color: black;`
72+
}
73+
74+
const comps = [styled.div`color: black;`]
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import React, { Component } from 'react';
2+
import styled from 'styled-components';
3+
4+
/* should be transformed */
5+
6+
const basic = styled.div.attrs({
7+
'data-qa': 'basic'
8+
})`
9+
color: black;
10+
`;
11+
12+
const withChain = styled.div.attrs({
13+
'data-qa': 'with-chain'
14+
}).withConfig()`
15+
color: black;
16+
`;
17+
18+
const withInterpolation = styled.div.attrs({
19+
'data-qa': 'with-interpolation'
20+
})`
21+
color: ${Colors.black};
22+
`;
23+
24+
const el = styled(El).attrs({
25+
'data-qa': 'el'
26+
})`
27+
color: black;
28+
`;
29+
30+
const arrowExpr = () => styled.div.attrs({
31+
'data-qa': 'arrow-expr'
32+
})`
33+
color: black;
34+
`;
35+
36+
// covered by VariableDeclaraion cases
37+
export const exported = styled.div.attrs({
38+
'data-qa': 'exported'
39+
})`
40+
color: black;
41+
`;
42+
43+
export default styled.div.attrs({
44+
'data-qa': 'actual'
45+
})`
46+
color: black;
47+
`;
48+
49+
/* should not be transformed */
50+
51+
let assigned;
52+
assigned = styled.div`
53+
color: black;
54+
`;
55+
56+
const arrowReturn = () => {
57+
return styled.div`
58+
color: black;
59+
`;
60+
};
61+
62+
function fnReturn() {
63+
return styled.div`
64+
color: black;
65+
`;
66+
}
67+
68+
const fnExprReturn = function () {
69+
return styled.div`
70+
color: black;
71+
`;
72+
};
73+
74+
const namedFnExprReturn = function namedFnExpr() {
75+
return styled.div`
76+
color: black;
77+
`;
78+
};
79+
80+
const ternary = cond ? styled.div`color: black;` : null;
81+
82+
const conditional = cond && styled.div`color: black;`;
83+
84+
const map = {
85+
comp: styled.div`color: black;`
86+
};
87+
88+
const comps = [styled.div`color: black;`];
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"plugins": [
3+
"syntax-jsx",
4+
["../../../../src"]
5+
]
6+
}

yarn.lock

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,7 +1564,7 @@ babel-traverse@^6.24.1, babel-traverse@^6.26.0:
15641564
invariant "^2.2.2"
15651565
lodash "^4.17.4"
15661566

1567-
babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0:
1567+
babel-types@^6.19.0, babel-types@^6.24.1:
15681568
version "6.26.0"
15691569
resolved "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
15701570
dependencies:
@@ -1573,6 +1573,15 @@ babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0:
15731573
lodash "^4.17.4"
15741574
to-fast-properties "^1.0.3"
15751575

1576+
babel-types@^6.26.0:
1577+
version "6.26.0"
1578+
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
1579+
dependencies:
1580+
babel-runtime "^6.26.0"
1581+
esutils "^2.0.2"
1582+
lodash "^4.17.4"
1583+
to-fast-properties "^1.0.3"
1584+
15761585
babylon@^6.18.0:
15771586
version "6.18.0"
15781587
resolved "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
@@ -2200,7 +2209,7 @@ debug@^4.0.0:
22002209
dependencies:
22012210
ms "^2.1.1"
22022211

2203-
debuglog@*, debuglog@^1.0.1:
2212+
debuglog@^1.0.1:
22042213
version "1.0.1"
22052214
resolved "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
22062215

@@ -3095,7 +3104,7 @@ import-lazy@^2.1.0:
30953104
version "2.1.0"
30963105
resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
30973106

3098-
imurmurhash@*, imurmurhash@^0.1.4:
3107+
imurmurhash@^0.1.4:
30993108
version "0.1.4"
31003109
resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
31013110

@@ -3649,36 +3658,18 @@ lodash._basecreate@^3.0.0:
36493658
version "3.0.3"
36503659
resolved "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821"
36513660

3652-
lodash._baseindexof@*:
3653-
version "3.1.0"
3654-
resolved "https://registry.npmjs.org/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"
3655-
36563661
lodash._baseuniq@~4.6.0:
36573662
version "4.6.0"
36583663
resolved "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
36593664
dependencies:
36603665
lodash._createset "~4.0.0"
36613666
lodash._root "~3.0.0"
36623667

3663-
lodash._bindcallback@*:
3664-
version "3.0.1"
3665-
resolved "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
3666-
3667-
lodash._cacheindexof@*:
3668-
version "3.0.2"
3669-
resolved "https://registry.npmjs.org/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"
3670-
3671-
lodash._createcache@*:
3672-
version "3.1.2"
3673-
resolved "https://registry.npmjs.org/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
3674-
dependencies:
3675-
lodash._getnative "^3.0.0"
3676-
36773668
lodash._createset@~4.0.0:
36783669
version "4.0.3"
36793670
resolved "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
36803671

3681-
lodash._getnative@*, lodash._getnative@^3.0.0:
3672+
lodash._getnative@^3.0.0:
36823673
version "3.9.1"
36833674
resolved "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
36843675

@@ -3746,10 +3737,6 @@ lodash.keys@^3.0.0:
37463737
lodash.isarguments "^3.0.0"
37473738
lodash.isarray "^3.0.0"
37483739

3749-
lodash.restparam@*:
3750-
version "3.6.1"
3751-
resolved "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
3752-
37533740
lodash.snakecase@^4.1.1:
37543741
version "4.1.1"
37553742
resolved "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d"
@@ -5029,7 +5016,7 @@ readable-stream@~1.1.10:
50295016
isarray "0.0.1"
50305017
string_decoder "~0.10.x"
50315018

5032-
readdir-scoped-modules@*, readdir-scoped-modules@^1.0.0:
5019+
readdir-scoped-modules@^1.0.0:
50335020
version "1.0.2"
50345021
resolved "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747"
50355022
dependencies:

0 commit comments

Comments
 (0)