Created
August 9, 2018 16:10
-
-
Save satya164/dc45a17b7bc74a144f57e1ea12509f59 to your computer and use it in GitHub Desktop.
aphrodite to styled-components codemod
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const dashify = text => text.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); | |
const unitless = { | |
animationIterationCount: true, | |
borderImageOutset: true, | |
borderImageSlice: true, | |
borderImageWidth: true, | |
boxFlex: true, | |
boxFlexGroup: true, | |
boxOrdinalGroup: true, | |
columnCount: true, | |
columns: true, | |
flex: true, | |
flexGrow: true, | |
flexPositive: true, | |
flexShrink: true, | |
flexNegative: true, | |
flexOrder: true, | |
gridRow: true, | |
gridRowEnd: true, | |
gridRowSpan: true, | |
gridRowStart: true, | |
gridColumn: true, | |
gridColumnEnd: true, | |
gridColumnSpan: true, | |
gridColumnStart: true, | |
fontWeight: true, | |
lineClamp: true, | |
lineHeight: true, | |
opacity: true, | |
order: true, | |
orphans: true, | |
tabSize: true, | |
widows: true, | |
zIndex: true, | |
zoom: true, | |
// SVG-related properties | |
fillOpacity: true, | |
floodOpacity: true, | |
stopOpacity: true, | |
strokeDasharray: true, | |
strokeDashoffset: true, | |
strokeMiterlimit: true, | |
strokeOpacity: true, | |
strokeWidth: true, | |
}; | |
export default function(babel) { | |
const { types: t } = babel; | |
return { | |
visitor: { | |
Program: { | |
enter(path, state) { | |
state.components = {}; | |
state.styled = {}; | |
}, | |
exit(path, state) { | |
Object.keys(state.styled).forEach(key => { | |
const name = Object.keys(state.components).find(k => { | |
const { classes } = state.components[k]; | |
return classes && classes.includes(key); | |
}); | |
if (name) { | |
const tag = state.components[name].id; | |
const node = t.variableDeclaration('const', [ | |
t.variableDeclarator( | |
t.identifier(name), | |
t.taggedTemplateExpression( | |
/^[a-z0-9]+$/.test(tag) | |
? t.memberExpression(t.identifier('styled'), t.identifier(tag)) | |
: t.callExpression(t.identifier('styled'), [t.identifier(tag)]), | |
state.styled[key] | |
) | |
), | |
]); | |
path.node.body.push(node); | |
} | |
}); | |
}, | |
}, | |
JSXOpeningElement(path, state) { | |
let name; | |
let classes; | |
path.traverse({ | |
JSXExpressionContainer(p) { | |
if (t.isCallExpression(p.node.expression) && p.node.expression.callee.name === 'css') { | |
if (name) { | |
throw new Error(`Found duplicate css calls in component ${path.node.name.name}`); | |
} | |
name = p.node.expression.arguments | |
.map(a => a.property.name.charAt(0).toUpperCase() + a.property.name.substr(1)) | |
.join(''); | |
classes = p.node.expression.arguments.map(a => a.property.name); | |
p.parentPath.remove(); | |
} | |
}, | |
}); | |
if (name) { | |
state.components[name] = { | |
id: path.node.name.name, | |
classes, | |
}; | |
path.node.name.name = name; | |
if (path.parentPath.node.closingElement) { | |
path.parentPath.node.closingElement.name.name = name; | |
} | |
} | |
}, | |
MemberExpression(path, state) { | |
if (path.node.object.name === 'StyleSheet' && path.node.property.name === 'create') { | |
path.parentPath.parentPath.node.init.arguments[0].properties.forEach(p => { | |
const quasis = []; | |
const expressions = []; | |
let text = ''; | |
const indentation = ' '; | |
const finalize = (expr, str) => { | |
quasis.push(t.templateElement({ raw: text })); | |
expressions.push(expr); | |
text = str; | |
}; | |
const serialize = (styles, level = 1) => { | |
const indent = indentation.repeat(level); | |
styles.forEach((prop, i) => { | |
if (t.isObjectExpression(prop.value)) { | |
if (i !== 0) { | |
text += '\n'; | |
} | |
if (prop.computed) { | |
text += `\n${indent}`; | |
finalize(prop.key, ' {'); | |
} else { | |
let key; | |
if (t.isIdentifier(prop.key)) { | |
key = prop.key.name; | |
} else { | |
key = prop.key.value; | |
} | |
text += `\n${indent}${key} {`; | |
} | |
serialize(prop.value.properties, level + 1); | |
text += `\n${indent}}`; | |
return; | |
} | |
let key; | |
if (prop.computed) { | |
text += `\n${indent}`; | |
finalize(prop.key, ': '); | |
} else { | |
if (t.isIdentifier(prop.key)) { | |
key = prop.key.name; | |
} else { | |
key = prop.key.value; | |
} | |
text += `\n${indent}${dashify(key)}: `; | |
} | |
if (t.isStringLiteral(prop.value) || t.isNumericLiteral(prop.value)) { | |
let value = prop.value.value; | |
if (t.isNumericLiteral(prop.value) && key && !unitless[key]) { | |
value += 'px'; | |
} | |
text += `${value};`; | |
} else { | |
finalize(prop.value, ';'); | |
} | |
}); | |
}; | |
serialize(p.value.properties); | |
quasis.push(t.templateElement({ raw: `${text}\n` })); | |
state.styled[p.key.name] = t.templateLiteral(quasis, expressions); | |
}); | |
path.parentPath.parentPath.remove(); | |
} | |
}, | |
}, | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment