Skip to content

Instantly share code, notes, and snippets.

@jcarroll2007
Last active July 11, 2019 16:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcarroll2007/8d1797d7d108118ca7b733ad0b8119e8 to your computer and use it in GitHub Desktop.
Save jcarroll2007/8d1797d7d108118ca7b733ad0b8119e8 to your computer and use it in GitHub Desktop.
A codemod to transform styled-components attrs first argument into a function from an object.

Overview

This codemod is for fixing one of the deprecated features of styled-components in v4. Namely, number 7 mentioned here - https://www.styled-components.com/docs/faqs#what-do-i-need-to-do-to-migrate-to-v4.

Styled Components has deprecated the ability to pass in an object as an argument to the .attrs function.

🚫

import styled from 'styled-components'

const Input = styled.input.attrs({
  type: props => props.inputType,
})`
  background: blue;
  color: red;
`

import styled from 'styled-components'

const Input = styled.input.attrs(props => ({
  type: props.inputType,
}))`
  background: blue;
  color: red;
`

Getting Started

  1. yarn global add jscodeshift
  2. Create a file in the root of your project (really whereever you would like) called transforms/attrsObjectToFunction.js and paste the codemod into the file.
  3. run in the root of your project jscodeshift -t transform/attrsObjectToFunction.js src (or wherever your source is)

Limitations

This codemod isn't 100% accurate. So watch out for the following:

1. Other .attr functions

This codemod does not verify that any call to a function called .attrs is actually a Styled object or call off of the Styled object. So make sure to manually revert any .attrs calls that may have been changed that are not a part of Styled objects.

2. Complex attr object properties

All this codemod does is it takes the first arg of each .attrs call, checks to make sure it's an object, and then wraps that exact object in an arrow function with no parameters. A smarter codemod might check the object to see if there are any properties on that object that are functions to modify them as well, but this doesn't support that right now. See the example below for more information.

Example

In the example below the codemod would simply wrap that exact object in a parameterless arrow function which would still be invalid becasue the color attr would need to be changed from a function to use props passed into the new attrs function. You will need to manually catch all of the situations and fix them yourself.

export const MyStyledDiv = styled.div.attrs({
  color: props => props.hasError ? 'red' : 'green'
})

Codemod Wrong Output

export const MyStyledDiv = styled.div.attrs(() => ({
  color: props => props.hasError ? 'red' : 'green'
}))

Correct Output

export const MyStyledDiv = styled.div.attrs(props => ({
  color: props.hasError ? 'red' : 'green'
}))

export default function transformer(file, api) {
const j = api.jscodeshift
const root = j(file.source)
const attrCalls = root.find(j.CallExpression, {
callee: {
property: {
name: 'attrs'
}
}
})
attrCalls.forEach(p => {
const firstArg = p.value.arguments[0]
if (firstArg.type === 'ObjectExpression') {
const objArg = p.value.arguments.pop()
p.value.arguments.push(j.arrowFunctionExpression([], objArg))
}
})
return root.toSource()
}
@joshuaalpuerto
Copy link

Please forgive me but I don't see any different between the wrong one and right one

import styled from 'styled-components'

const Input = styled.input.attrs(props => ({
  type: props.inputType,
}))`
  background: blue;
  color: red;
`

vs

import styled from 'styled-components'

const Input = styled.input.attrs(({ inputType }) => ({
  type: inputType,
}))`
  background: blue;
  color: red;

The only difference i'm seeing is you destructure the props on the correct one.

@jcarroll2007
Copy link
Author

@joshuaalpuerto

In my example in the Readme

Codemod Wrong Output

export const MyStyledDiv = styled.div.attrs(() => ({
color: props => props.hasError ? 'red' : 'green'
}))

Correct Output

export const MyStyledDiv = styled.div.attrs(props => ({
color: props.hasError ? 'red' : 'green'
}))

Notice the first (wrong) example does not have any arguments being passed in however the correct one (the second example) does define props as the first argument.

Your example you provided is not from my Readme. Not sure where it's from.

@joshuaalpuerto
Copy link

Sorry for the confusion but it was actually the top part of the read me. Just after this word

Styled Components has deprecated the ability to pass in an object as an argument to the .attrs function.

@jcarroll2007
Copy link
Author

jcarroll2007 commented Jul 10, 2019

@joshuaalpuerto My apologies! You're correct. I actually copied that directly from the styled-components upgrade guide. I will fix the example. Good call!

It should be

🚫

import styled from 'styled-components'

const Input = styled.input.attrs({
  type: props => props.inputType,
})`
  background: blue;
  color: red;
`

import styled from 'styled-components'

const Input = styled.input.attrs(props => ({
  type: props.inputType,
}))`
  background: blue;
  color: red;
`

@joshuaalpuerto
Copy link

No worries man! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment