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

Add label property and autoLabel option for babel-plugin-emotion #375

Merged
merged 32 commits into from
Nov 4, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6a86754
Attempt to pass some metadata through to css function.
Oct 3, 2017
7669412
More work on sending meta data.
Oct 4, 2017
bd17ccc
Update snapshots that include meta data. Composition is broken.
Oct 4, 2017
fff7be1
It works. I do not combine the class names.
Oct 4, 2017
2a693dd
Add some functionality to getIdentifierName.
Oct 4, 2017
9afdbb0
Add better tests for css prop ids
Oct 4, 2017
e9ca17f
Update from master
Oct 9, 2017
af83838
Fix insertion hash bug
Oct 9, 2017
eb6073b
Insert id into class name and update snapshots
Oct 9, 2017
648c6b5
Lint and update snapshots
Oct 9, 2017
c065a0c
Fix snapshots and rearrange prefix and id name
Oct 9, 2017
d440dc4
Merge master
Nov 2, 2017
35ba2e9
Update snapshots and put meta data behind babel opt
Nov 2, 2017
c22835f
Update tests and related snapshots.
Nov 2, 2017
edf32c4
Update yarn.lock
Nov 2, 2017
aa9b2af
Add testing around the babel plugin meta flag.
Nov 2, 2017
6ced9a9
Linting
Nov 2, 2017
3c043c8
Simplify it and fix a bug
emmatown Nov 3, 2017
dd4cf99
Make it slightly smaller and likely faster
emmatown Nov 3, 2017
4a234ea
Add label property
emmatown Nov 3, 2017
c549936
Update snapshots
emmatown Nov 3, 2017
5ba6ffd
Merge branch 'css-parse-meta-object' into label-property
emmatown Nov 3, 2017
2e054df
Update snapshots
emmatown Nov 3, 2017
33ab042
Simplify SSR regex (I think it might have actually worked before) and…
emmatown Nov 3, 2017
9cbfe9e
Merge branch 'css-parse-meta-object' into label-property
emmatown Nov 3, 2017
c17c2b3
Update snapshots
emmatown Nov 3, 2017
e2b8ca2
Change stuff for styled
emmatown Nov 3, 2017
9e744c9
Merge pull request #442 from emotion-js/label-property
Nov 3, 2017
1bd2f6c
change option to autoLabel and add support for object styled calls.
Nov 3, 2017
09436c4
update emotion server and snapshots.
Nov 3, 2017
4d9371e
Add label property to css and keyframes object calls
emmatown Nov 4, 2017
654aa06
Merge branch 'master' into css-parse-meta-object
emmatown Nov 4, 2017
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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@
"eslint-plugin-react": "^7.3.0",
"eslint-plugin-standard": "^3.0.1",
"hoist-non-react-statics": "^2.3.1",
"jest": "^20.0.4",
"jest": "^21.2.1",
"jest-cli": "^20.0.4",
"jest-glamor-react": "^3.1.0",
"jest-glamor-react": "^3.1.1",
"lerna": "^2.2.0",
"module-alias": "^2.0.1",
"npm-run-all": "^4.0.2",
Expand Down
70 changes: 67 additions & 3 deletions packages/babel-plugin-emotion/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The Babel Plugin is highly recommended, but not required in version 8 and above.
| Static Extraction | | ✅ | Generated CSS that is eligible for extraction can be moved to an external css file. |
| Source Maps | | ✅ | When enabled, navigate directly to the style declaration in your javascript file. |
| `css` as Prop | | ✅ | Convenient helper for calling `css` and appending the generated className during compile time. |

| Contextual Class Names | | ✅ | Generated class names include the name of the variable or component they were defined in.
## Example

