From 8ce869b8b013766234ebffa7d4c69bef7b04f5b5 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 24 Nov 2016 16:39:30 +0100 Subject: [PATCH 1/4] Rewrite to the new, private API With the new private API, all the IIFEs are unnecessary since we can just pass an object to the styled() calls. This commit updates the plugin and the tests to reflect the new, much better behaviour. This way we can take full advantage of styled-components/styled-components#227 when it's merged! --- src/index.js | 120 +++++++++--------- src/utils/get-target.js | 21 +++ test/fixtures/add-display-names/after.js | 63 ++++----- test/fixtures/add-display-names/before.js | 3 +- .../add-identifier-and-display-name/after.js | 64 +++++----- test/fixtures/add-identifier/after.js | 58 ++++----- 6 files changed, 175 insertions(+), 154 deletions(-) create mode 100644 src/utils/get-target.js diff --git a/src/index.js b/src/index.js index 4b2fa97..444861b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,14 +1,16 @@ import template from 'babel-template' import hash from './utils/hash' +import getTarget from './utils/get-target' -const buildNodeWithDisplayNameAndIdentifier = template(`(function() { var c = VALUE; c.identifier = IDENTIFIER; c.displayName = DISPLAYNAME; return c })()`) -const buildNodeWithDisplayName = template(`(function() { var c = VALUE; c.displayName = DISPLAYNAME; return c })()`) -const buildNodeWithIdentifier = template(`(function() { var c = VALUE; c.identifier = IDENTIFIER; return c })()`) +const buildStyledCall = template(`styled({ + target: TARGET, + displayName: DISPLAYNAME, + identifier: IDENTIFIER +})`) const isStyled = (tag) => (tag.object && tag.object.name == 'styled') || (tag.callee && tag.callee.name == 'styled') - let id = 0 export default function({ types: t }) { @@ -21,72 +23,70 @@ export default function({ types: t }) { const tag = path.node.tag if (!isStyled(tag)) return - if (path.node._styledComponentsSeen) { + if (path._styledComponentsSeen) { return } - let displayName + let displayName, target - if (addDisplayName) { - path.find((path) => { - if (path.isAssignmentExpression()) { - displayName = path.node.left - } else if (path.isObjectProperty()) { - displayName = path.node.key - } else if (path.isVariableDeclarator()) { - displayName = path.node.id - } else if (path.isStatement()) { - // we've hit a statement, we should stop crawling up - return true - } + path.find((path) => { + // const X = styled + if (path.isAssignmentExpression()) { + target = getTarget(path.node.right.tag) + displayName = path.node.left + // const X = { Y: styled } + } else if (path.isObjectProperty()) { + target = getTarget(path.node.value.tag) + displayName = path.node.key + // let X; X = styled + } else if (path.isVariableDeclarator()) { + target = getTarget(path.node.init.tag) + displayName = path.node.id + } else if (path.isStatement()) { + // we've hit a statement, we should stop crawling up + return true + } - // we've got an displayName! no need to continue - if (displayName) return true - }) + // we've got an displayName (if we need it) and a target! no need to continue + if ((!addDisplayName || displayName) && target) return true + }) - // ensure that we have an displayName we can inherit from - if (!displayName) return + // foo.bar -> bar + if (t.isMemberExpression(displayName)) { + displayName = displayName.property + } - // foo.bar -> bar - if (t.isMemberExpression(displayName)) { - displayName = displayName.property - } + // styled call without variable + if (!target) { + target = getTarget(path.node.tag) + } - // identifiers are the only thing we can reliably get a name from - if (!t.isIdentifier(displayName)) { - displayName = undefined - } else { - displayName = displayName.name - } - } + // identifiers are the only thing we can reliably get a name from + if (!t.isIdentifier(displayName)) { + displayName = undefined + } else { + displayName = displayName.name + } - let newNode - if (addIdentifier) { - id++ - // Prefix the identifier with a character if no displayName exists because CSS classes cannot start with a number - const identifier = `${displayName || 's'}-${hash(`${id}${displayName}`)}` - if (!addDisplayName) { - newNode = buildNodeWithIdentifier({ - VALUE: path.node, - IDENTIFIER: t.stringLiteral(identifier), - }) - } else { - newNode = buildNodeWithDisplayNameAndIdentifier({ - VALUE: path.node, - DISPLAYNAME: t.stringLiteral(displayName), - IDENTIFIER: t.stringLiteral(identifier), - }) - } - } else if (addDisplayName) { - newNode = buildNodeWithDisplayName({ - VALUE: path.node, - DISPLAYNAME: t.stringLiteral(displayName), - }) - } - path.node._styledComponentsSeen = true + id++ + // Prefix the identifier with a character if no displayName exists because CSS classes cannot start with a number + const identifier = `${displayName || 's'}-${hash(`${id}${displayName}`)}` + // Put together the final code again + // Create the styled({ }) call + const call = buildStyledCall({ + TARGET: target, + DISPLAYNAME: (addDisplayName && t.stringLiteral(displayName)) || t.identifier('undefined'), + IDENTIFIER: (addIdentifier && t.stringLiteral(identifier)) || t.identifier('undefined') + }) + // Put together the styled call with the template literal + // to get the finished styled({ })`` form! 🎉 + const styledCall = t.taggedTemplateExpression( + call.expression, + path.node.quasi + ) + path._styledComponentsSeen = true - if (!newNode) return - path.replaceWith(newNode) + path.replaceWith(styledCall) } } } diff --git a/src/utils/get-target.js b/src/utils/get-target.js new file mode 100644 index 0000000..ac328d8 --- /dev/null +++ b/src/utils/get-target.js @@ -0,0 +1,21 @@ +import * as t from 'babel-types' + +/** + * Get the target (i.e. tagname or component variable) from a styled call + * + * @param {Node} node + * + * @return {String} The target + */ +const getTarget = (node) => { + // styled.div`` => "div" + if (t.isMemberExpression(node)) { + return t.stringLiteral(node.property.name) + } + // styled(Bla) => Bla + if (t.isCallExpression(node)) { + return node.arguments[0] + } +} + +export default getTarget diff --git a/test/fixtures/add-display-names/after.js b/test/fixtures/add-display-names/after.js index 7994cda..a2ff3cc 100644 --- a/test/fixtures/add-display-names/after.js +++ b/test/fixtures/add-display-names/after.js @@ -1,30 +1,35 @@ -const Test = function () { - var c = styled.div` width: 100%;`; - c.displayName = "Test"; - return c; -}(); -const Test2 = true ? function () { - var c = styled.div``; - c.displayName = "Test2"; - return c; -}() : function () { - var c = styled.div``; - c.displayName = "Test2"; - return c; -}(); -const styles = { One: function () { - var c = styled.div``; - c.displayName = "One"; - return c; - }() }; +const Test = styled({ + target: 'div', + displayName: 'Test', + identifier: undefined +})` width: 100%;`; +const Test2 = styled({ + target: 'div', + displayName: 'Test2', + identifier: undefined +})``; +const Test3 = true ? styled({ + target: 'div', + displayName: 'Test3', + identifier: undefined +})`` : styled({ + target: 'div', + displayName: 'Test3', + identifier: undefined +})``; +const styles = { One: styled({ + target: 'div', + displayName: 'One', + identifier: undefined + })`` }; let Component; -Component = function () { - var c = styled.div``; - c.displayName = "Component"; - return c; -}(); -const WrappedComponent = function () { - var c = styled(Inner)``; - c.displayName = "WrappedComponent"; - return c; -}(); +Component = styled({ + target: 'div', + displayName: 'Component', + identifier: undefined +})``; +const WrappedComponent = styled({ + target: Inner, + displayName: 'WrappedComponent', + identifier: undefined +})``; diff --git a/test/fixtures/add-display-names/before.js b/test/fixtures/add-display-names/before.js index 76a1998..f18524e 100644 --- a/test/fixtures/add-display-names/before.js +++ b/test/fixtures/add-display-names/before.js @@ -1,5 +1,6 @@ const Test = styled.div` width: 100%;`; -const Test2 = true ? styled.div`` : styled.div``; +const Test2 = styled('div')``; +const Test3 = true ? styled.div`` : styled.div``; const styles = { One: styled.div`` } let Component; Component = styled.div``; diff --git a/test/fixtures/add-identifier-and-display-name/after.js b/test/fixtures/add-identifier-and-display-name/after.js index ecfcef0..66077f0 100644 --- a/test/fixtures/add-identifier-and-display-name/after.js +++ b/test/fixtures/add-identifier-and-display-name/after.js @@ -1,36 +1,30 @@ -const Test = function () { - var c = styled.div` width: 100%;`; - c.identifier = "Test-1s574it"; - c.displayName = "Test"; - return c; -}(); -const Test2 = true ? function () { - var c = styled.div``; - c.identifier = "Test2-ibltve"; - c.displayName = "Test2"; - return c; -}() : function () { - var c = styled.div``; - c.identifier = "Test2-bjbly"; - c.displayName = "Test2"; - return c; -}(); -const styles = { One: function () { - var c = styled.div``; - c.identifier = "One-1b6bjft"; - c.displayName = "One"; - return c; - }() }; +const Test = styled({ + target: "div", + displayName: "Test", + identifier: "Test-18z24ew" +})` width: 100%;`; +const Test2 = true ? styled({ + target: "div", + displayName: "Test2", + identifier: "Test2-1hn2b5r" +})`` : styled({ + target: "div", + displayName: "Test2", + identifier: "Test2-1hl3gfs" +})``; +const styles = { One: styled({ + target: "div", + displayName: "One", + identifier: "One-1r8uh7y" + })`` }; let Component; -Component = function () { - var c = styled.div``; - c.identifier = "Component-1ww18aw"; - c.displayName = "Component"; - return c; -}(); -const WrappedComponent = function () { - var c = styled(Inner)``; - c.identifier = "WrappedComponent-1o6g4hl"; - c.displayName = "WrappedComponent"; - return c; -}(); +Component = styled({ + target: "div", + displayName: "Component", + identifier: "Component-1e47qp" +})``; +const WrappedComponent = styled({ + target: Inner, + displayName: "WrappedComponent", + identifier: "WrappedComponent-kam0ab" +})``; diff --git a/test/fixtures/add-identifier/after.js b/test/fixtures/add-identifier/after.js index fa8dc34..22e01b1 100644 --- a/test/fixtures/add-identifier/after.js +++ b/test/fixtures/add-identifier/after.js @@ -1,30 +1,30 @@ -const Test = function () { - var c = styled.div` width: 100%;`; - c.identifier = "s-14xequy"; - return c; -}(); -const Test2 = true ? function () { - var c = styled.div``; - c.identifier = "s-18nhwt3"; - return c; -}() : function () { - var c = styled.div``; - c.identifier = "s-wi0iju"; - return c; -}(); -const styles = { One: function () { - var c = styled.div``; - c.identifier = "s-1d4mzhe"; - return c; - }() }; +const Test = styled({ + target: "div", + displayName: undefined, + identifier: "Test-137hlza" +})` width: 100%;`; +const Test2 = true ? styled({ + target: "div", + displayName: undefined, + identifier: "Test2-bjbly" +})`` : styled({ + target: "div", + displayName: undefined, + identifier: "Test2-3rtkrg" +})``; +const styles = { One: styled({ + target: "div", + displayName: undefined, + identifier: "One-1sf086v" + })`` }; let Component; -Component = function () { - var c = styled.div``; - c.identifier = "s-1qb5dq3"; - return c; -}(); -const WrappedComponent = function () { - var c = styled(Inner)``; - c.identifier = "s-15ghyhk"; - return c; -}(); +Component = styled({ + target: "div", + displayName: undefined, + identifier: "Component-1uo0y5n" +})``; +const WrappedComponent = styled({ + target: Inner, + displayName: undefined, + identifier: "WrappedComponent-1hk71jk" +})``; From fe17962adbbbd89121cab700120000bd11dd2702 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 24 Nov 2016 16:43:43 +0100 Subject: [PATCH 2/4] Fix indentation --- src/index.js | 62 ++++++++++++++++++++--------------------- src/utils/get-target.js | 16 +++++------ 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/index.js b/src/index.js index 444861b..c080c44 100644 --- a/src/index.js +++ b/src/index.js @@ -4,9 +4,9 @@ import hash from './utils/hash' import getTarget from './utils/get-target' const buildStyledCall = template(`styled({ - target: TARGET, - displayName: DISPLAYNAME, - identifier: IDENTIFIER + target: TARGET, + displayName: DISPLAYNAME, + identifier: IDENTIFIER })`) const isStyled = (tag) => (tag.object && tag.object.name == 'styled') || (tag.callee && tag.callee.name == 'styled') @@ -19,7 +19,7 @@ export default function({ types: t }) { TaggedTemplateExpression: { enter(path, { opts }) { const addDisplayName = (opts.displayName === undefined || opts.displayName === null) ? true : opts.displayName - const addIdentifier = (opts.ssr === undefined || opts.ssr === null) ? true : opts.ssr + const addIdentifier = (opts.ssr === undefined || opts.ssr === null) ? true : opts.ssr const tag = path.node.tag if (!isStyled(tag)) return @@ -30,17 +30,17 @@ export default function({ types: t }) { let displayName, target path.find((path) => { - // const X = styled + // const X = styled if (path.isAssignmentExpression()) { target = getTarget(path.node.right.tag) displayName = path.node.left - // const X = { Y: styled } + // const X = { Y: styled } } else if (path.isObjectProperty()) { - target = getTarget(path.node.value.tag) + target = getTarget(path.node.value.tag) displayName = path.node.key - // let X; X = styled + // let X; X = styled } else if (path.isVariableDeclarator()) { - target = getTarget(path.node.init.tag) + target = getTarget(path.node.init.tag) displayName = path.node.id } else if (path.isStatement()) { // we've hit a statement, we should stop crawling up @@ -56,34 +56,34 @@ export default function({ types: t }) { displayName = displayName.property } - // styled call without variable - if (!target) { - target = getTarget(path.node.tag) - } + // styled call without variable + if (!target) { + target = getTarget(path.node.tag) + } // identifiers are the only thing we can reliably get a name from if (!t.isIdentifier(displayName)) { displayName = undefined } else { - displayName = displayName.name - } + displayName = displayName.name + } - id++ - // Prefix the identifier with a character if no displayName exists because CSS classes cannot start with a number - const identifier = `${displayName || 's'}-${hash(`${id}${displayName}`)}` - // Put together the final code again - // Create the styled({ }) call - const call = buildStyledCall({ - TARGET: target, - DISPLAYNAME: (addDisplayName && t.stringLiteral(displayName)) || t.identifier('undefined'), - IDENTIFIER: (addIdentifier && t.stringLiteral(identifier)) || t.identifier('undefined') - }) - // Put together the styled call with the template literal - // to get the finished styled({ })`` form! 🎉 - const styledCall = t.taggedTemplateExpression( - call.expression, - path.node.quasi - ) + id++ + // Prefix the identifier with a character if no displayName exists because CSS classes cannot start with a number + const identifier = `${displayName || 's'}-${hash(`${id}${displayName}`)}` + // Put together the final code again + // Create the styled({ }) call + const call = buildStyledCall({ + TARGET: target, + DISPLAYNAME: (addDisplayName && t.stringLiteral(displayName)) || t.identifier('undefined'), + IDENTIFIER: (addIdentifier && t.stringLiteral(identifier)) || t.identifier('undefined') + }) + // Put together the styled call with the template literal + // to get the finished styled({ })`` form! 🎉 + const styledCall = t.taggedTemplateExpression( + call.expression, + path.node.quasi + ) path._styledComponentsSeen = true path.replaceWith(styledCall) diff --git a/src/utils/get-target.js b/src/utils/get-target.js index ac328d8..f98ebbf 100644 --- a/src/utils/get-target.js +++ b/src/utils/get-target.js @@ -8,14 +8,14 @@ import * as t from 'babel-types' * @return {String} The target */ const getTarget = (node) => { - // styled.div`` => "div" - if (t.isMemberExpression(node)) { - return t.stringLiteral(node.property.name) - } - // styled(Bla) => Bla - if (t.isCallExpression(node)) { - return node.arguments[0] - } + // styled.div`` => "div" + if (t.isMemberExpression(node)) { + return t.stringLiteral(node.property.name) + } + // styled(Bla) => Bla + if (t.isCallExpression(node)) { + return node.arguments[0] + } } export default getTarget From 4b7fca957acce82ede117d44c054968921925f5d Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 26 Nov 2016 11:41:52 +0100 Subject: [PATCH 3/4] Fix target determination, much easier --- src/index.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/index.js b/src/index.js index c080c44..0036869 100644 --- a/src/index.js +++ b/src/index.js @@ -27,28 +27,25 @@ export default function({ types: t }) { return } - let displayName, target + let displayName path.find((path) => { // const X = styled if (path.isAssignmentExpression()) { - target = getTarget(path.node.right.tag) displayName = path.node.left // const X = { Y: styled } } else if (path.isObjectProperty()) { - target = getTarget(path.node.value.tag) displayName = path.node.key // let X; X = styled } else if (path.isVariableDeclarator()) { - target = getTarget(path.node.init.tag) displayName = path.node.id } else if (path.isStatement()) { // we've hit a statement, we should stop crawling up return true } - // we've got an displayName (if we need it) and a target! no need to continue - if ((!addDisplayName || displayName) && target) return true + // we've got an displayName (if we need it) no need to continue + if (displayName) return true }) // foo.bar -> bar @@ -56,10 +53,8 @@ export default function({ types: t }) { displayName = displayName.property } - // styled call without variable - if (!target) { - target = getTarget(path.node.tag) - } + // Get target + const target = getTarget(path.node.tag) // identifiers are the only thing we can reliably get a name from if (!t.isIdentifier(displayName)) { From cf47736657c8da4b32be8e6905d6eb0e02f2345a Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 26 Nov 2016 11:42:54 +0100 Subject: [PATCH 4/4] Simplify even further --- src/index.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/index.js b/src/index.js index 0036869..564bc44 100644 --- a/src/index.js +++ b/src/index.js @@ -23,9 +23,6 @@ export default function({ types: t }) { const tag = path.node.tag if (!isStyled(tag)) return - if (path._styledComponentsSeen) { - return - } let displayName @@ -75,13 +72,7 @@ export default function({ types: t }) { }) // Put together the styled call with the template literal // to get the finished styled({ })`` form! 🎉 - const styledCall = t.taggedTemplateExpression( - call.expression, - path.node.quasi - ) - path._styledComponentsSeen = true - - path.replaceWith(styledCall) + path.node.tag = call.expression } } }