Repository: styled-components/babel-plugin-styled-components
Pull request #13 (2016-12-04 10:21:46)

Move code around, reduce output.
@mxstbr take a note of changed output. It might require changes in styled-components code, but I doubt it.

As we are going to minify CSS, I'd start with this changes.


Patch:
diff --git a/package.json b/package.json
index 29fbabd..39c49e6 100644
--- a/package.json
+++ b/package.json
@@ -28,8 +28,5 @@
     "server-side rendering",
     "ssr",
     "displayName"
-  ],
-  "dependencies": {
-    "babel-template": "^6.16.0"
-  }
+  ]
 }
diff --git a/src/index.js b/src/index.js
index 93521d8..37c2958 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,13 +1,16 @@
-import template from 'babel-template'
-
 import hash from './utils/hash'
 import getTarget from './utils/get-target'
+import getName from './utils/get-name'
+
+const blockName = (file) => {
+  return file.opts.basename !== 'index' ?
+    file.opts.basename :
+    path.basename(path.dirname(file.opts.filename))
+}
 
-const buildStyledCall = template(`STYLED({
-  target: TARGET,
-  displayName: DISPLAYNAME,
-  identifier: IDENTIFIER
-})`)
+const getOption = (opts, name, defaultValue = true) => {
+  return opts[name] === undefined || opts[name] === null ? defaultValue : opts[name]
+}
 
 let id = 0
 
@@ -36,73 +39,55 @@ export default function({ types: t }) {
           }
         })
 