**In**
Expand Down Expand Up @@ -77,6 +77,7 @@ With options:
["emotion", {
"hoist": false,
"sourceMap": false,
"autoLabel": false,
"extractStatic": false,
"importedNames": {
"styled": "styled",
Expand All @@ -100,10 +101,10 @@ Use [Babel's `env` property](https://babeljs.io/docs/usage/babelrc/#env-option)
{
"env": {
"production": {
"plugins": [["emotion", { "sourceMap": false, "hoist": true }]]
"plugins": [["emotion", { "sourceMap": false, "hoist": true, "autoLabel": true }]]
},
"development": {
"plugins": [["emotion", { "sourceMap": true, "hoist": false }]]
"plugins": [["emotion", { "sourceMap": true, "hoist": false, "autoLabel": true }]]
}
}
}
Expand Down Expand Up @@ -161,6 +162,69 @@ This option enables the following:

[**Documentation**](docs/source-maps.md)

### `autoLabel`

`boolean`, defaults to `false`.

This option enables the following:

- Automatically adds the `label` property to styles so that class names generated by `css` or `styled` include the name of the variable the result is assigned to.

#### css

**In**

```javascript
const brownStyles = css({ color: 'brown' });
```

**Out**

```javascript
const brownStyles = /*#__PURE__*/css({ color: 'blue' }, "label:brownStyles;");
```

`brownStyles`'s value would be `css-1q8eu9e-brownStyles`

#### styled

**In**

```javascript
const Profile = () => {
const H1 = styled.h1({
borderRadius: '50%',
transition: 'transform 400ms ease-in-out',
boxSizing: 'border-box',
display: 'flex',
':hover': {
transform: 'scale(1.2)'
}
})
}
```

**Out**

```javascript
const Profile = () => {
const H1 = /*#__PURE__*/styled('h1', {
label: 'H1'
})({
borderRadius: '50%',
transition: 'transform 400ms ease-in-out',
boxSizing: 'border-box',
display: 'flex',
':hover': {
transform: 'scale(1.2)'
}
});
};
```

`H1`'s class name attribute would be `css-13djram-H1`


### `extractStatic`

`boolean`, defaults to `false`.
Expand Down
17 changes: 16 additions & 1 deletion packages/babel-plugin-emotion/src/babel-utils.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
import { hashArray } from './index'

export function getIdentifierName(path, t) {
function getDeclaratorName(path, t) {
const parent = path.findParent(p => p.isVariableDeclarator())
return parent && t.isIdentifier(parent.node.id) ? parent.node.id.name : ''
}

export function getIdentifierName(path, t) {
const classParent = path.findParent(p => t.isClass(p))
Copy link
Member Author

Choose a reason for hiding this comment

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

This is so we can include the name of the component the inline css call is in.

if (classParent && classParent.node.id) {
return t.isIdentifier(classParent.node.id) ? classParent.node.id.name : ''
} else if (
classParent &&
classParent.node.superClass &&
classParent.node.superClass.name
) {
return `${getDeclaratorName(path, t)}(${classParent.node.superClass.name})`
}

return getDeclaratorName(path, t)
}

export function getRuntimeImportPath(path, t) {
const binding = path.scope.getBinding(path.node.name)
if (!t.isImportDeclaration(binding.path.parentPath)) {
Expand Down
59 changes: 51 additions & 8 deletions packages/babel-plugin-emotion/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ export function replaceCssWithCallExpression(
) {
try {
let { hash, src } = createRawStringFromTemplateLiteral(path.node.quasi)
const name = getName(getIdentifierName(path, t), 'css')
const identifierName = getIdentifierName(path, t)
const name = getName(identifierName, 'css')

if (state.extractStatic && !path.node.quasi.expressions.length) {
const staticCSSRules = staticStylis(
Expand All @@ -68,11 +69,13 @@ export function replaceCssWithCallExpression(
path.replaceWith(
t.callExpression(
identifier,
new ASTObject(
minify(src),
path.node.quasi.expressions,
t
).toExpressions()
new ASTObject(minify(src), path.node.quasi.expressions, t)
.toExpressions()
.concat(
state.opts.autoLabel && identifierName
? [t.stringLiteral(`label:${identifierName.trim()};`)]
: []
)
)
)

Expand Down Expand Up @@ -121,13 +124,28 @@ export function buildStyledCallExpression(identifier, tag, path, state, t) {
if (state.opts.sourceMap === true && path.node.quasi.loc !== undefined) {
src += addSourceMaps(path.node.quasi.loc.start, state)
}

return t.callExpression(
t.callExpression(identifier, [tag]),
t.callExpression(
identifier,
state.opts.autoLabel && identifierName
? [
tag,
t.objectExpression([
t.objectProperty(
t.identifier('label'),
t.stringLiteral(identifierName.trim())
)
])
]
: [tag]
),
new ASTObject(minify(src), path.node.quasi.expressions, t).toExpressions()
)
}

export function buildStyledObjectCallExpression(path, state, identifier, t) {
const identifierName = getIdentifierName(path, t)
const tag = t.isCallExpression(path.node.callee)
? path.node.callee.arguments[0]
: t.stringLiteral(path.node.callee.property.name)
Expand All @@ -136,9 +154,26 @@ export function buildStyledObjectCallExpression(path, state, identifier, t) {
if (state.opts.sourceMap === true && path.node.loc !== undefined) {
args.push(t.stringLiteral(addSourceMaps(path.node.loc.start, state)))
}

path.addComment('leading', '#__PURE__')

return t.callExpression(t.callExpression(identifier, [tag]), args)
return t.callExpression(
t.callExpression(
identifier,
state.opts.autoLabel && identifierName
? [
tag,
t.objectExpression([
t.objectProperty(
t.identifier('label'),
t.stringLiteral(identifierName.trim())
)
])
]
: [tag]
),
args
)
}

const visited = Symbol('visited')
Expand Down Expand Up @@ -255,6 +290,14 @@ export default function(babel) {
case state.importedNames.css:
case state.importedNames.keyframes: {
path.addComment('leading', '#__PURE__')
if (state.opts.autoLabel) {
const identifierName = getIdentifierName(path, t)
if (identifierName) {
path.node.arguments.push(
t.stringLiteral(`label:${identifierName.trim()};`)
)
}
}
}
// eslint-disable-next-line no-fallthrough
case state.importedNames.injectGlobal:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,34 @@ const Profile = () => {
};"
`;

exports[`babel css prop label in class component 1`] = `
"import { css as _css } from \\"emotion\\";

class ClsComp extends React.Component {
render() {
return <div className={/*#__PURE__*/_css(\\"foo\\", \\"label:ClsComp;\\")}>Hello</div>;
}
}"
`;

exports[`babel css prop label in higher order component 1`] = `
"import { css as _css } from \\"emotion\\";

const foo = W => class extends Component {
render() {
return <div className={/*#__PURE__*/_css(\\"color:brown;\\", \\"label:foo(Component);\\")}>Hello</div>;
}
};"
`;

exports[`babel css prop label in stateless functional component 1`] = `
"import { css as _css } from \\"emotion\\";

const SFC = () => {
return <div className={/*#__PURE__*/_css(\\"color:brown;\\", \\"label:SFC;\\")}>Hello</div>;
};"
`;

exports[`babel css prop no css attr 1`] = `"<div></div>;"`;

exports[`babel css prop no import css prop 1`] = `"<div className={merge( /*#__PURE__*/css(\\"color:brown;\\") + (\\" \\" + \`test__class\`))}></div>;"`;
Expand Down
18 changes: 18 additions & 0 deletions packages/babel-plugin-emotion/test/__snapshots__/css.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,24 @@ const cls2 = /*#__PURE__*/css([{
}]);"
`;

exports[`babel css inline autoLabel 1`] = `
"
function test() {
const cls1 = /*#__PURE__*/css(\\"font-size:20px;@media(min-width:420px){color:blue;\\", /*#__PURE__*/css(\\"width:96px;height:96px;\\", \\"label:cls1;\\"), \\";line-height:26px;}background:green;\\", { backgroundColor: \\"hotpink\\" }, \\";\\", \\"label:cls1;\\");

const cls2 = /*#__PURE__*/css({ color: 'blue' }, \\"label:cls2;\\");
const cls4 = /*#__PURE__*/css({ color: \\"hotpink\\" }, \\"label:cls4;\\");

const cls3 = /*#__PURE__*/css(\\"display:flex;&:hover{color:hotpink;}\\", \\"label:cls3;\\");
function inner() {
const styles = { color: \\"darkorchid\\" };
const color = 'aquamarine';

const cls4 = /*#__PURE__*/css(cls3, \\";\\", cls1, \\";\\", () => ({ color: \\"darkorchid\\" }), \\";\\", () => ({ color }), \\";\\", /*#__PURE__*/css(\\"height:420px;width:\\", styles, \\"label:cls4;\\"), \\";\\", \\"label:cls4;\\");
}
}"
`;

exports[`babel css inline css basic 1`] = `
"
/*#__PURE__*/css(\\"margin:12px 48px;color:#ffffff;display:flex;flex:1 0 auto;color:blue;@media(min-width:420px){line-height:40px;}width:\\", widthVar, \\";\\");"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`babel styled component autoLabel string styles 1`] = `
"
const Profile = () => {
const ProfileH1 = /*#__PURE__*/styled('h1', {
label: 'ProfileH1'
})('color:blue;');

return <H1>Hello</H1>;
};"
`;

exports[`babel styled component extract basic 1`] = `
"import \\"./styled.test.emotion.css\\";
const H1 = styled(\\"h1\\", {
Expand All @@ -18,6 +29,23 @@ styled(\\"h1\\", {

exports[`babel styled component extract no use 2`] = `""`;

exports[`babel styled component inline autoLabel object styles 1`] = `
"
const Profile = () => {
const H1 = /*#__PURE__*/styled('h1', {
label: 'H1'
})({
borderRadius: '50%',
transition: 'transform 400ms ease-in-out',
boxSizing: 'border-box',
display: 'flex',
':hover': {
transform: 'scale(1.2)'
}
});
};"
`;

exports[`babel styled component inline basic 1`] = `"const H1 = /*#__PURE__*/styled('h1')('font-size:', fontSize + 'px', ';');"`;

exports[`babel styled component inline composition based on props 1`] = `
Expand Down
40 changes: 40 additions & 0 deletions packages/babel-plugin-emotion/test/css-prop.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,46 @@ describe('babel css prop', () => {
expect(code).toMatchSnapshot()
})

test('label in stateless functional component', () => {
const basic = `
const SFC = () => {
return <div css={\`color: brown;\`}>Hello</div>
}
`
const { code } = babel.transform(basic, {
plugins: [[plugin, { autoLabel: true }]]
})
expect(code).toMatchSnapshot()
})

test('label in class component', () => {
const basic = `
class ClsComp extends React.Component {
render() {
return <div css="foo">Hello</div>
}
}
`
const { code } = babel.transform(basic, {
plugins: [[plugin, { autoLabel: true }]]
})
expect(code).toMatchSnapshot()
})

test('label in higher order component', () => {
const basic = `
const foo = (W) => class extends Component {
render() {
return <div css={\`color: brown;\`}>Hello</div>
}
}
`
const { code } = babel.transform(basic, {
plugins: [[plugin, { autoLabel: true }]]
})
expect(code).toMatchSnapshot()
})

test('hoisting object styles', () => {
const basic =
'const Profile = () => ' +
Expand Down
Loading