From 4380a1812547466a263348aa1b113676279b1cb1 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Thu, 1 Jun 2017 14:09:48 -0600 Subject: [PATCH 01/10] initial work --- .../__tests__/__snapshots__/index.js.snap | 60 ------------------- src/glam/__tests__/{index.js => babel.js} | 4 +- src/glam/__tests__/index.js.css | 26 -------- src/glam/__tests__/react.js | 36 +++++++++++ src/glam/babel.js | 15 ++++- src/glam/react.js | 9 +++ 6 files changed, 59 insertions(+), 91 deletions(-) delete mode 100644 src/glam/__tests__/__snapshots__/index.js.snap rename src/glam/__tests__/{index.js => babel.js} (97%) delete mode 100644 src/glam/__tests__/index.js.css create mode 100644 src/glam/__tests__/react.js create mode 100644 src/glam/react.js diff --git a/src/glam/__tests__/__snapshots__/index.js.snap b/src/glam/__tests__/__snapshots__/index.js.snap deleted file mode 100644 index 0fc06a538..000000000 --- a/src/glam/__tests__/__snapshots__/index.js.snap +++ /dev/null @@ -1,60 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`emotion/glam babel StringLiteral css prop value 1`] = `"
;"`; - -exports[`emotion/glam babel basic 1`] = `"
;"`; - -exports[`emotion/glam babel className as expression 1`] = `"
;"`; - -exports[`emotion/glam babel className as expression string 1`] = `"
;"`; - -exports[`emotion/glam babel css empty 1`] = `"
;"`; - -exports[`emotion/glam babel emptyClassName 1`] = `"
;"`; - -exports[`emotion/glam babel no css attr 1`] = `"
;"`; - -exports[`emotion/glam babel noClassName 1`] = `"
;"`; - -exports[`emotion/glam real basic 1`] = ` -

- hello world -

-`; - -exports[`emotion/glam real kitchen sink 1`] = ` -
-

- BOOM -

-

- Hello -

-

- World -

-

- hello world -

-
-`; - -exports[`emotion/glam real string expression 1`] = ` -

- hello world -

-`; diff --git a/src/glam/__tests__/index.js b/src/glam/__tests__/babel.js similarity index 97% rename from src/glam/__tests__/index.js rename to src/glam/__tests__/babel.js index a5fc858fe..322dd99da 100644 --- a/src/glam/__tests__/index.js +++ b/src/glam/__tests__/babel.js @@ -1,4 +1,4 @@ -/* eslint-disable jsx-quotes,no-useless-escape */ +/* 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' @@ -8,7 +8,7 @@ import css, {fragment} from 'glam' const babel = require('babel-core') describe('emotion/glam', () => { - describe('babel', () => { + describe('babel css prop', () => { test('basic', () => { const basic = '(
)' const {code} = babel.transform(basic, {plugins: [plugin, 'glam/babel']}) diff --git a/src/glam/__tests__/index.js.css b/src/glam/__tests__/index.js.css deleted file mode 100644 index 5709cc9fd..000000000 --- a/src/glam/__tests__/index.js.css +++ /dev/null @@ -1,26 +0,0 @@ -/* do not edit this file */ -.css-jk0pkr { color: red; } -.css-7sqgip { color: red;background: blue;font-size: 48px; } -.frag-15qcrjv { --frag-15qcrjv: { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; - font-weight: bold; }; } -.frag-193n5ss { --frag-193n5ss: { @apply --frag-193n5ss-0; - font-size: var(--frag-193n5ss-1) }; } -.frag-1wh34bm { --frag-1wh34bm: { font-size: var(--frag-1wh34bm-0) }; } -.frag-mdk64u { --frag-mdk64u: { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; - justify-content: center; - -webkit-box-pack: center; - -ms-flex-pack: center; - -webkit-justify-content: center; - align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - -webkit-align-items: center }; } -.css-1170hdn { @apply --css-1170hdn-0 - @apply --css-1170hdn-1; } -.css-wjhr0q { @apply --css-wjhr0q-0; - color: red } -.css-14jcta0 { color: blue; } -.css-1gdxe9e { display: -webkit-box; display: -moz-box; display: -ms-inline-flexbox; display: -webkit-inline-flex; display: inline-flex } -.css-1g8ltio { color: red; - border-radius: var(--css-1g8ltio-0); } -.css-1g8ltio:hover { font-weight: bold; color: var(--css-1g8ltio-1); } \ No newline at end of file diff --git a/src/glam/__tests__/react.js b/src/glam/__tests__/react.js new file mode 100644 index 000000000..0c410bbfd --- /dev/null +++ b/src/glam/__tests__/react.js @@ -0,0 +1,36 @@ +/* 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 css, { fragment } from 'glam' +import glam from '../react' +import plugin from '../babel' + +const babel = require('babel-core') + +describe('glam react', () => { + describe('babel glam component', () => { + test('basic', () => { + const basic = 'glam.h1\`font-size: \$\{fontSize\}px;\`' + const {code} = babel.transform(basic, {plugins: [plugin, 'glam/babel']}) + expect(code).toMatchSnapshot() + }) + }) + + test('basic render', () => { + const fontSize = 20 + const H1 = glam.h1` + font-size: ${fontSize}px; + ` + + const tree = renderer + .create( +

+ hello world +

+ ) + .toJSON() + + expect(tree).toMatchSnapshot() + }) +}) diff --git a/src/glam/babel.js b/src/glam/babel.js index 268b0c4f9..643253fb8 100644 --- a/src/glam/babel.js +++ b/src/glam/babel.js @@ -106,9 +106,18 @@ module.exports = function (babel) { ) } }, - JSXAttribute (path, state) { - if (path.node.name.name === 'css') { - console.log('whatup', path.node.value) + TaggedTemplateExpression (path) { + if ( + t.isMemberExpression(path.node.tag) && + path.node.tag.object.name === 'glam' && + t.isTemplateLiteral(path.node.quasi) + ) { + path.replaceWith( + t.callExpression(t.identifier(path.node.tag.object.name), [ + t.stringLiteral(path.node.tag.property.name), + t.taggedTemplateExpression(t.identifier('css'), path.node.quasi) + ]) + ) } } } diff --git a/src/glam/react.js b/src/glam/react.js new file mode 100644 index 000000000..7552e3ab1 --- /dev/null +++ b/src/glam/react.js @@ -0,0 +1,9 @@ +import React from 'react' + +export default function glam (tag, className) { + return class Target extends React.Component { + render () { + return React.createElement(tag, { className }) + } + } +} From 88350c6963ca7cfd4aabefb3bcf7d42628df06ce Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Thu, 1 Jun 2017 15:28:13 -0600 Subject: [PATCH 02/10] here we go --- README.md | 86 ++++---------- cxs.js | 1 - glam.js | 2 +- glamor.js | 1 - src/{glam => }/__tests__/.babelrc | 0 src/{glam => }/__tests__/babel.js | 0 src/{glam => }/__tests__/react.js | 17 +++ src/{glam => }/babel.js | 0 src/cxs/__tests__/.babelrc | 10 -- src/cxs/__tests__/__snapshots__/index.js.snap | 52 --------- src/cxs/__tests__/index.js | 106 ------------------ src/cxs/babel.js | 86 -------------- src/glam/react.js | 9 -- src/glamor/__tests__/.babelrc | 10 -- .../__tests__/__snapshots__/index.js.snap | 91 --------------- src/glamor/__tests__/index.js | 104 ----------------- src/glamor/babel.js | 37 ------ src/react.js | 14 +++ 18 files changed, 55 insertions(+), 571 deletions(-) delete mode 100644 cxs.js delete mode 100644 glamor.js rename src/{glam => }/__tests__/.babelrc (100%) rename src/{glam => }/__tests__/babel.js (100%) rename src/{glam => }/__tests__/react.js (75%) rename src/{glam => }/babel.js (100%) delete mode 100644 src/cxs/__tests__/.babelrc delete mode 100644 src/cxs/__tests__/__snapshots__/index.js.snap delete mode 100644 src/cxs/__tests__/index.js delete mode 100644 src/cxs/babel.js delete mode 100644 src/glam/react.js delete mode 100644 src/glamor/__tests__/.babelrc delete mode 100644 src/glamor/__tests__/__snapshots__/index.js.snap delete mode 100644 src/glamor/__tests__/index.js delete mode 100644 src/glamor/babel.js create mode 100644 src/react.js diff --git a/README.md b/README.md index a4b730c6d..ff731d551 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # emotion -#### `css` prop for all +## 👩‍🚀 Glam + React [![npm version](https://badge.fury.io/js/emotion.svg)](https://badge.fury.io/js/emotion) [![Build Status](https://travis-ci.org/tkh44/emotion.svg?branch=master)](https://travis-ci.org/tkh44/emotion) @@ -8,23 +8,14 @@ - [Install](#install) -- [glam](#emotionglam) -- [glamor](#emotionglamor) -- [cxs](#emotioncxs) +- [Example Project](https://github.com/tkh44/emotion/tree/master/examples/glam) ## Install ```bash -npm install -S emotion +npm install -S emotion glam ``` -## `emotion/glam` - -[Example Project](https://github.com/tkh44/emotion/tree/master/examples/glam) - -```bash -npm install -S glam -``` **.babelrc** ```json @@ -36,72 +27,41 @@ npm install -S glam } ``` -```jsx harmony -const Name = ({ color, name }) =>

{name}

-``` - -is converted to +### glam ```jsx harmony -const Name = ({ color, name }) =>

{name}

-``` +import glam from 'emotion' +const fontSize = 48 +const H1 = glam.h1` + font-size: ${fontSize}px; + color: 'blue'; +` -**Similar to importing React when using jsx, `import css from 'glam'` must be at the top of your source files.** +// is compiled to -## `emotion/glamor` +const H1 = glam('h1', css` + font-size: ${fontSize}px; + color: 'blue'; +`) -```bash -npm install -S glamor -``` +// can be used as any other normal component -**.babelrc** -```json -{ - "plugins": [ - "emotion/glamor", - ] +function Greeting ({ name }) { + return

Hello {name}

// blue, 48px text } ``` -```jsx harmony -const Name = ({ color, name }) =>

{name}

-``` -is converted to +### css prop -```jsx harmony -const Name = ({ color, name }) =>

{name}

-``` +When using the emotion babel plugin any `css` prop is converted to a tagged template expression and appended to any existing class names. -**Similar to importing React when using jsx, `import {css} from 'glamor'` must be at the top of your source files.** - -## `emotion/cxs` - -```bash -npm install -S cxs -``` - -**.babelrc** -```json -{ - "plugins": [ - "emotion/cxs", - ] -} -``` - ```jsx harmony -const Name = ({ color, name }) =>

{name}

-``` +const Name = ({ color, name }) =>

{name}

-is converted to +// is converted to -```jsx harmony -const Name = ({ color, name }) =>

{name}

+const Name = ({ color, name }) =>

{name}

``` - - -**Similar to importing React when using jsx, `import cxs from 'cxs'` must be at the top of your source files.** - diff --git a/cxs.js b/cxs.js deleted file mode 100644 index 5e9e73237..000000000 --- a/cxs.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./src/cxs/babel') diff --git a/glam.js b/glam.js index 93e603a50..d8d05ee65 100644 --- a/glam.js +++ b/glam.js @@ -1 +1 @@ -module.exports = require('./src/glam/babel') +module.exports = require('./src/babel') diff --git a/glamor.js b/glamor.js deleted file mode 100644 index d2cce17a5..000000000 --- a/glamor.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./src/glamor/babel') diff --git a/src/glam/__tests__/.babelrc b/src/__tests__/.babelrc similarity index 100% rename from src/glam/__tests__/.babelrc rename to src/__tests__/.babelrc diff --git a/src/glam/__tests__/babel.js b/src/__tests__/babel.js similarity index 100% rename from src/glam/__tests__/babel.js rename to src/__tests__/babel.js diff --git a/src/glam/__tests__/react.js b/src/__tests__/react.js similarity index 75% rename from src/glam/__tests__/react.js rename to src/__tests__/react.js index 0c410bbfd..65c03c606 100644 --- a/src/glam/__tests__/react.js +++ b/src/__tests__/react.js @@ -33,4 +33,21 @@ describe('glam react', () => { expect(tree).toMatchSnapshot() }) + + test('passes props', () => { + const fontSize = 20 + const H1 = glam.h1` + font-size: ${fontSize}px; + ` + + const tree = renderer + .create( +

+ hello world +

+ ) + .toJSON() + + expect(tree).toMatchSnapshot() + }) }) diff --git a/src/glam/babel.js b/src/babel.js similarity index 100% rename from src/glam/babel.js rename to src/babel.js diff --git a/src/cxs/__tests__/.babelrc b/src/cxs/__tests__/.babelrc deleted file mode 100644 index 1f68b4d6a..000000000 --- a/src/cxs/__tests__/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - "env", - "stage-0", - "react" - ], - "plugins": [ - ["../babel.js"], - ] -} diff --git a/src/cxs/__tests__/__snapshots__/index.js.snap b/src/cxs/__tests__/__snapshots__/index.js.snap deleted file mode 100644 index 0b9d9ef53..000000000 --- a/src/cxs/__tests__/__snapshots__/index.js.snap +++ /dev/null @@ -1,52 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`emotion/cxs babel basic 1`] = `"
;"`; - -exports[`emotion/cxs babel className as expression 1`] = `"
;"`; - -exports[`emotion/cxs babel className as expression string 1`] = `"
;"`; - -exports[`emotion/cxs babel css empty 1`] = `"
;"`; - -exports[`emotion/cxs babel emptyClassName 1`] = `"
;"`; - -exports[`emotion/cxs babel no className 1`] = `"
;"`; - -exports[`emotion/cxs babel no css attr 1`] = `"
;"`; - -exports[`emotion/cxs babel wrong value type 1`] = `"
;"`; - -exports[`emotion/cxs real basic 1`] = ` -

- hello world -

-`; - -exports[`emotion/cxs real kitchen sink 1`] = ` -
-

- BOOM -

-

- Hello -

-

- World -

-

- hello world -

-
-`; diff --git a/src/cxs/__tests__/index.js b/src/cxs/__tests__/index.js deleted file mode 100644 index 3800fac08..000000000 --- a/src/cxs/__tests__/index.js +++ /dev/null @@ -1,106 +0,0 @@ -/* eslint-disable jsx-quotes,no-useless-escape */ -/* eslint-env jest */ -import React from 'react' -import renderer from 'react-test-renderer' -import plugin from '../babel' -import {matcher, serializer} from 'jest-glamor-react' -import cxs from 'cxs' - -expect.addSnapshotSerializer(serializer) -expect.extend(matcher) - -const babel = require('babel-core') - -describe('emotion/cxs', () => { - describe('babel', () => { - test('basic', () => { - const basic = `(
)` - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - - test('no css attr', () => { - const basic = '(
)' - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - - test('css empty', () => { - const basic = '(
)' - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - - test('wrong value type', () => { - const basic = '(
)' - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - - test('no className', () => { - const basic = `(
)` - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - - test('emptyClassName', () => { - const basic = `(
)` - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - - test('className as expression', () => { - const basic = `(
)` - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - - test('className as expression string', () => { - const basic = - "(
)" - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - }) - - describe('real', () => { - test('basic', (done) => { - const tree = renderer - .create( -

- hello world -

- ) - .toJSON() - - expect(tree).toMatchSnapshotWithGlamor() - setTimeout(done) - }) - - test('kitchen sink', (done) => { - const tree = renderer - .create( -
-

- BOOM -

-

Hello

-

- World -

-

- hello world -

- -
- ) - .toJSON() - - expect(tree).toMatchSnapshotWithGlamor() - setTimeout(done) - }) - }) -}) diff --git a/src/cxs/babel.js b/src/cxs/babel.js deleted file mode 100644 index c75eafde4..000000000 --- a/src/cxs/babel.js +++ /dev/null @@ -1,86 +0,0 @@ -module.exports = function (babel) { - const {types: t} = babel - - function createClassNameAttr (expression) { - return t.jSXAttribute( - t.jSXIdentifier('className'), - t.JSXExpressionContainer(expression) - ) - } - - return { - name: 'emotion-for-cxs', // not required - inherits: require('babel-plugin-syntax-jsx'), - visitor: { - JSXOpeningElement (path, state) { - let cssPath - let classNamesPath - - path.get('attributes').forEach(openElPath => { - const attrPath = openElPath.get('name') - const name = attrPath.node.name - - if (name === 'css') { - cssPath = attrPath - } - - if (name === 'className') { - classNamesPath = attrPath - } - }) - - if (!cssPath) return - - let cssPropValue = cssPath.container && cssPath.container.value - let classNamesValue = - classNamesPath && - classNamesPath.container && - classNamesPath.container.value - - if (t.isJSXExpressionContainer(cssPropValue)) { - cssPropValue = cssPropValue.expression - } - - let cxsCssFunction = t.callExpression(t.identifier('cxs'), [ - cssPropValue - ]) - - if (!classNamesValue) { - cssPath.parentPath.replaceWith(createClassNameAttr(cxsCssFunction)) - return - } - - cssPath.parentPath.remove() - if (t.isJSXExpressionContainer(classNamesValue)) { - classNamesPath.parentPath.replaceWith( - createClassNameAttr( - t.binaryExpression( - '+', - t.binaryExpression( - '+', - classNamesValue.expression, - t.stringLiteral(' ') - ), - cxsCssFunction - ) - ) - ) - } else { - classNamesPath.parentPath.replaceWith( - createClassNameAttr( - t.binaryExpression( - '+', - t.binaryExpression( - '+', - t.stringLiteral(classNamesValue.value || ''), - t.stringLiteral(' ') - ), - cxsCssFunction - ) - ) - ) - } - } - } - } -} diff --git a/src/glam/react.js b/src/glam/react.js deleted file mode 100644 index 7552e3ab1..000000000 --- a/src/glam/react.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' - -export default function glam (tag, className) { - return class Target extends React.Component { - render () { - return React.createElement(tag, { className }) - } - } -} diff --git a/src/glamor/__tests__/.babelrc b/src/glamor/__tests__/.babelrc deleted file mode 100644 index 1f68b4d6a..000000000 --- a/src/glamor/__tests__/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - "env", - "stage-0", - "react" - ], - "plugins": [ - ["../babel.js"], - ] -} diff --git a/src/glamor/__tests__/__snapshots__/index.js.snap b/src/glamor/__tests__/__snapshots__/index.js.snap deleted file mode 100644 index 2e03eb77c..000000000 --- a/src/glamor/__tests__/__snapshots__/index.js.snap +++ /dev/null @@ -1,91 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`emotion/glamor babel basic 1`] = `"
;"`; - -exports[`emotion/glamor babel className as expression 1`] = `"
;"`; - -exports[`emotion/glamor babel className as expression string 1`] = `"
;"`; - -exports[`emotion/glamor babel css empty 1`] = `"
;"`; - -exports[`emotion/glamor babel emptyClassName 1`] = `"
;"`; - -exports[`emotion/glamor babel no className 1`] = `"
;"`; - -exports[`emotion/glamor babel no css attr 1`] = `"
;"`; - -exports[`emotion/glamor babel wrong value type 1`] = `"
;"`; - -exports[`emotion/glamor real basic 1`] = ` -.css-1ezp9xe, -[data-css-1ezp9xe] { - color: red; -} - -

- hello world -

-`; - -exports[`emotion/glamor real kitchen sink 1`] = ` -.css-icjsl7, -[data-css-icjsl7] { - color: blue; -} - -.css-gvvg5g, -[data-css-gvvg5g] { - color: gray; -} - -.css-16ywyew, -[data-css-16ywyew] { - border: 1px solid blue; -} - -.css-1cqgl9p, -[data-css-1cqgl9p] { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; -} - -@media (min-width: 420px) { - .css-198yaui, - [data-css-198yaui] { - font-size: 48px; - } -} - -
-

- BOOM -

-

- Hello -

-

- World -

-

- hello world -

-
-`; diff --git a/src/glamor/__tests__/index.js b/src/glamor/__tests__/index.js deleted file mode 100644 index d750cffb1..000000000 --- a/src/glamor/__tests__/index.js +++ /dev/null @@ -1,104 +0,0 @@ -/* eslint-disable jsx-quotes,no-useless-escape */ -/* eslint-env jest */ -import React from 'react' -import renderer from 'react-test-renderer' -import plugin from '../babel' -import { matcher, serializer } from 'jest-glamor-react' -import { css } from 'glamor' - -expect.addSnapshotSerializer(serializer) -expect.extend(matcher) - -const babel = require('babel-core') - -describe('emotion/glamor', () => { - describe('babel', () => { - test('basic', () => { - const basic = `(
)` - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - - test('no css attr', () => { - const basic = '(
)' - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - - test('css empty', () => { - const basic = '(
)' - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - - test('wrong value type', () => { - const basic = '(
)' - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - - test('no className', () => { - const basic = `(
)` - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - - test('emptyClassName', () => { - const basic = `(
)` - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - - test('className as expression', () => { - const basic = `(
)` - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - - test('className as expression string', () => { - const basic = - "(
)" - const {code} = babel.transform(basic, {plugins: [plugin]}) - expect(code).toMatchSnapshot() - }) - }) - - describe('real', () => { - test('basic', () => { - const tree = renderer - .create( -

- hello world -

- ) - .toJSON() - - expect(tree).toMatchSnapshotWithGlamor() - }) - - test('kitchen sink', () => { - const tree = renderer - .create( -
-

- BOOM -

-

Hello

-

- World -

-

- hello world -

- -
- ) - .toJSON() - - expect(tree).toMatchSnapshotWithGlamor() - }) - }) -}) diff --git a/src/glamor/babel.js b/src/glamor/babel.js deleted file mode 100644 index ceaa23b59..000000000 --- a/src/glamor/babel.js +++ /dev/null @@ -1,37 +0,0 @@ -module.exports = function (babel) { - const {types: t} = babel - - return { - name: 'emotion-for-glam', // not required - inherits: require('babel-plugin-syntax-jsx'), - visitor: { - JSXOpeningElement (path, state) { - let cssPath - - path.get('attributes').forEach(openElPath => { - const attrPath = openElPath.get('name') - const name = attrPath.node.name - - if (name === 'css') { - cssPath = attrPath - } - }) - - if (!cssPath) return - - let cssPropValue = cssPath.container && cssPath.container.value - - if (t.isJSXExpressionContainer(cssPropValue)) { - cssPropValue = cssPropValue.expression - } - - let glamorCssFunction = t.callExpression(t.identifier('css'), [ - cssPropValue - ]) - - cssPath.parentPath.insertAfter(t.jSXSpreadAttribute(glamorCssFunction)) - cssPath.parentPath.remove() - } - } - } -} diff --git a/src/react.js b/src/react.js new file mode 100644 index 000000000..52261908e --- /dev/null +++ b/src/react.js @@ -0,0 +1,14 @@ +import React from 'react' + +const h = React.createElement + +export default function glam (tag, className, {filename} = {}) { + return class Target extends React.Component { + render () { + return h(tag, { + className: (this.props.className || '') + ' ' + className, + ...this.props + }) + } + } +} From e13c6dc61786912b914f92ca6ae338f353996b68 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Thu, 1 Jun 2017 16:11:29 -0600 Subject: [PATCH 03/10] =?UTF-8?q?=F0=9F=91=A9=E2=80=8D=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 30 +++++++++++++++++++++++++++++- src/__tests__/react.js | 33 +++++++++++++++++++++++++++++---- src/babel.js | 11 +++++++++++ src/react.js | 4 ++-- 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ff731d551..2da4285e3 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ npm install -S emotion glam import glam from 'emotion' const fontSize = 48 -const H1 = glam.h1` +const H1 = glam('h1')` font-size: ${fontSize}px; color: 'blue'; ` @@ -50,6 +50,34 @@ const H1 = glam('h1', css` function Greeting ({ name }) { return

Hello {name}

// blue, 48px text } + +/* +

+ hello world +

+*/ + +// You can also pass components in + +const H2 = glam(H1)` + font-size: ${fontSize * 2/3}px; + color: 'red'; +` + +function Greeting ({ name }) { + return

Hello {name}

// blue, 48px text +} + +/* +

+ hello world +

+*/ +// results ``` diff --git a/src/__tests__/react.js b/src/__tests__/react.js index 65c03c606..6e1766253 100644 --- a/src/__tests__/react.js +++ b/src/__tests__/react.js @@ -2,7 +2,7 @@ /* eslint-env jest */ import React from 'react' import renderer from 'react-test-renderer' -import css, { fragment } from 'glam' +import css, {fragment} from 'glam' import glam from '../react' import plugin from '../babel' @@ -15,6 +15,12 @@ describe('glam react', () => { const {code} = babel.transform(basic, {plugins: [plugin, 'glam/babel']}) expect(code).toMatchSnapshot() }) + + test('function call', () => { + const basic = 'glam(MyComponent)\`font-size: \$\{fontSize\}px;\`' + const {code} = babel.transform(basic, {plugins: [plugin, 'glam/babel']}) + expect(code).toMatchSnapshot() + }) }) test('basic render', () => { @@ -34,15 +40,15 @@ describe('glam react', () => { expect(tree).toMatchSnapshot() }) - test('passes props', () => { + test('call expression', () => { const fontSize = 20 - const H1 = glam.h1` + const H1 = glam('h1')` font-size: ${fontSize}px; ` const tree = renderer .create( -

+

hello world

) @@ -50,4 +56,23 @@ describe('glam react', () => { expect(tree).toMatchSnapshot() }) + + test('composition', () => { + const fontSize = 20 + const H1 = glam('h1')` + font-size: ${fontSize}px; + ` + + const H2 = glam(H1)`font-size: ${fontSize * 2 / 3}` + + const tree = renderer + .create( +

+ hello world +

+ ) + .toJSON() + + expect(tree).toMatchSnapshot() + }) }) diff --git a/src/babel.js b/src/babel.js index 643253fb8..9bac7d11c 100644 --- a/src/babel.js +++ b/src/babel.js @@ -118,6 +118,17 @@ module.exports = function (babel) { t.taggedTemplateExpression(t.identifier('css'), path.node.quasi) ]) ) + } else if ( + t.isCallExpression(path.node.tag) && + path.node.tag.callee.name === 'glam' && + t.isTemplateLiteral(path.node.quasi) + ) { + path.replaceWith( + t.callExpression(t.identifier(path.node.tag.callee.name), [ + path.node.tag.arguments[0], + t.taggedTemplateExpression(t.identifier('css'), path.node.quasi) + ]) + ) } } } diff --git a/src/react.js b/src/react.js index 52261908e..59cb42251 100644 --- a/src/react.js +++ b/src/react.js @@ -6,8 +6,8 @@ export default function glam (tag, className, {filename} = {}) { return class Target extends React.Component { render () { return h(tag, { - className: (this.props.className || '') + ' ' + className, - ...this.props + ...this.props, + className: (this.props.className || '') + ' ' + className }) } } From 2b2be2f11647bbceb64281501df02671e7d609e4 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Thu, 1 Jun 2017 16:12:09 -0600 Subject: [PATCH 04/10] Add snapshots --- src/__tests__/__snapshots__/babel.js.snap | 60 +++++++++++++++++++++++ src/__tests__/__snapshots__/react.js.snap | 29 +++++++++++ src/__tests__/babel.js.css | 26 ++++++++++ src/__tests__/react.js.css | 3 ++ 4 files changed, 118 insertions(+) create mode 100644 src/__tests__/__snapshots__/babel.js.snap create mode 100644 src/__tests__/__snapshots__/react.js.snap create mode 100644 src/__tests__/babel.js.css create mode 100644 src/__tests__/react.js.css diff --git a/src/__tests__/__snapshots__/babel.js.snap b/src/__tests__/__snapshots__/babel.js.snap new file mode 100644 index 000000000..c7a78296b --- /dev/null +++ b/src/__tests__/__snapshots__/babel.js.snap @@ -0,0 +1,60 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`emotion/glam babel css prop StringLiteral css prop value 1`] = `"
;"`; + +exports[`emotion/glam babel css prop basic 1`] = `"
;"`; + +exports[`emotion/glam babel css prop className as expression 1`] = `"
;"`; + +exports[`emotion/glam babel css prop className as expression string 1`] = `"
;"`; + +exports[`emotion/glam babel css prop css empty 1`] = `"
;"`; + +exports[`emotion/glam babel css prop emptyClassName 1`] = `"
;"`; + +exports[`emotion/glam babel css prop no css attr 1`] = `"
;"`; + +exports[`emotion/glam babel css prop noClassName 1`] = `"
;"`; + +exports[`emotion/glam real basic 1`] = ` +

+ hello world +

+`; + +exports[`emotion/glam real kitchen sink 1`] = ` +
+

+ BOOM +

+

+ Hello +

+

+ World +

+

+ hello world +

+
+`; + +exports[`emotion/glam real string expression 1`] = ` +

+ hello world +

+`; diff --git a/src/__tests__/__snapshots__/react.js.snap b/src/__tests__/__snapshots__/react.js.snap new file mode 100644 index 000000000..5e03f52d1 --- /dev/null +++ b/src/__tests__/__snapshots__/react.js.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`glam react babel glam component basic 1`] = `"glam(\\"h1\\", css('css-8xpzga', [fontSize]));"`; + +exports[`glam react babel glam component function call 1`] = `"glam(MyComponent, css('css-8xpzga', [fontSize]));"`; + +exports[`glam react basic render 1`] = ` +

+ hello world +

+`; + +exports[`glam react call expression 1`] = ` +

+ hello world +

+`; + +exports[`glam react composition 1`] = ` +

+ hello world +

+`; diff --git a/src/__tests__/babel.js.css b/src/__tests__/babel.js.css new file mode 100644 index 000000000..5709cc9fd --- /dev/null +++ b/src/__tests__/babel.js.css @@ -0,0 +1,26 @@ +/* do not edit this file */ +.css-jk0pkr { color: red; } +.css-7sqgip { color: red;background: blue;font-size: 48px; } +.frag-15qcrjv { --frag-15qcrjv: { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; + font-weight: bold; }; } +.frag-193n5ss { --frag-193n5ss: { @apply --frag-193n5ss-0; + font-size: var(--frag-193n5ss-1) }; } +.frag-1wh34bm { --frag-1wh34bm: { font-size: var(--frag-1wh34bm-0) }; } +.frag-mdk64u { --frag-mdk64u: { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; + justify-content: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center }; } +.css-1170hdn { @apply --css-1170hdn-0 + @apply --css-1170hdn-1; } +.css-wjhr0q { @apply --css-wjhr0q-0; + color: red } +.css-14jcta0 { color: blue; } +.css-1gdxe9e { display: -webkit-box; display: -moz-box; display: -ms-inline-flexbox; display: -webkit-inline-flex; display: inline-flex } +.css-1g8ltio { color: red; + border-radius: var(--css-1g8ltio-0); } +.css-1g8ltio:hover { font-weight: bold; color: var(--css-1g8ltio-1); } \ No newline at end of file diff --git a/src/__tests__/react.js.css b/src/__tests__/react.js.css new file mode 100644 index 000000000..50c474777 --- /dev/null +++ b/src/__tests__/react.js.css @@ -0,0 +1,3 @@ +/* do not edit this file */ +.css-13wdnau { font-size: var(--css-13wdnau-0)px; } +.css-vxb7tq { font-size: var(--css-vxb7tq-0) } \ No newline at end of file From 345b9568e3b5a06e958a3ee82c181a6fa9eddd0a Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Thu, 1 Jun 2017 16:14:04 -0600 Subject: [PATCH 05/10] update readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2da4285e3..d43db411d 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ function Greeting ({ name }) {

- hello world + Hello user

*/ @@ -67,14 +67,14 @@ const H2 = glam(H1)` ` function Greeting ({ name }) { - return

Hello {name}

// blue, 48px text + return

Hello {name}

// red, 32px text } /*

- hello world + Hello user

*/ // results From aae2a4cc53b8be99a37681c06ead485a2b1acfec Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Thu, 1 Jun 2017 16:27:10 -0600 Subject: [PATCH 06/10] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d43db411d..97ddcce23 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # emotion -## 👩‍🚀 Glam + React +## 👩‍🎤 Glam + React [![npm version](https://badge.fury.io/js/emotion.svg)](https://badge.fury.io/js/emotion) [![Build Status](https://travis-ci.org/tkh44/emotion.svg?branch=master)](https://travis-ci.org/tkh44/emotion) @@ -73,7 +73,7 @@ function Greeting ({ name }) { /*

+>` Hello user

*/ From 1199b986aa16961d81d5373479f630000f42dc6b Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Thu, 1 Jun 2017 21:10:00 -0600 Subject: [PATCH 07/10] Add fn as expression value support! --- README.md | 5 +++-- package.json | 3 ++- src/__tests__/__snapshots__/react.js.snap | 13 +++++++++++-- src/__tests__/react.js | 19 +++++++++++++++++++ src/babel.js | 14 ++++++++++++-- src/react.js | 10 ++++++++-- 6 files changed, 55 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 97ddcce23..1b8752593 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ import glam from 'emotion' const fontSize = 48 const H1 = glam('h1')` font-size: ${fontSize}px; + transform: scale(${props => props.scale}); color: 'blue'; ` @@ -48,7 +49,7 @@ const H1 = glam('h1', css` // can be used as any other normal component function Greeting ({ name }) { - return

Hello {name}

// blue, 48px text + return

Hello {name}

// blue, 48px, and scaled 2x text } /* @@ -67,7 +68,7 @@ const H2 = glam(H1)` ` function Greeting ({ name }) { - return

Hello {name}

// red, 32px text + return

Hello {name}

// red, 32px, and scaled 2x text } /* diff --git a/package.json b/package.json index a5dd4faff..54ee32ef3 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "cxs": "^3.0.4", "glam": "^4.0.3", "glamor": "^2.20.25", - "jest": "^20.0.1", + "jest": "^20.0.4", + "jest-cli": "^20.0.4", "jest-glamor-react": "^1.3.0", "react": "^15.5.4", "react-addons-test-utils": "^15.5.1", diff --git a/src/__tests__/__snapshots__/react.js.snap b/src/__tests__/__snapshots__/react.js.snap index 5e03f52d1..4a200f80c 100644 --- a/src/__tests__/__snapshots__/react.js.snap +++ b/src/__tests__/__snapshots__/react.js.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`glam react babel glam component basic 1`] = `"glam(\\"h1\\", css('css-8xpzga', [fontSize]));"`; +exports[`glam react babel glam component basic 1`] = `"glam(\\"h1\\", ['css-8xpzga', [fontSize]]);"`; -exports[`glam react babel glam component function call 1`] = `"glam(MyComponent, css('css-8xpzga', [fontSize]));"`; +exports[`glam react babel glam component function call 1`] = `"glam(MyComponent, ['css-8xpzga', [fontSize]]);"`; exports[`glam react basic render 1`] = `

`; + +exports[`glam react function in expression 1`] = ` +

+ hello world +

+`; diff --git a/src/__tests__/react.js b/src/__tests__/react.js index 6e1766253..1915e4d97 100644 --- a/src/__tests__/react.js +++ b/src/__tests__/react.js @@ -75,4 +75,23 @@ describe('glam react', () => { expect(tree).toMatchSnapshot() }) + + test('function in expression', () => { + const fontSize = 20 + const H1 = glam('h1')` + font-size: ${fontSize}px; + ` + + const H2 = glam(H1)`font-size: ${({ scale }) => fontSize * scale}` + + const tree = renderer + .create( +

+ hello world +

+ ) + .toJSON() + + expect(tree).toMatchSnapshot() + }) }) diff --git a/src/babel.js b/src/babel.js index 9bac7d11c..d1d8d1171 100644 --- a/src/babel.js +++ b/src/babel.js @@ -16,6 +16,18 @@ module.exports = function (babel) { name: 'emotion-for-glam', // not required inherits: require('babel-plugin-syntax-jsx'), visitor: { + CallExpression (path) { + if (path.node.callee.name === 'css') { + const parentPath = path.parentPath + if ( + parentPath.isCallExpression() && + parentPath.node.callee && + parentPath.node.callee.name === 'glam' + ) { + path.replaceWithMultiple(t.arrayExpression(path.node.arguments)) + } + } + }, JSXOpeningElement (path, state) { let cssPath let classNamesPath @@ -41,8 +53,6 @@ module.exports = function (babel) { classNamesPath.container && classNamesPath.container.value - // if (!cssPropValue) return - if (t.isJSXExpressionContainer(cssPropValue)) { cssPropValue = cssPropValue.expression } diff --git a/src/react.js b/src/react.js index 59cb42251..6e0c69d36 100644 --- a/src/react.js +++ b/src/react.js @@ -1,13 +1,19 @@ import React from 'react' +import css from 'glam' const h = React.createElement -export default function glam (tag, className, {filename} = {}) { +export default function glam (tag, [cls, vars]) { return class Target extends React.Component { render () { + const finalClassName = css( + cls, + vars.map(v => (typeof v === 'function' ? v(this.props) : v)) + ) + return h(tag, { ...this.props, - className: (this.props.className || '') + ' ' + className + className: (this.props.className || '') + ' ' + finalClassName }) } } From b18003d728345958719aa9f72160f1ec466a550c Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Thu, 1 Jun 2017 21:14:54 -0600 Subject: [PATCH 08/10] clean up readme --- README.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 1b8752593..ed92606d2 100644 --- a/README.md +++ b/README.md @@ -32,20 +32,12 @@ npm install -S emotion glam ```jsx harmony import glam from 'emotion' -const fontSize = 48 const H1 = glam('h1')` - font-size: ${fontSize}px; - transform: scale(${props => props.scale}); color: 'blue'; + font-size: 48px; + transform: scale(${props => props.scale}); ` -// is compiled to - -const H1 = glam('h1', css` - font-size: ${fontSize}px; - color: 'blue'; -`) - // can be used as any other normal component function Greeting ({ name }) { From d7619712a00658d36f981e4dc026a8e5b33766ab Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Thu, 1 Jun 2017 21:30:06 -0600 Subject: [PATCH 09/10] Add build step --- README.md | 2 +- glam.js | 3 ++- package.json | 43 ++++++++++++++++++++++++++++++------------- rollup.config.js | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 rollup.config.js diff --git a/README.md b/README.md index ed92606d2..d3525d808 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ npm install -S emotion glam ### glam -```jsx harmony +```jsx import glam from 'emotion' const H1 = glam('h1')` diff --git a/glam.js b/glam.js index d8d05ee65..a31073090 100644 --- a/glam.js +++ b/glam.js @@ -1 +1,2 @@ -module.exports = require('./src/babel') +function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function r(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function n(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function o(e,o){var p=o[0],f=o[1];return function(o){function i(){return t(this,i),r(this,o.apply(this,arguments))}return n(i,o),i.prototype.render=function(){var t=this,r=a(p,f.map(function(e){return"function"==typeof e?e(t.props):e}));return c(e,u({},this.props,{className:(this.props.className||"")+" "+r}))},i}(i.Component)}Object.defineProperty(exports,"__esModule",{value:!0});var i=e(require("react")),a=e(require("glam")),u=Object.assign||function(e){for(var t=1;t Date: Thu, 1 Jun 2017 21:39:20 -0600 Subject: [PATCH 10/10] Fix build dir --- README.md | 2 +- glam.js | 3 +-- package.json | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d3525d808..ef22a8cb3 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ function Greeting ({ name }) { ### css prop -When using the emotion babel plugin any `css` prop is converted to a tagged template expression and appended to any existing class names. +When using the emotion babel plugin, any `css` prop is converted to a class name via glam and appended to any existing class names. ```jsx harmony diff --git a/glam.js b/glam.js index a31073090..93e603a50 100644 --- a/glam.js +++ b/glam.js @@ -1,2 +1 @@ -function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function r(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function n(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function o(e,o){var p=o[0],f=o[1];return function(o){function i(){return t(this,i),r(this,o.apply(this,arguments))}return n(i,o),i.prototype.render=function(){var t=this,r=a(p,f.map(function(e){return"function"==typeof e?e(t.props):e}));return c(e,u({},this.props,{className:(this.props.className||"")+" "+r}))},i}(i.Component)}Object.defineProperty(exports,"__esModule",{value:!0});var i=e(require("react")),a=e(require("glam")),u=Object.assign||function(e){for(var t=1;t