Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSX transforms #3

Merged
merged 8 commits into from
Aug 7, 2018
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 14 additions & 16 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ module.exports = function(babel) {
const stylesArguments = [];
let classNameAttr = null;
let originalCssValue;
const newAttrs = jsxAttrs.filter(attr => {
const transformedJsxAttrs = jsxAttrs.filter(attr => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to be a pain! But transformedJsxAttrs => transformed from what? A little

/**
  * this does this
  */

would be helpful

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we can merge it 🚀

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no worries :) Update is in.

if (t.isJSXSpreadAttribute(attr)) return true;
const {value, name: jsxKey} = attr;
if (jsxKey.name === "css") {
Expand Down Expand Up @@ -92,15 +92,15 @@ module.exports = function(babel) {

if (withBabelPlugin) {
// if babel plugin is enabled use <div css={styles}/> syntax
newAttrs.push(
transformedJsxAttrs.push(
t.jsxAttribute(t.jsxIdentifier("css"), t.jsxExpressionContainer(stylesObject))
);
} else {
// if babel plugin is not enabled use <div className={css(styles)}/> syntax

if (!classNameAttr) {
const cssCall = t.callExpression(getCssFn(), [stylesObject]);
newAttrs.push(
transformedJsxAttrs.push(
t.jsxAttribute(t.jsxIdentifier("className"), t.jsxExpressionContainer(cssCall))
);
} else {
Expand All @@ -110,19 +110,19 @@ module.exports = function(babel) {
}
}
}
return newAttrs;
return transformedJsxAttrs;
};

const glamorousVisitor = {
// for each reference to an identifier...
ReferencedIdentifier(path, {getNewName, oldName, withBabelPlugin, getCssFn, getCxFn}) {
ReferencedIdentifier(path, {getStyledFn, oldName, withBabelPlugin, getCssFn, getCxFn}) {
// skip if the name of the identifier does not correspond to the name of glamorous default import
if (path.node.name !== oldName) return;

switch (path.parent.type) {
// replace `glamorous()` with `styled()`
case "CallExpression": {
path.replaceWith(getNewName());
path.replaceWith(getStyledFn());
break;
}

Expand All @@ -132,7 +132,7 @@ module.exports = function(babel) {
if (t.isCallExpression(grandParentPath.node)) {
grandParentPath.replaceWith(
t.callExpression(
t.callExpression(getNewName(), [
t.callExpression(getStyledFn(), [
t.stringLiteral(grandParentPath.node.callee.property.name),
]),
fixContentProp(grandParentPath.node.arguments)
Expand Down Expand Up @@ -179,15 +179,14 @@ module.exports = function(babel) {
return;
}

// use "styled" as new default import, only if there's no such variable in use yet
// use "name" as identifier, but only if it's not already used in the current scope
const createUniqueIdentifier = name =>
path.scope.hasBinding(name) ? path.scope.generateUidIdentifier(name) : t.identifier(name);
let newImports = [];

// this object collects all the imports we'll need from "react-emotion"
let emotionImports = {};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this do?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on what features are needed, we potentially need to import the default export or css or cx from react-emotion.
These imports are added to the code here


// only if the traversal below wants to know the newName,
// we're gonna add the default import
const getNewName = () => {
const getStyledFn = () => {
if (!emotionImports["default"]) {
emotionImports["default"] = t.importDefaultSpecifier(createUniqueIdentifier("styled"));
}
Expand All @@ -213,7 +212,7 @@ module.exports = function(babel) {
// only if the default import of glamorous is used, we're gonna apply the transforms
path.node.specifiers.filter(s => t.isImportDefaultSpecifier(s)).forEach(s => {
path.parentPath.traverse(glamorousVisitor, {
getNewName,
getStyledFn,
oldName: s.local.name,
withBabelPlugin: opts.withBabelPlugin,
getCssFn,
Expand All @@ -226,7 +225,7 @@ module.exports = function(babel) {
);

if (themeProvider) {
newImports.push(
path.insertBefore(
t.importDeclaration(
[t.importSpecifier(t.identifier("ThemeProvider"), t.identifier("ThemeProvider"))],
t.stringLiteral("emotion-theming")
Expand All @@ -236,15 +235,14 @@ module.exports = function(babel) {

// if we needed something from the emotion lib, we need to add the import
if (Object.keys(emotionImports).length) {
newImports.push(
path.insertBefore(
t.importDeclaration(
Object.values(emotionImports),
t.stringLiteral(opts.preact ? "preact-emotion" : "react-emotion")
)
);
}

newImports.forEach(ni => path.insertBefore(ni));
path.remove();
},
},
Expand Down