-        path.traverse({
-          TaggedTemplateExpression(path, { opts, file }) {
-            const addDisplayName = (opts.displayName === undefined || opts.displayName === null) ? true : opts.displayName
-            const addIdentifier = (opts.ssr === undefined || opts.ssr === null) ? true : opts.ssr
-            const useFileName = (opts.fileName === undefined || opts.fileName === null) ? true : opts.fileName
+        const options = {
+          displayName: getOption(state.opts, 'displayName'),
+          ssr: getOption(state.opts, 'ssr'),
+          fileName: getOption(state.opts, 'fileName'),
+        }
+
+        if (!options.ssr && !options.displayName) {
+          return
+        }
 
+        path.traverse({
+          TaggedTemplateExpression(path, { file }) {
             const tag = path.node.tag
 
             if (!isStyled(tag)) return
 
-            let namedNode
-
-            path.find((path) => {
-              // const X = styled
-              if (path.isAssignmentExpression()) {
-                namedNode = path.node.left
-              // const X = { Y: styled }
-              } else if (path.isObjectProperty()) {
-                namedNode = path.node.key
-              // let X; X = styled
-              } else if (path.isVariableDeclarator()) {
-                namedNode = 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) no need to continue
-              if (namedNode) return true
-            })
-
-            // foo.bar -> bar
-            if (t.isMemberExpression(namedNode)) {
-              namedNode = namedNode.property
-            }
-
             // Get target
             const target = getTarget(path.node.tag)
 
-            // identifiers are the only thing we can reliably get a name from
-            const componentName = t.isIdentifier(namedNode) ? namedNode.name : undefined
+            const componentName = getName(path)
 
             let displayName
-
-            if (!useFileName) {
-              displayName = componentName
+            if (options.fileName) {
+              displayName = componentName ? `${blockName(file)}__${componentName}` : blockName(file)
             } else {
-              let blockName = file.opts.basename
-              if (blockName === 'index') {
-                blockName = path.basename(path.dirname(file.opts.filename))
-              }
-              displayName = componentName ? `${blockName}__${namedNode.name}` : blockName
+              displayName = componentName
             }
 
             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
-            const call = buildStyledCall({
-              STYLED: t.identifier(importedVariableName),
-              TARGET: target,
-              DISPLAYNAME: (addDisplayName && displayName && t.stringLiteral(displayName)) || t.identifier('undefined'),
-              IDENTIFIER: (addIdentifier && identifier && t.stringLiteral(identifier)) || t.identifier('undefined')
-            })
+            const styledCallProps = [ t.objectProperty(t.identifier('target'), target) ]
+            if (options.displayName && displayName) {
+              styledCallProps.push(t.objectProperty(t.identifier('displayName'), t.stringLiteral(displayName)))
+            }
+            if (options.ssr && identifier) {
+              styledCallProps.push(t.objectProperty(t.identifier('identifier'), t.stringLiteral(identifier)))
+            }
+
+            const call = t.callExpression(
+              t.identifier(importedVariableName),
+              [ t.objectExpression(styledCallProps) ]
+            )
+
             // Put together the styled call with the template literal
             // to get the finished styled({ })`` form! 🎉
-            path.node.tag = call.expression
+            path.node.tag = call
           }
         }, state)
       }
diff --git a/src/utils/get-name.js b/src/utils/get-name.js
new file mode 100644
index 0000000..9b079a5
--- /dev/null
+++ b/src/utils/get-name.js
@@ -0,0 +1,40 @@
+import * as t from 'babel-types'
+
+/**
+ * Get the name of variable that contains node
+ *
+ * @param  {Path} path to the node
+ *
+ * @return {String}   The target
+ */
+
+export default (path) => {
+  let namedNode
+
+  path.find((path) => {
+    // const X = styled
+    if (path.isAssignmentExpression()) {
+      namedNode = path.node.left
+    // const X = { Y: styled }
+    } else if (path.isObjectProperty()) {
+      namedNode = path.node.key
+    // let X; X = styled
+    } else if (path.isVariableDeclarator()) {
+      namedNode = 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) no need to continue
+    if (namedNode) return true
+  })
+
+  // foo.bar -> bar
+  if (t.isMemberExpression(namedNode)) {
+    namedNode = namedNode.property
+  }
+
+  // identifiers are the only thing we can reliably get a name from
+  return t.isIdentifier(namedNode) ? namedNode.name : undefined
+}


Test patch:
diff --git a/test/fixtures/01-add-display-names/after.js b/test/fixtures/01-add-display-names/after.js
index a2ff3cc..bb30dc0 100644
--- a/test/fixtures/01-add-display-names/after.js
+++ b/test/fixtures/01-add-display-names/after.js
@@ -1,35 +1,28 @@
 const Test = styled({
   target: 'div',
-  displayName: 'Test',
-  identifier: undefined
+  displayName: 'Test'
 })` width: 100%;`;
 const Test2 = styled({
   target: 'div',
-  displayName: 'Test2',
-  identifier: undefined
+  displayName: 'Test2'
 })``;
 const Test3 = true ? styled({
   target: 'div',
-  displayName: 'Test3',
-  identifier: undefined
+  displayName: 'Test3'
 })`` : styled({
   target: 'div',
-  displayName: 'Test3',
-  identifier: undefined
+  displayName: 'Test3'
 })``;
 const styles = { One: styled({
     target: 'div',
-    displayName: 'One',
-    identifier: undefined
+    displayName: 'One'
   })`` };
 let Component;
 Component = styled({
   target: 'div',
-  displayName: 'Component',
-  identifier: undefined
+  displayName: 'Component'
 })``;
 const WrappedComponent = styled({
   target: Inner,
-  displayName: 'WrappedComponent',
-  identifier: undefined
+  displayName: 'WrappedComponent'
 })``;
diff --git a/test/fixtures/02-add-identifier/after.js b/test/fixtures/02-add-identifier/after.js
index 22e01b1..daadc8b 100644
--- a/test/fixtures/02-add-identifier/after.js
+++ b/test/fixtures/02-add-identifier/after.js
@@ -1,30 +1,24 @@
 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 = styled({
   target: "div",
-  displayName: undefined,
   identifier: "Component-1uo0y5n"
 })``;
 const WrappedComponent = styled({
   target: Inner,
-  displayName: undefined,
   identifier: "WrappedComponent-1hk71jk"
 })``;
diff --git a/test/fixtures/06-works-with-hoisted-default-as-import/.babelrc b/test/fixtures/06-work-with-hoisted-default-as-import/.babelrc
similarity index 100%
rename from test/fixtures/06-works-with-hoisted-default-as-import/.babelrc
rename to test/fixtures/06-work-with-hoisted-default-as-import/.babelrc
diff --git a/test/fixtures/06-works-with-hoisted-default-as-import/after.js b/test/fixtures/06-work-with-hoisted-default-as-import/after.js
similarity index 100%
rename from test/fixtures/06-works-with-hoisted-default-as-import/after.js
rename to test/fixtures/06-work-with-hoisted-default-as-import/after.js
diff --git a/test/fixtures/06-works-with-hoisted-default-as-import/before.js b/test/fixtures/06-work-with-hoisted-default-as-import/before.js
similarity index 100%
rename from test/fixtures/06-works-with-hoisted-default-as-import/before.js
rename to test/fixtures/06-work-with-hoisted-default-as-import/before.js
diff --git a/test/fixtures/07-not-use-private-api-if-not-required/.babelrc b/test/fixtures/07-not-use-private-api-if-not-required/.babelrc
new file mode 100644
index 0000000..d4c9c82
--- /dev/null
+++ b/test/fixtures/07-not-use-private-api-if-not-required/.babelrc
@@ -0,0 +1,8 @@
+{
+  "plugins": [
+    ["../../../src", {
+      "ssr": false,
+      "displayName": false
+    }]
+  ]
+}
diff --git a/test/fixtures/07-not-use-private-api-if-not-required/after.js b/test/fixtures/07-not-use-private-api-if-not-required/after.js
new file mode 100644
index 0000000..9fa94bb
--- /dev/null
+++ b/test/fixtures/07-not-use-private-api-if-not-required/after.js
@@ -0,0 +1,3 @@
+import styled from 'styled-components';
+
+const Test = styled.div`width: 100%;`;
diff --git a/test/fixtures/07-not-use-private-api-if-not-required/before.js b/test/fixtures/07-not-use-private-api-if-not-required/before.js
new file mode 100644
index 0000000..9fa94bb
--- /dev/null
+++ b/test/fixtures/07-not-use-private-api-if-not-required/before.js
@@ -0,0 +1,3 @@
+import styled from 'styled-components';
+
+const Test = styled.div`width: 100%;`;