Comparing version 1.0.5 to 1.0.6
import React from 'react'; | ||
import css from 'glam'; | ||
var h = React.createElement; | ||
function emotion (tag, ref) { | ||
var emotion = function (tag, ref) { | ||
var cls = ref[0]; | ||
var vars = ref[1]; if ( vars === void 0 ) vars = []; | ||
return (function (superclass) { | ||
function Target () { | ||
superclass.apply(this, arguments); | ||
} | ||
return function (props) { return React.createElement( | ||
tag, | ||
Object.assign({}, props, { | ||
className: (props.className || '') + | ||
' ' + | ||
css( | ||
cls, | ||
vars.map(function (v) { return (v && typeof v === 'function' ? v(props) : v); }) | ||
) | ||
}) | ||
); }; | ||
}; | ||
if ( superclass ) Target.__proto__ = superclass; | ||
Target.prototype = Object.create( superclass && superclass.prototype ); | ||
Target.prototype.constructor = Target; | ||
Target.prototype.render = function render () { | ||
var this$1 = this; | ||
var finalClassName = css( | ||
cls, | ||
vars.map(function (v) { return (v && typeof v === 'function' ? v(this$1.props) : v); }) | ||
); | ||
return h( | ||
tag, | ||
Object.assign({}, this.props, { | ||
className: (this.props.className || '') + ' ' + finalClassName | ||
}) | ||
) | ||
}; | ||
return Target; | ||
}(React.Component)) | ||
} | ||
export { emotion }; | ||
//# sourceMappingURL=emotion.es.js.map |
@@ -1,2 +0,2 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("glam")):"function"==typeof define&&define.amd?define(["exports","react","glam"],t):t(e.emotion=e.emotion||{},e.React,e.glam)}(this,function(e,t,o){function n(e,n){var i=n[0],p=n[1];return void 0===p&&(p=[]),function(t){function n(){t.apply(this,arguments)}return t&&(n.__proto__=t),n.prototype=Object.create(t&&t.prototype),n.prototype.constructor=n,n.prototype.render=function(){var t=this,n=o(i,p.map(function(e){return e&&"function"==typeof e?e(t.props):e}));return r(e,Object.assign({},this.props,{className:(this.props.className||"")+" "+n}))},n}(t.Component)}t="default"in t?t.default:t,o="default"in o?o.default:o;var r=t.createElement;e.emotion=n,Object.defineProperty(e,"__esModule",{value:!0})}); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("glam")):"function"==typeof define&&define.amd?define(["exports","react","glam"],t):t(e.emotion=e.emotion||{},e.React,e.glam)}(this,function(e,t,n){t="default"in t?t.default:t,n="default"in n?n.default:n;var o=function(e,o){var i=o[0],a=o[1];return void 0===a&&(a=[]),function(o){return t.createElement(e,Object.assign({},o,{className:(o.className||"")+" "+n(i,a.map(function(e){return e&&"function"==typeof e?e(o):e}))}))}};e.emotion=o,Object.defineProperty(e,"__esModule",{value:!0})}); | ||
//# sourceMappingURL=emotion.umd.js.map |
@@ -1,2 +0,2 @@ | ||
function t(t){return t&&"object"==typeof t&&"default"in t?t.default:t}function e(t,e){var p=e[0],u=e[1];return void 0===u&&(u=[]),function(e){function r(){e.apply(this,arguments)}return e&&(r.__proto__=e),r.prototype=Object.create(e&&e.prototype),r.prototype.constructor=r,r.prototype.render=function(){var e=this,r=o(p,u.map(function(t){return t&&"function"==typeof t?t(e.props):t}));return n(t,Object.assign({},this.props,{className:(this.props.className||"")+" "+r}))},r}(r.Component)}Object.defineProperty(exports,"__esModule",{value:!0});var r=t(require("react")),o=t(require("glam")),n=r.createElement;exports.emotion=e; | ||
function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}Object.defineProperty(exports,"__esModule",{value:!0});var t=e(require("react")),r=e(require("glam")),n=function(e,n){var o=n[0],u=n[1];return void 0===u&&(u=[]),function(n){return t.createElement(e,Object.assign({},n,{className:(n.className||"")+" "+r(o,u.map(function(e){return e&&"function"==typeof e?e(n):e}))}))}};exports.emotion=n; | ||
//# sourceMappingURL=glam.js.map |
{ | ||
"name": "emotion", | ||
"version": "1.0.5", | ||
"version": "1.0.6", | ||
"description": "👩🎤 Glam + React", | ||
@@ -27,3 +27,3 @@ "jsnext:main": "dist/emotion.es.js", | ||
"babel-plugin-syntax-jsx": "^6.18.0", | ||
"glam": "^4.0.3" | ||
"glam": "^4.0.4" | ||
}, | ||
@@ -30,0 +30,0 @@ "devDependencies": { |
@@ -71,3 +71,42 @@ # emotion | ||
#### attr | ||
The [attr](https://developer.mozilla.org/en-US/docs/Web/CSS/attr) CSS function is supported in | ||
a basic capacity. | ||
```css | ||
/* get value from `width` prop */ | ||
width: attr(width vw); | ||
/* specify type or unit to apply to value */ | ||
width: attr(width vw); | ||
/* fallback value if props.width is falsey */ | ||
width: attr(width vw, 50); | ||
``` | ||
```jsx | ||
const H1 = emotion.h1` | ||
font-size: attr(fontSize px); | ||
margin: attr(margin rem, 4); | ||
font-family: sans-serif; | ||
color: ${colors.pink[5]}; | ||
@media (min-width: 680px) { | ||
color: attr(desktopColor); | ||
} | ||
` | ||
const Title = ({ title, scale }) => { | ||
return ( | ||
<H1 fontSize={16 * scale} desktopColor={colors.gray[5]}> | ||
{title} | ||
</H1> | ||
) | ||
} | ||
``` | ||
##### Supported value types | ||
`em|ex|px|rem|vw|vh|vmin|vmax|mm|cm|in|pt|pc` | ||
### css prop | ||
@@ -74,0 +113,0 @@ |
/* eslint-disable jsx-quotes,no-useless-escape,no-template-curly-in-string */ | ||
/* eslint-env jest */ | ||
import React from 'react' | ||
import renderer from 'react-test-renderer' | ||
import plugin from '../babel' | ||
import css, {fragment} from 'glam' | ||
const babel = require('babel-core') | ||
describe('emotion/glam', () => { | ||
describe('emotion/babel', () => { | ||
describe('babel emotion component', () => { | ||
test('basic', () => { | ||
const basic = 'emotion.h1\`font-size: \$\{fontSize\}px;\`' | ||
const {code} = babel.transform(basic, {plugins: [plugin, 'glam/babel']}) | ||
expect(code).toMatchSnapshot() | ||
}) | ||
test('function call', () => { | ||
const basic = 'emotion(MyComponent)\`font-size: \$\{fontSize\}px;\`' | ||
const {code} = babel.transform(basic, {plugins: [plugin, 'glam/babel']}) | ||
expect(code).toMatchSnapshot() | ||
}) | ||
test('attr', () => { | ||
const basic = `emotion('input')\` | ||
margin: attr(margin); | ||
color: #ffffff; | ||
height: \$\{props => props.height * props.scale\}; | ||
width: attr(width); | ||
color: blue; | ||
display: \$\{flex\}; | ||
\`` | ||
const {code} = babel.transform(basic, {plugins: [plugin, 'glam/babel']}) | ||
expect(code).toMatchSnapshot() | ||
}) | ||
test('attr with value type', () => { | ||
const basic = `emotion('input')\` | ||
margin: attr(margin px); | ||
\`` | ||
const {code} = babel.transform(basic, {plugins: [plugin, 'glam/babel']}) | ||
expect(code).toMatchSnapshot() | ||
}) | ||
test('attr with default value', () => { | ||
const basic = `emotion('input')\` | ||
margin: attr(margin, 16); | ||
\`` | ||
const {code} = babel.transform(basic, {plugins: [plugin, 'glam/babel']}) | ||
expect(code).toMatchSnapshot() | ||
}) | ||
test('attr with value type and default value', () => { | ||
const basic = `emotion('input')\` | ||
margin: attr(margin px, 16); | ||
\`` | ||
const {code} = babel.transform(basic, {plugins: [plugin, 'glam/babel']}) | ||
expect(code).toMatchSnapshot() | ||
}) | ||
test('match works on multiple', () => { | ||
const basic = `emotion('input')\` | ||
margin: attr(margin px, 16); | ||
color: blue; | ||
padding: attr(padding em, 16); | ||
\`` | ||
const {code} = babel.transform(basic, {plugins: [plugin, 'glam/babel']}) | ||
expect(code).toMatchSnapshot() | ||
}) | ||
test('attr kitchen sink', () => { | ||
const basic = `emotion('input')\` | ||
margin: attr(margin px, 16); | ||
padding: attr(padding em, 16); | ||
font-size: attr(fontSize ch, 8); | ||
width: attr(width %, 95); | ||
height: attr(height vw, 90); | ||
display: attr(display, flex); | ||
\`` | ||
const {code} = babel.transform(basic, {plugins: [plugin, 'glam/babel']}) | ||
expect(code).toMatchSnapshot() | ||
}) | ||
}) | ||
describe('babel css prop', () => { | ||
@@ -66,93 +137,2 @@ test('basic', () => { | ||
}) | ||
describe('real', () => { | ||
test('basic', () => { | ||
const tree = renderer | ||
.create( | ||
<p css={`color: red;`}> | ||
hello world | ||
</p> | ||
) | ||
.toJSON() | ||
expect(tree).toMatchSnapshot() | ||
}) | ||
test('string expression', () => { | ||
const tree = renderer | ||
.create( | ||
<p css="color:red;background:blue;font-size:48px;"> | ||
hello world | ||
</p> | ||
) | ||
.toJSON() | ||
expect(tree).toMatchSnapshot() | ||
}) | ||
test('kitchen sink', () => { | ||
const props = {online: false, error: false, radius: 5} | ||
const huge = 100 | ||
const tiny = 6 | ||
const bold = fragment` | ||
display: flex; | ||
font-weight: bold;` | ||
const big = fragment` | ||
@apply ${bold}; | ||
font-size: ${huge}` | ||
const small = fragment` | ||
font-size: ${tiny}` | ||
const flexCenter = fragment` | ||
display: flex; | ||
justify-content: center; | ||
align-items: center` | ||
const tree = renderer | ||
.create( | ||
<div | ||
className="css__legacy-stuff" | ||
css={` | ||
@apply ${bold} | ||
@apply ${flexCenter}; | ||
`} | ||
> | ||
<h1 | ||
css={` | ||
@apply ${props.error ? big : small}; | ||
color: red | ||
`} | ||
> | ||
BOOM | ||
</h1> | ||
<p className="test_class1" css={`color: blue;`}>Hello</p> | ||
<p | ||
className="test_class1 test___class45" | ||
css={`display: inline-flex`} | ||
> | ||
World | ||
</p> | ||
<p | ||
css={` | ||
color: red; | ||
border-radius: ${props.radius}; | ||
&:hover { | ||
font-weight: bold; | ||
color: ${props.online ? 'green' : 'gray'}; | ||
} | ||
`} | ||
> | ||
hello world | ||
</p> | ||
</div> | ||
) | ||
.toJSON() | ||
expect(tree).toMatchSnapshot() | ||
}) | ||
}) | ||
}) |
@@ -7,21 +7,4 @@ /* eslint-disable jsx-quotes,no-useless-escape,no-template-curly-in-string */ | ||
import {emotion} from '../react' | ||
import plugin from '../babel' | ||
const babel = require('babel-core') | ||
describe('glam react', () => { | ||
describe('babel glam component', () => { | ||
test('basic', () => { | ||
const basic = 'emotion.h1\`font-size: \$\{fontSize\}px;\`' | ||
const {code} = babel.transform(basic, {plugins: [plugin, 'glam/babel']}) | ||
expect(code).toMatchSnapshot() | ||
}) | ||
test('function call', () => { | ||
const basic = 'emotion(MyComponent)\`font-size: \$\{fontSize\}px;\`' | ||
const {code} = babel.transform(basic, {plugins: [plugin, 'glam/babel']}) | ||
expect(code).toMatchSnapshot() | ||
}) | ||
}) | ||
test('basic render', () => { | ||
@@ -44,2 +27,25 @@ const fontSize = 20 | ||
test('attr', () => { | ||
const H1 = emotion.h1` | ||
font-size: attr(fontSize); | ||
margin: attr(margin rem, 4); | ||
` | ||
const Title = ({title}) => { | ||
return ( | ||
<H1 fontSize={48}> | ||
{title} | ||
</H1> | ||
) | ||
} | ||
const tree = renderer | ||
.create( | ||
<Title /> | ||
) | ||
.toJSON() | ||
expect(tree).toMatchSnapshot() | ||
}) | ||
test('call expression', () => { | ||
@@ -100,3 +106,3 @@ const fontSize = 20 | ||
test('function in expression', () => { | ||
test('higher order component', () => { | ||
const fontSize = 20 | ||
@@ -118,2 +124,4 @@ const Content = emotion('div')` | ||
expect(ColumnContent.displayName).toBe('flexColumnundefined') | ||
const tree = renderer | ||
@@ -140,2 +148,91 @@ .create( | ||
}) | ||
test('basic', () => { | ||
const tree = renderer | ||
.create( | ||
<p css={`color: red;`}> | ||
hello world | ||
</p> | ||
) | ||
.toJSON() | ||
expect(tree).toMatchSnapshot() | ||
}) | ||
test('string expression', () => { | ||
const tree = renderer | ||
.create( | ||
<p css="color:red;background:blue;font-size:48px;"> | ||
hello world | ||
</p> | ||
) | ||
.toJSON() | ||
expect(tree).toMatchSnapshot() | ||
}) | ||
test('kitchen sink', () => { | ||
const props = {online: false, error: false, radius: 5} | ||
const huge = 100 | ||
const tiny = 6 | ||
const bold = fragment` | ||
display: flex; | ||
font-weight: bold;` | ||
const big = fragment` | ||
@apply ${bold}; | ||
font-size: ${huge}` | ||
const small = fragment` | ||
font-size: ${tiny}` | ||
const flexCenter = fragment` | ||
display: flex; | ||
justify-content: center; | ||
align-items: center` | ||
const tree = renderer | ||
.create( | ||
<div | ||
className="css__legacy-stuff" | ||
css={` | ||
@apply ${bold} | ||
@apply ${flexCenter}; | ||
`} | ||
> | ||
<h1 | ||
css={` | ||
@apply ${props.error ? big : small}; | ||
color: red | ||
`} | ||
> | ||
BOOM | ||
</h1> | ||
<p className="test_class1" css={`color: blue;`}>Hello</p> | ||
<p | ||
className="test_class1 test___class45" | ||
css={`display: inline-flex`} | ||
> | ||
World | ||
</p> | ||
<p | ||
css={` | ||
color: red; | ||
border-radius: ${props.radius}; | ||
&:hover { | ||
font-weight: bold; | ||
color: ${props.online ? 'green' : 'gray'}; | ||
} | ||
`} | ||
> | ||
hello world | ||
</p> | ||
</div> | ||
) | ||
.toJSON() | ||
expect(tree).toMatchSnapshot() | ||
}) | ||
}) |
124
src/babel.js
@@ -15,2 +15,112 @@ module.exports = function (babel) { | ||
function findAndReplaceAttrs (path) { | ||
let quasis = path.node.quasi.quasis | ||
let stubs = path.node.quasi.expressions | ||
let didFindAtLeastOneMatch = false | ||
let [nextQuasis, nextStubs] = quasis.reduce( | ||
(accum, quasi, i) => { | ||
const str = quasi.value.cooked | ||
const regex = /attr\(([\S]+)(?:\s*(em|ex|px|rem|vw|vh|vmin|vmax|mm|cm|in|pt|pc)?)(?:,\s*([\S^)]+))?\)/gm | ||
let attrMatch | ||
let matches = [] | ||
while ((attrMatch = regex.exec(str)) !== null) { | ||
didFindAtLeastOneMatch = true | ||
matches.push({ | ||
value: attrMatch[0], | ||
propName: attrMatch[1], | ||
valueType: attrMatch[2], | ||
defaultValue: attrMatch[3], | ||
index: attrMatch.index | ||
}) | ||
} | ||
let cursor = 0 | ||
for (let j = 0; j < matches.length; ++j) { | ||
const match = matches[j] | ||
const value = match.value | ||
const propName = match.propName | ||
const valueType = match.valueType | ||
const defaultValue = match.defaultValue | ||
const index = match.index | ||
const preAttr = `${str.slice(cursor, index)}` | ||
cursor = index + value.length | ||
const postAttr = `${str.slice(cursor)}` | ||
if (preAttr) { | ||
accum[0].push( | ||
t.templateElement({raw: preAttr, cooked: preAttr}, false) | ||
) | ||
} | ||
if (postAttr && j === matches.length - 1) { | ||
accum[0].push( | ||
t.templateElement( | ||
{raw: postAttr, cooked: postAttr}, | ||
i === quasis.length - 1 | ||
) | ||
) | ||
} | ||
let createMemberExpression = () => | ||
t.memberExpression(t.identifier('props'), t.identifier(propName)) | ||
let returnValue = createMemberExpression() | ||
if (valueType) { | ||
returnValue = t.binaryExpression( | ||
'+', | ||
createMemberExpression(), | ||
t.stringLiteral(valueType) | ||
) | ||
} | ||
if (defaultValue) { | ||
returnValue = t.conditionalExpression( | ||
createMemberExpression(), | ||
createMemberExpression(), | ||
t.parenthesizedExpression( | ||
t.binaryExpression( | ||
'+', | ||
t.stringLiteral(defaultValue), | ||
t.stringLiteral(valueType || '') | ||
) | ||
) | ||
) | ||
} | ||
const body = t.blockStatement([t.returnStatement(returnValue)]) | ||
const expr = t.functionExpression( | ||
t.identifier( | ||
`get${propName.charAt(0).toUpperCase() + propName.slice(1)}` | ||
), | ||
[t.identifier('props')], | ||
body | ||
) | ||
accum[1].push(expr) | ||
} | ||
if (stubs[i]) { | ||
accum[1].push(stubs[i]) | ||
} | ||
if (matches.length === 0) { | ||
accum[0].push(quasi) | ||
if (stubs[i]) { | ||
accum[1].push(stubs[i]) | ||
} | ||
} | ||
return accum | ||
}, | ||
[[], []] | ||
) | ||
if (didFindAtLeastOneMatch) { | ||
return t.templateLiteral(nextQuasis, nextStubs) | ||
} | ||
return path.node.quasi | ||
} | ||
return { | ||
@@ -21,2 +131,3 @@ name: 'emotion-for-glam', // not required | ||
CallExpression (path) { | ||
// emotion('h1', css(css-12, [color])) -> emotion('h1', [css-12, [color]]) | ||
if (path.node.callee.name === 'css') { | ||
@@ -123,2 +234,3 @@ const parentPath = path.parentPath | ||
if ( | ||
// emotion.h1`color:${color};` -> emotion('h1', css`color:${color};`) | ||
t.isMemberExpression(path.node.tag) && | ||
@@ -128,9 +240,12 @@ path.node.tag.object.name === 'emotion' && | ||
) { | ||
const built = findAndReplaceAttrs(path) | ||
path.replaceWith( | ||
t.callExpression(t.identifier(path.node.tag.object.name), [ | ||
t.callExpression(path.node.tag.object, [ | ||
t.stringLiteral(path.node.tag.property.name), | ||
t.taggedTemplateExpression(t.identifier('css'), path.node.quasi) | ||
t.taggedTemplateExpression(t.identifier('css'), built) | ||
]) | ||
) | ||
} else if ( | ||
// emotion('h1')`color:${color};` -> emotion('h1', css`color:${color};`) | ||
t.isCallExpression(path.node.tag) && | ||
@@ -140,6 +255,7 @@ path.node.tag.callee.name === 'emotion' && | ||
) { | ||
const built = findAndReplaceAttrs(path) | ||
path.replaceWith( | ||
t.callExpression(t.identifier(path.node.tag.callee.name), [ | ||
t.callExpression(path.node.tag.callee, [ | ||
path.node.tag.arguments[0], | ||
t.taggedTemplateExpression(t.identifier('css'), path.node.quasi) | ||
t.taggedTemplateExpression(t.identifier('css'), built) | ||
]) | ||
@@ -146,0 +262,0 @@ ) |
import React from 'react' | ||
import css from 'glam' | ||
const h = React.createElement | ||
export function emotion (tag, [cls, vars = []]) { | ||
return class Target extends React.Component { | ||
render () { | ||
const finalClassName = css( | ||
cls, | ||
vars.map(v => (v && typeof v === 'function' ? v(this.props) : v)) | ||
) | ||
return h( | ||
tag, | ||
Object.assign({}, this.props, { | ||
className: (this.props.className || '') + ' ' + finalClassName | ||
}) | ||
) | ||
} | ||
} | ||
} | ||
export const emotion = (tag, [cls, vars = []]) => props => | ||
React.createElement( | ||
tag, | ||
Object.assign({}, props, { | ||
className: (props.className || '') + | ||
' ' + | ||
css( | ||
cls, | ||
vars.map(v => (v && typeof v === 'function' ? v(props) : v)) | ||
) | ||
}) | ||
) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
35950
644
126
Updatedglam@^4.0.4