├── .yarnrc
├── docs
├── _config.yml
├── benchmarks.md
├── with-props.md
├── webpack.md
├── inject-global.md
├── instances.md
├── theming.md
├── nested.md
├── keyframes.md
├── install.md
├── source-maps.md
├── composition.md
├── extract-static.md
├── media-queries.md
├── object-styles.md
├── index.md
├── README.md
├── testing.md
├── css.md
├── babel.md
└── ssr.md
├── emotion.png
├── packages
├── emotion
│ ├── macro.js
│ ├── test
│ │ ├── no-babel
│ │ │ └── .babelrc
│ │ ├── auto-label
│ │ │ ├── .babelrc
│ │ │ ├── auto-label.test.js
│ │ │ └── __snapshots__
│ │ │ │ └── auto-label.test.js.snap
│ │ ├── extract
│ │ │ ├── .babelrc
│ │ │ ├── extract.test.emotion.css
│ │ │ ├── __snapshots__
│ │ │ │ └── extract.test.js.snap
│ │ │ └── extract.test.js
│ │ ├── source-map
│ │ │ ├── .babelrc
│ │ │ └── source-map.test.js
│ │ ├── prod-mode
│ │ │ ├── .babelrc
│ │ │ ├── __snapshots__
│ │ │ │ └── prod-mode.test.js.snap
│ │ │ └── prod-mode.test.js
│ │ ├── __snapshots__
│ │ │ ├── label-pattern.test.js.snap
│ │ │ ├── sheet.dom.test.js.snap
│ │ │ ├── component-selector.test.js.snap
│ │ │ ├── cx.test.js.snap
│ │ │ ├── inject-global.test.js.snap
│ │ │ ├── selectivity.test.js.snap
│ │ │ └── css-prop.test.js.snap
│ │ ├── label-pattern.test.js
│ │ ├── component-selector.test.js
│ │ ├── sheet.dom.test.js
│ │ ├── keyframes.test.js
│ │ └── inject-global.test.js
│ ├── types
│ │ ├── tslint.json
│ │ ├── tsconfig.json
│ │ ├── tests.tsx
│ │ └── index.d.ts
│ ├── src
│ │ └── index.js
│ └── package.json
├── emotion-theming
│ ├── src
│ │ ├── channel.js
│ │ ├── index.js
│ │ ├── utils.js
│ │ ├── create-broadcast.js
│ │ └── with-theme.js
│ ├── types
│ │ ├── tslint.json
│ │ ├── tsconfig.json
│ │ ├── index.d.ts
│ │ └── tests.tsx
│ ├── package.json
│ └── test
│ │ ├── test-helpers.js
│ │ └── __snapshots__
│ │ └── index.test.js.snap
├── preact-emotion
│ ├── macro.js
│ ├── src
│ │ └── index.js
│ └── package.json
├── react-emotion
│ ├── macro.js
│ ├── types
│ │ ├── tslint.json
│ │ └── tsconfig.json
│ ├── test
│ │ ├── component-selectors
│ │ │ ├── .babelrc
│ │ │ └── component-selector.test.js
│ │ └── auto-label
│ │ │ └── .babelrc
│ ├── src
│ │ └── index.js
│ └── package.json
├── babel-plugin-emotion
│ ├── test
│ │ ├── styled
│ │ │ └── macro.js
│ │ ├── .babelrc
│ │ ├── macro
│ │ │ ├── .babelrc
│ │ │ ├── __snapshots__
│ │ │ │ └── inject-global.test.js.snap
│ │ │ ├── babel-macros-register.js
│ │ │ ├── inject-global.test.js
│ │ │ └── keyframes.test.js
│ │ ├── __snapshots__
│ │ │ └── fs.test.js.snap
│ │ ├── keyframes.test.js
│ │ ├── fs.test.js
│ │ ├── source-map.test.js
│ │ └── inject-global.test.js
│ ├── src
│ │ ├── source-map.js
│ │ ├── macro.js
│ │ ├── macro-styled.js
│ │ └── ast-object.js
│ └── package.json
├── site
│ ├── favicon.ico
│ ├── README.md
│ ├── src
│ │ ├── blocks
│ │ │ ├── intro.example
│ │ │ ├── styled-with-object.example
│ │ │ ├── named.example
│ │ │ ├── nested.example
│ │ │ ├── styled-with-component.example
│ │ │ ├── media-queries.example
│ │ │ ├── composition.example
│ │ │ ├── .eslintrc
│ │ │ ├── styled.example
│ │ │ ├── theming.example
│ │ │ ├── keyframes.example
│ │ │ ├── css.example
│ │ │ └── object-styles.example
│ │ └── index.tpl.html
│ ├── .babelrc
│ ├── package.json
│ └── webpack.config.js
├── benchmarks
│ ├── src
│ │ ├── glamorous.js
│ │ ├── emotion-css.js
│ │ ├── emotion-obj.js
│ │ ├── styled-components.js
│ │ ├── css-modules.js
│ │ ├── glamor.js
│ │ ├── emotion.js
│ │ └── components
│ │ │ ├── Wrapper
│ │ │ ├── emotion.js
│ │ │ └── glamor.js
│ │ │ ├── View
│ │ │ ├── styles.css
│ │ │ ├── css-modules.js
│ │ │ ├── emotion.js
│ │ │ ├── styled-components.js
│ │ │ ├── glamorous.js
│ │ │ ├── emotion-obj.js
│ │ │ ├── emotion-css.js
│ │ │ └── glamor.js
│ │ │ ├── Dot
│ │ │ ├── emotion.js
│ │ │ └── glamor.js
│ │ │ ├── Box
│ │ │ ├── styles.css
│ │ │ ├── css-modules.js
│ │ │ ├── glamorous.js
│ │ │ ├── emotion-obj.js
│ │ │ ├── emotion.js
│ │ │ ├── styled-components.js
│ │ │ ├── glamor.js
│ │ │ └── emotion-css.js
│ │ │ ├── NestedTree.js
│ │ │ └── SierpinskiTriangle.js
│ ├── index.html
│ ├── README.md
│ ├── tests
│ │ ├── renderDeepTree.js
│ │ ├── renderWideTree.js
│ │ └── renderSierpinskiTriangle.js
│ ├── createRenderBenchmark.js
│ ├── send-results.js
│ ├── package.json
│ ├── webpack.config.js
│ ├── run-headless.js
│ ├── index.js
│ └── benchmark.js
├── emotion-server
│ ├── test
│ │ ├── auto-label
│ │ │ └── .babelrc
│ │ ├── index.test.js
│ │ ├── inline.test.js
│ │ └── stream.test.js
│ ├── src
│ │ └── index.js
│ └── package.json
├── create-emotion
│ ├── test
│ │ ├── __snapshots__
│ │ │ └── instance.test.js.snap
│ │ ├── .babelrc
│ │ ├── instance.test.js
│ │ ├── emotion-instance.js
│ │ ├── inline.test.js
│ │ └── stream.test.js
│ ├── package.json
│ └── src
│ │ └── utils.js
├── create-emotion-server
│ ├── src
│ │ ├── index.js
│ │ ├── extract-critical.js
│ │ ├── stream.js
│ │ └── inline.js
│ ├── README.md
│ └── package.json
├── create-emotion-styled
│ ├── README.md
│ ├── package.json
│ └── src
│ │ └── utils.js
├── jest-emotion
│ ├── package.json
│ ├── src
│ │ └── replace-class-names.js
│ └── README.md
└── emotion-utils
│ ├── package.json
│ └── src
│ ├── index.js
│ └── hash.js
├── .eslintignore
├── .gitignore
├── netlify.toml
├── babel-plugin-emotion-test.js
├── jest.dist.js
├── codecov.yml
├── test
├── styleTransform.js
├── pretty-css.js
└── testSetup.js
├── jest.config.js
├── lerna.json
├── .travis.yml
├── .babelrc
├── CONTRIBUTING.md
├── flow-typed
└── npm
│ ├── jest-glamor-react_vx.x.x.js
│ └── react-test-renderer_vx.x.x.js
├── .flowconfig
├── LICENSE
├── .github
├── PULL_REQUEST_TEMPLATE.md
└── ISSUE_TEMPLATE.md
└── rollup.config.js
/.yarnrc:
--------------------------------------------------------------------------------
1 | workspaces-experimental true
2 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-minimal
--------------------------------------------------------------------------------
/emotion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stereobooster/emotion/master/emotion.png
--------------------------------------------------------------------------------
/packages/emotion/macro.js:
--------------------------------------------------------------------------------
1 | module.exports = require('babel-plugin-emotion/lib/macro')
2 |
--------------------------------------------------------------------------------
/packages/emotion-theming/src/channel.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export default '__EMOTION_THEMING__'
3 |
--------------------------------------------------------------------------------
/packages/preact-emotion/macro.js:
--------------------------------------------------------------------------------
1 | module.exports = require('babel-plugin-emotion/lib/macro-styled')
2 |
--------------------------------------------------------------------------------
/packages/react-emotion/macro.js:
--------------------------------------------------------------------------------
1 | module.exports = require('babel-plugin-emotion/lib/macro-styled')
2 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/test/styled/macro.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../../src/macro-styled')
2 |
--------------------------------------------------------------------------------
/packages/site/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stereobooster/emotion/master/packages/site/favicon.ico
--------------------------------------------------------------------------------
/docs/benchmarks.md:
--------------------------------------------------------------------------------
1 | # Benchmarks
2 |
3 | https://github.com/A-gambit/CSS-IN-JS-Benchmarks/blob/master/RESULT.md
4 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | lib/
2 | dist/
3 | coverage/
4 | node_modules/
5 | stylis.js
6 | /demo/dist
7 | /packages/site/build
8 | flow-typed/
--------------------------------------------------------------------------------
/packages/emotion/test/no-babel/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "flow",
4 | "env",
5 | "react"
6 | ],
7 | "plugins": ["codegen"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/site/README.md:
--------------------------------------------------------------------------------
1 | # emotion example project
2 |
3 | ## Start
4 |
5 | `npm install`
6 |
7 | `npm start`
8 |
9 | Go to `localhost:3000`
10 |
--------------------------------------------------------------------------------
/packages/emotion/types/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "dtslint/dtslint.json",
3 | "rules": {
4 | "no-relative-import-in-test": false
5 | }
6 | }
--------------------------------------------------------------------------------
/packages/react-emotion/types/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "dtslint/dtslint.json",
3 | "rules": {
4 | "no-relative-import-in-test": false
5 | }
6 | }
--------------------------------------------------------------------------------
/packages/emotion-theming/types/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "dtslint/dtslint.json",
3 | "rules": {
4 | "no-relative-import-in-test": false
5 | }
6 | }
--------------------------------------------------------------------------------
/packages/site/src/blocks/intro.example:
--------------------------------------------------------------------------------
1 | const Avatar = styled.img`
2 | width: 96px;
3 | height: 96px;
4 | border-radius: 50%;
5 | `
6 |
7 | render( )
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /demo/dist
3 | dist/
4 | lib/
5 | node_modules/
6 | *.log
7 | .idea
8 | packages/site/build
9 | package-lock.json
10 | .DS_Store
11 | .cache
12 | public/
--------------------------------------------------------------------------------
/packages/benchmarks/src/glamorous.js:
--------------------------------------------------------------------------------
1 | import Box from './components/Box/glamorous'
2 | import View from './components/View/glamorous'
3 |
4 | export default {
5 | Box,
6 | View
7 | }
8 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/emotion-css.js:
--------------------------------------------------------------------------------
1 | import Box from './components/Box/emotion-css'
2 | import View from './components/View/emotion-css'
3 |
4 | export default {
5 | Box,
6 | View
7 | }
8 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/emotion-obj.js:
--------------------------------------------------------------------------------
1 | import Box from './components/Box/emotion-obj'
2 | import View from './components/View/emotion-obj'
3 |
4 | export default {
5 | Box,
6 | View
7 | }
8 |
--------------------------------------------------------------------------------
/packages/site/src/blocks/styled-with-object.example:
--------------------------------------------------------------------------------
1 | const Avatar = styled('img')({
2 | width: 96,
3 | height: 96,
4 | borderRadius: '50%'
5 | })
6 |
7 | render( )
8 |
--------------------------------------------------------------------------------
/packages/site/src/blocks/named.example:
--------------------------------------------------------------------------------
1 | const Avatar = styled('img')`
2 | name: emotion-avatar;
3 | width: 96px;
4 | height: 96px;
5 | border-radius: 50%;
6 | `
7 |
8 | render( )
9 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/styled-components.js:
--------------------------------------------------------------------------------
1 | import Box from './components/Box/styled-components'
2 | import View from './components/View/styled-components'
3 |
4 | export default {
5 | Box,
6 | View
7 | }
8 |
--------------------------------------------------------------------------------
/packages/react-emotion/test/component-selectors/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "flow",
4 | "env",
5 | "react"
6 | ],
7 | "plugins": [["../../../../babel-plugin-emotion-test"], "codegen"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/emotion-theming/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export { default as ThemeProvider } from './theme-provider'
3 | export { default as withTheme } from './with-theme'
4 | export { channel, contextTypes } from './utils'
5 |
--------------------------------------------------------------------------------
/packages/emotion/test/auto-label/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "flow",
4 | "env",
5 | "react"
6 | ],
7 | "plugins": [["../../../../babel-plugin-emotion-test", {"autoLabel": true}], "codegen"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/emotion/test/extract/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "flow",
4 | "env",
5 | "react"
6 | ],
7 | "plugins": [["../../../../babel-plugin-emotion-test", {"extractStatic": true}], "codegen"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/emotion/test/source-map/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "flow",
4 | "env",
5 | "react"
6 | ],
7 | "plugins": [["../../../../babel-plugin-emotion-test", {"sourceMap": true}], "codegen"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/emotion-server/test/auto-label/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "flow",
4 | "env",
5 | "react"
6 | ],
7 | "plugins": [["../../../../babel-plugin-emotion-test", {"autoLabel": true}], "codegen"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/react-emotion/test/auto-label/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "flow",
4 | "env",
5 | "react"
6 | ],
7 | "plugins": [["../../../../babel-plugin-emotion-test", {"autoLabel": true}], "codegen"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/css-modules.js:
--------------------------------------------------------------------------------
1 | import Box from './components/Box/css-modules'
2 | import View from './components/View/css-modules'
3 |
4 | const api = {
5 | Box,
6 | View
7 | }
8 |
9 | export default api
10 |
--------------------------------------------------------------------------------
/packages/benchmarks/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Performance tests
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/packages/preact-emotion/src/index.js:
--------------------------------------------------------------------------------
1 | import preact from 'preact'
2 | import * as emotion from 'emotion'
3 | import createEmotionStyled from 'create-emotion-styled'
4 |
5 | export default createEmotionStyled(emotion, preact)
6 |
7 | export * from 'emotion'
8 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | command = "rm -rf /opt/buildhome/.yarn && curl -o- -L https://yarnpkg.com/install.sh | bash && export PATH=\"$HOME/.yarn/bin:$PATH\" && yarn && npm run bootstrap && npm run build && npm run build:site"
3 | publish = "packages/site/build"
--------------------------------------------------------------------------------
/packages/react-emotion/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React from 'react'
3 | import * as emotion from 'emotion'
4 | import createEmotionStyled from 'create-emotion-styled'
5 |
6 | export default createEmotionStyled(emotion, React)
7 |
8 | export * from 'emotion'
9 |
--------------------------------------------------------------------------------
/packages/site/src/blocks/nested.example:
--------------------------------------------------------------------------------
1 | const Avatar = styled('div')`
2 | & img {
3 | width: 96px;
4 | height: 96px;
5 | border-radius: 50%;
6 | }
7 | `
8 |
9 | render(
10 |
11 |
12 |
13 | )
14 |
--------------------------------------------------------------------------------
/packages/site/src/blocks/styled-with-component.example:
--------------------------------------------------------------------------------
1 | const Circle = styled('span')`
2 | width: 96px;
3 | height: 96px;
4 | border-radius: 50%;
5 | `
6 |
7 | const Avatar = Circle.withComponent('img')
8 |
9 | render( )
10 |
--------------------------------------------------------------------------------
/packages/create-emotion/test/__snapshots__/instance.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`general instance tests throws with invalid key 1`] = `"Emotion key must only contain lower case alphabetical characters and - but \\"css1\\" was passed"`;
4 |
--------------------------------------------------------------------------------
/babel-plugin-emotion-test.js:
--------------------------------------------------------------------------------
1 | require('babel-register')
2 | const path = require('path')
3 | require('module-alias').addAliases({
4 | 'emotion-utils': path.resolve(__dirname, './packages/emotion-utils/src')
5 | })
6 |
7 | module.exports = require('./packages/babel-plugin-emotion/src')
8 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/test/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env",
5 | {
6 | "loose": true,
7 | "exclude": ["transform-es2015-typeof-symbol"]
8 | }
9 | ],
10 | "stage-0",
11 | "react",
12 | "flow"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/packages/emotion-server/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import createEmotionServer from 'create-emotion-server'
3 | import * as emotion from 'emotion'
4 |
5 | export const {
6 | extractCritical,
7 | renderStylesToString,
8 | renderStylesToNodeStream
9 | } = createEmotionServer(emotion)
10 |
--------------------------------------------------------------------------------
/packages/site/src/blocks/media-queries.example:
--------------------------------------------------------------------------------
1 | const Avatar = styled('img')`
2 | width: 32px;
3 | height: 32px;
4 | border-radius: 50%;
5 |
6 | @media (min-width: 420px) {
7 | width: 96px;
8 | height: 96px;
9 | }
10 | `
11 |
12 | render( )
13 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/glamor.js:
--------------------------------------------------------------------------------
1 | import Box from './components/Box/glamor'
2 | import View from './components/View/glamor'
3 | import Dot from './components/Dot/glamor'
4 | import Wrapper from './components/Wrapper/glamor'
5 |
6 | export default {
7 | Box,
8 | View,
9 | Wrapper,
10 | Dot
11 | }
12 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/emotion.js:
--------------------------------------------------------------------------------
1 | import Box from './components/Box/emotion'
2 | import View from './components/View/emotion'
3 | import Dot from './components/Dot/emotion'
4 | import Wrapper from './components/Wrapper/emotion'
5 |
6 | export default {
7 | Box,
8 | View,
9 | Dot,
10 | Wrapper
11 | }
12 |
--------------------------------------------------------------------------------
/packages/emotion-theming/src/utils.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import PropTypes from 'prop-types'
3 | import channel from './channel'
4 |
5 | export const contextTypes = {
6 | [channel]: PropTypes.object
7 | }
8 |
9 | export { default as channel } from './channel'
10 |
11 | export type Theme = Object | ((theme: Object) => Object)
12 |
--------------------------------------------------------------------------------
/packages/emotion/test/prod-mode/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "flow",
4 | "env",
5 | "react"
6 | ],
7 | "plugins": [
8 | [
9 | "transform-define",
10 | {
11 | "process.env.NODE_ENV": "production"
12 | }
13 | ], ["../../../../babel-plugin-emotion-test"], "codegen"]
14 | }
15 |
--------------------------------------------------------------------------------
/packages/site/src/blocks/composition.example:
--------------------------------------------------------------------------------
1 | const imageBase = css`
2 | width: 32px;
3 | height: 32px;
4 | border-radius: 50%;
5 | `
6 |
7 | const Avatar = styled('img')`
8 | ${imageBase};
9 |
10 | @media (min-width: 420px) {
11 | width: 96px;
12 | height: 96px;
13 | }
14 | `
15 |
16 | render( )
17 |
--------------------------------------------------------------------------------
/packages/site/src/blocks/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "React": false,
4 | "css": false,
5 | "styled": false,
6 | "colors": false,
7 | "render": false,
8 | "ReactMarkdown": false,
9 | "docMarkdown": false
10 | },
11 | "rules": {
12 | max-len: [
13 | "error",
14 | 50
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/jest.dist.js:
--------------------------------------------------------------------------------
1 | const { jest: lernaAliases } = require('lerna-alias')
2 | const baseConfig = require('./jest.config.js')
3 |
4 | module.exports = Object.assign({}, baseConfig, {
5 | moduleNameMapper: lernaAliases({ sourceDirectory: false }),
6 | transformIgnorePatterns: ['dist', 'node_modules'],
7 | testPathIgnorePatterns: ['babel-plugin-emotion']
8 | })
9 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/test/macro/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env",
5 | {
6 | "loose": true,
7 | "exclude": ["transform-es2015-typeof-symbol"]
8 | }
9 | ],
10 | "stage-0",
11 | "react",
12 | "flow"
13 | ],
14 | "plugins": ["codegen", "./babel-macros-register"]
15 | }
16 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/test/macro/__snapshots__/inject-global.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`injectGlobal 1`] = `
4 | "html {
5 | background: pink;
6 | }
7 |
8 | html.active {
9 | background: red;
10 | }
11 |
12 | body {
13 | color: yellow;
14 | margin: 0;
15 | padding: 0;
16 | }"
17 | `;
18 |
--------------------------------------------------------------------------------
/packages/emotion/test/extract/extract.test.emotion.css:
--------------------------------------------------------------------------------
1 | .css-dae0kw{font-size:12px;}
2 | .css-s9f816{font-size:20px;}.css-s9f816 span{color:blue;}.css-s9f816 span:hover{color:green;}.css-s9f816 span:hover:after{content:'after';}
3 | .css-mdajr1{font-size:20px;}
4 | html{background:pink;}
5 | .css-14yvnlz{font-family:sans-serif;color:yellow;background-color:purple;}
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | comment: # https://docs.codecov.io/docs/pull-request-comments
2 | layout: "files"
3 | behavior: once
4 | require_changes: true # if true: only post the comment if coverage changes
5 | require_base: no # [yes :: must have a base report to post]
6 | require_head: yes # [yes :: must have a head report to post]
7 | branches: null
8 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/Wrapper/emotion.js:
--------------------------------------------------------------------------------
1 | import styled from 'react-emotion/macro'
2 |
3 | const Wrapper = styled('div')`
4 | position: absolute;
5 | transform-origin: 0 0;
6 | left: 50%;
7 | top: 50%;
8 | width: 10px;
9 | height: 10px;
10 | background: #eee;
11 | transform: scale(0.33);
12 | `
13 |
14 | export default Wrapper
15 |
--------------------------------------------------------------------------------
/packages/emotion/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import createEmotion from 'create-emotion'
3 |
4 | const context = typeof global !== 'undefined' ? global : {}
5 |
6 | export const {
7 | flush,
8 | hydrate,
9 | cx,
10 | merge,
11 | getRegisteredStyles,
12 | injectGlobal,
13 | keyframes,
14 | css,
15 | sheet,
16 | caches
17 | } = createEmotion(context)
18 |
--------------------------------------------------------------------------------
/test/styleTransform.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | const path = require('path')
3 |
4 | module.exports = {
5 | process(src: string, filename: string) {
6 | return `
7 | if (!global.stylesMocked) global.mockedCssImports = {}
8 | global.mockedCssImports[${JSON.stringify(
9 | path.basename(filename)
10 | )}] = ${JSON.stringify(src)}
11 | `
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/View/styles.css:
--------------------------------------------------------------------------------
1 | .initial {
2 | align-items: stretch;
3 | border-width: 0;
4 | border-style: solid;
5 | box-sizing: border-box;
6 | display: flex;
7 | flex-basis: auto;
8 | flex-direction: column;
9 | flex-shrink: 0;
10 | margin: 0;
11 | padding: 0;
12 | position: relative;
13 | min-height: 0;
14 | min-width: 0;
15 | }
16 |
--------------------------------------------------------------------------------
/packages/site/src/blocks/styled.example:
--------------------------------------------------------------------------------
1 | const Image = ({ className, src }) =>
2 |
3 | const Avatar = styled(Image)`
4 | width: 96px;
5 | height: 96px;
6 | border-radius: 50%;
7 | transition: transform 400ms ease-in-out;
8 |
9 | &:hover {
10 | transform: scale(1.2);
11 | }
12 | `
13 |
14 | render( )
15 |
--------------------------------------------------------------------------------
/packages/emotion/test/__snapshots__/label-pattern.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`label pattern input + label styled 1`] = `
4 | .emotion-0 + label::after {
5 | color: pink;
6 | background: orange;
7 | }
8 |
9 |
10 |
13 |
14 | Label
15 |
16 |
17 | `;
18 |
--------------------------------------------------------------------------------
/packages/site/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env",
5 | {
6 | "modules": false,
7 | "loose": true,
8 | "targets": {
9 | "uglify": true
10 | }
11 | }
12 | ],
13 | "stage-0",
14 | "react"
15 | ],
16 | "plugins": [
17 | ["emotion", { "sourceMap": true }],
18 | "transform-class-properties"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/create-emotion/test/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "flow",
4 | [
5 | "env",
6 | {
7 | "loose": true,
8 | "exclude": ["transform-es2015-typeof-symbol"]
9 | }
10 | ],
11 | "react",
12 | "stage-2"
13 | ],
14 | "plugins": [["../../../babel-plugin-emotion-test", {"paths": ["./packages/create-emotion/test/emotion-instance"]}], "codegen"]
15 | }
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | const { jest: lernaAliases } = require('lerna-alias')
2 |
3 | module.exports = {
4 | transform: {
5 | '\\.css$': '/test/styleTransform.js',
6 | '^.+\\.js?$': 'babel-jest'
7 | },
8 | moduleNameMapper: lernaAliases(),
9 | setupTestFrameworkScriptFile: '/test/testSetup.js',
10 | coveragePathIgnorePatterns: ['/packages/emotion-utils/src/stylis.js']
11 | }
12 |
--------------------------------------------------------------------------------
/test/pretty-css.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { parse, stringify } from 'css'
3 | import typeof { sheet as StyleSheet } from 'emotion'
4 |
5 | export default {
6 | test: (val: any) => val.tags !== undefined && Array.isArray(val.tags),
7 | print(val: StyleSheet, printer: Function) {
8 | return printer(
9 | stringify(parse(val.tags.map(tag => tag.textContent || '').join('')))
10 | )
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/Wrapper/glamor.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { css } from 'glamor'
3 |
4 | const styles = {
5 | position: 'absolute',
6 | transformOrigin: '0 0',
7 | left: '50%',
8 | top: '50%',
9 | width: '10px',
10 | height: '10px',
11 | background: '#eee',
12 | transform: 'scale(0.5)'
13 | }
14 |
15 | export default props => {props.children}
16 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/test/macro/babel-macros-register.js:
--------------------------------------------------------------------------------
1 | require('babel-register')
2 | const path = require('path')
3 | require('module-alias').addAliases({
4 | 'emotion-utils': path.join(__dirname, '../../../emotion-utils/src'),
5 | 'react-emotion/macro': path.join(__dirname, '../../src/macro-styled'),
6 | 'emotion/macro': path.join(__dirname, '../../src/macro')
7 | })
8 |
9 | module.exports = require('babel-macros')
10 |
--------------------------------------------------------------------------------
/test/testSetup.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /* eslint-env jest */
3 | import { createSerializer } from 'jest-emotion'
4 | import * as emotion from 'emotion'
5 | import Enzyme from 'enzyme'
6 | import Adapter from 'enzyme-adapter-react-16'
7 | import prettyCSS from './pretty-css'
8 |
9 | expect.addSnapshotSerializer(createSerializer(emotion))
10 |
11 | expect.addSnapshotSerializer(prettyCSS)
12 |
13 | Enzyme.configure({ adapter: new Adapter() })
14 |
--------------------------------------------------------------------------------
/packages/site/src/blocks/theming.example:
--------------------------------------------------------------------------------
1 | const theme = {
2 | borderRadius: '50%',
3 | borderColor: '#BF67AD'
4 | }
5 |
6 | const Avatar = styled('img')`
7 | width: 96px;
8 | height: 96px;
9 | border-radius: ${props => props.theme.borderRadius};
10 | border: 1px solid ${props => props.theme.borderColor};
11 | `
12 |
13 | render(
14 |
15 |
16 |
17 | )
18 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/View/css-modules.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import classnames from 'classnames'
3 | import React from 'react'
4 | import styles from './styles.css'
5 |
6 | class View extends React.Component {
7 | render() {
8 | const props = this.props
9 | return (
10 |
11 | )
12 | }
13 | }
14 |
15 | export default View
16 |
--------------------------------------------------------------------------------
/docs/with-props.md:
--------------------------------------------------------------------------------
1 | ## Usage with recompose's withProps
2 |
3 | You can pass additional props to your components using recompose's `withProps` higher-order component.
4 |
5 | **[`withProps` documentation](https://github.com/acdlite/recompose/blob/master/docs/API.md#withprops)**
6 |
7 | ```js
8 | import withProps from 'recompose/withProps'
9 |
10 | const RedPasswordInput = withProps({ type: 'password' })(styled('input')`
11 | background-color: red;
12 | `);
13 | ```
14 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/View/emotion.js:
--------------------------------------------------------------------------------
1 | import styled from 'react-emotion/macro'
2 |
3 | const View = styled('div')`
4 | align-items: stretch;
5 | border-width: 0;
6 | border-style: solid;
7 | box-sizing: border-box;
8 | display: flex;
9 | flex-basis: auto;
10 | flex-direction: column;
11 | flex-shrink: 0;
12 | margin: 0;
13 | padding: 0;
14 | position: relative;
15 | min-height: 0;
16 | min-width: 0;
17 | `
18 |
19 | export default View
20 |
--------------------------------------------------------------------------------
/packages/emotion/test/__snapshots__/sheet.dom.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`sheet .speedy throws when a rule has already been inserted 1`] = `"cannot change speedy now"`;
4 |
5 | exports[`sheet inject method throws if the sheet is already injected 1`] = `"already injected!"`;
6 |
7 | exports[`sheet tags 1`] = `
8 | Array [
9 | ,
15 | ]
16 | `;
17 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "lerna": "2.2.0",
3 | "changelog": {
4 | "repo": "emotion-js/emotion",
5 | "labels": {
6 | "Tag: Breaking Change": ":boom: Breaking Change",
7 | "Tag: Enhancement": ":rocket: Enhancement",
8 | "Tag: Bug Fix": ":bug: Bug Fix",
9 | "Tag: Documentation": ":memo: Documentation",
10 | "Tag: Internal": ":house: Internal"
11 | }
12 | },
13 | "version": "9.0.0-1",
14 | "npmClient": "yarn",
15 | "useWorkspaces": true
16 | }
17 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/View/styled-components.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const View = styled.div`
4 | align-items: stretch;
5 | border-width: 0;
6 | border-style: solid;
7 | box-sizing: border-box;
8 | display: flex;
9 | flex-basis: auto;
10 | flex-direction: column;
11 | flex-shrink: 0;
12 | margin: 0;
13 | padding: 0;
14 | position: relative;
15 | min-height: 0;
16 | min-width: 0;
17 | `
18 |
19 | export default View
20 |
--------------------------------------------------------------------------------
/packages/emotion/types/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "es2015",
5 | "strict": true,
6 | "allowSyntheticDefaultImports": true,
7 | "moduleResolution": "node",
8 | "jsx": "react",
9 | "lib": ["es6"],
10 | "noImplicitAny": true,
11 | "noImplicitThis": true,
12 | "strictNullChecks": true,
13 | "strictFunctionTypes": true
14 | },
15 | "include": [
16 | "./*.ts",
17 | "./*.tsx"
18 | ]
19 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - "8"
5 |
6 | script: npm run test
7 |
8 | before_install:
9 | - curl -o- -L https://yarnpkg.com/install.sh | bash
10 | - export PATH="$HOME/.yarn/bin:$PATH"
11 |
12 | install: yarn install --frozen-lockfile
13 |
14 | before_script:
15 | - npm run bootstrap
16 |
17 | after_success:
18 | - cat ./coverage/lcov.info | ./node_modules/codecov/bin/codecov
19 |
20 | cache:
21 | yarn: true
22 | directories:
23 | - node_modules
24 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/View/glamorous.js:
--------------------------------------------------------------------------------
1 | import glamorous from 'glamorous'
2 |
3 | const View = glamorous.div({
4 | alignItems: 'stretch',
5 | borderWidth: 0,
6 | borderStyle: 'solid',
7 | boxSizing: 'border-box',
8 | display: 'flex',
9 | flexBasis: 'auto',
10 | flexDirection: 'column',
11 | flexShrink: 0,
12 | margin: 0,
13 | padding: 0,
14 | position: 'relative',
15 | // fix flexbox bugs
16 | minHeight: 0,
17 | minWidth: 0
18 | })
19 |
20 | export default View
21 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/View/emotion-obj.js:
--------------------------------------------------------------------------------
1 | import styled from 'react-emotion/macro'
2 |
3 | const View = styled.div({
4 | alignItems: 'stretch',
5 | borderWidth: 0,
6 | borderStyle: 'solid',
7 | boxSizing: 'border-box',
8 | display: 'flex',
9 | flexBasis: 'auto',
10 | flexDirection: 'column',
11 | flexShrink: 0,
12 | margin: 0,
13 | padding: 0,
14 | position: 'relative',
15 | // fix flexbox bugs
16 | minHeight: 0,
17 | minWidth: 0
18 | })
19 |
20 | export default View
21 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/test/macro/inject-global.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { injectGlobal, sheet } from 'emotion/macro'
3 |
4 | test('injectGlobal', () => {
5 | injectGlobal`
6 | html {
7 | background: pink;
8 | }
9 | html.active {
10 | background: red;
11 | }
12 | `
13 |
14 | const color = 'yellow'
15 | injectGlobal`
16 | body {
17 | color: ${color};
18 | margin: 0;
19 | padding: 0;
20 | }
21 | `
22 | expect(sheet).toMatchSnapshot()
23 | })
24 |
--------------------------------------------------------------------------------
/packages/react-emotion/types/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "es2015",
5 | "declaration": true,
6 | "strict": true,
7 | "allowSyntheticDefaultImports": true,
8 | "moduleResolution": "node",
9 | "jsx": "react",
10 | "lib": ["es6"],
11 | "noImplicitAny": true,
12 | "noImplicitThis": true,
13 | "strictNullChecks": true,
14 | "strictFunctionTypes": true
15 | },
16 | "include": [
17 | "./*.ts",
18 | "./*.tsx"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/emotion-theming/types/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "es2015",
5 | "declaration": true,
6 | "strict": true,
7 | "allowSyntheticDefaultImports": true,
8 | "moduleResolution": "node",
9 | "jsx": "react",
10 | "lib": ["es6"],
11 | "noImplicitAny": true,
12 | "noImplicitThis": true,
13 | "strictNullChecks": true,
14 | "strictFunctionTypes": true
15 | },
16 | "include": [
17 | "./*.ts",
18 | "./*.tsx"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/Dot/emotion.js:
--------------------------------------------------------------------------------
1 | import styled from 'react-emotion/macro'
2 |
3 | const Dot = styled('div')`
4 | position: absolute;
5 | text-align: center;
6 | cursor: pointer;
7 | width: 0;
8 | height: 0;
9 | left: ${p => p.x + 'px'};
10 | top: ${p => p.y + 'px'};
11 | border-style: solid;
12 | border-width: ${({ size: s }) => `0 ${s / 2}px ${s / 2}px ${s / 2}px`};
13 | border-color: ${({ color }) =>
14 | `transparent transparent ${color} transparent`};
15 | `
16 |
17 | export default Dot
18 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/Box/styles.css:
--------------------------------------------------------------------------------
1 | .outer {
2 | padding: 4px;
3 | }
4 |
5 | .row {
6 | flex-direction: row;
7 | }
8 |
9 | .color0 {
10 | background-color: #222;
11 | }
12 |
13 | .color1 {
14 | background-color: #666;
15 | }
16 |
17 | .color2 {
18 | background-color: #999;
19 | }
20 |
21 | .color3 {
22 | background-color: blue;
23 | }
24 |
25 | .color4 {
26 | background-color: orange;
27 | }
28 |
29 | .color5 {
30 | background-color: red;
31 | }
32 |
33 | .fixed {
34 | width: 20px;
35 | height: 20px;
36 | }
37 |
--------------------------------------------------------------------------------
/packages/benchmarks/README.md:
--------------------------------------------------------------------------------
1 | # Benchmarks
2 |
3 | These benchmarks test how fast various css-in-js libraries render with react components. They do not necessarily represent how fast a css-in-js library will be in the real world and are used by emotion to stop performance regressions in render performance/caching.
4 |
5 | To run these benchmarks run `npm run benchmark` in the root directory. These benchmarks should generally be run on Travis and not locally.
6 |
7 | These benchmarks are a modified version of [react-native-web's benchmarks](https://github.com/necolas/react-native-web/tree/master/benchmarks).
--------------------------------------------------------------------------------
/packages/benchmarks/tests/renderDeepTree.js:
--------------------------------------------------------------------------------
1 | import createRenderBenchmark from '../createRenderBenchmark'
2 | import NestedTree from '../src/components/NestedTree'
3 | import React from 'react'
4 |
5 | const renderDeepTree = (label, components) =>
6 | createRenderBenchmark({
7 | name: `Deep tree [${label}]`,
8 | runs: 20,
9 | getElement() {
10 | return (
11 |
18 | )
19 | }
20 | })
21 |
22 | export default renderDeepTree
23 |
--------------------------------------------------------------------------------
/packages/site/src/blocks/keyframes.example:
--------------------------------------------------------------------------------
1 | const bounce = keyframes`
2 | from, 20%, 53%, 80%, to {
3 | transform: translate3d(0,0,0);
4 | }
5 |
6 | 40%, 43% {
7 | transform: translate3d(0, -30px, 0);
8 | }
9 |
10 | 70% {
11 | transform: translate3d(0, -15px, 0);
12 | }
13 |
14 | 90% {
15 | transform: translate3d(0,-4px,0);
16 | }
17 | `
18 |
19 | const Avatar = styled('img')`
20 | width: 96px;
21 | height: 96px;
22 | border-radius: 50%;
23 | animation: ${bounce} 1s ease infinite;
24 | transform-origin: center bottom;
25 | `
26 |
27 | render( )
28 |
--------------------------------------------------------------------------------
/packages/site/src/index.tpl.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | emotion - The Next Generation of CSS-in-JS
8 |
9 |
10 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/packages/benchmarks/tests/renderWideTree.js:
--------------------------------------------------------------------------------
1 | import createRenderBenchmark from '../createRenderBenchmark'
2 | import NestedTree from '../src/components/NestedTree'
3 | import React from 'react'
4 |
5 | const renderWideTree = (label, components) =>
6 | createRenderBenchmark({
7 | name: `Wide tree [${label}]`,
8 | runs: 20,
9 | getElement() {
10 | return (
11 |
18 | )
19 | }
20 | })
21 |
22 | export default renderWideTree
23 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/Box/css-modules.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import classnames from 'classnames'
3 | import React from 'react'
4 | import View from '../View/css-modules'
5 | import styles from './styles.css'
6 |
7 | const Box = ({
8 | color,
9 | fixed = false,
10 | layout = 'column',
11 | outer = false,
12 | ...other
13 | }) => (
14 |
22 | )
23 |
24 | export default Box
25 |
--------------------------------------------------------------------------------
/packages/site/src/blocks/css.example:
--------------------------------------------------------------------------------
1 | const flexCenter = css`
2 | display: flex;
3 | align-items: center;
4 | justify-content: center;
5 | `
6 |
7 | render(
8 |
28 |
29 |
30 | )
31 |
--------------------------------------------------------------------------------
/docs/webpack.md:
--------------------------------------------------------------------------------
1 | ## Usage with Webpack
2 |
3 | #### Bundling [extracted CSS](https://github.com/emotion-js/emotion/blob/master/docs/extract-static.md)
4 |
5 | ```javascript
6 | {
7 | test: /emotion\.css$/,
8 | // extract a css bundle file for production
9 | use: PROD
10 | ? ExtractTextPlugin.extract({
11 | fallback: 'style-loader',
12 | use: {
13 | loader: 'css-loader',
14 | options: {
15 | sourceMap: true,
16 | modules: true
17 | }
18 | }
19 | })
20 | // extract css into a style tag
21 | : ['style-loader', 'css-loader']
22 | },
23 | ```
24 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/Dot/glamor.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { css } from 'glamor'
3 |
4 | export default ({ size, x, y, children, color }) => (
5 |
21 | {children}
22 |
23 | )
24 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env",
5 | {
6 | "loose": true,
7 | "exclude": ["transform-es2015-typeof-symbol"]
8 | }
9 | ],
10 | "stage-0",
11 | "react",
12 | "flow"
13 | ],
14 | "plugins": ["codegen"],
15 | "env": {
16 | "test": {
17 | "presets": [
18 | "flow",
19 | [
20 | "env",
21 | {
22 | "loose": true,
23 | "exclude": ["transform-es2015-typeof-symbol"]
24 | }
25 | ],
26 | "react",
27 | "stage-2"
28 | ],
29 | "plugins": ["./babel-plugin-emotion-test", "codegen"]
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/packages/benchmarks/createRenderBenchmark.js:
--------------------------------------------------------------------------------
1 | import benchmark from './benchmark'
2 | import ReactDOM from 'react-dom'
3 |
4 | const node = document.querySelector('.root')
5 |
6 | const createRenderBenchmark = ({
7 | description,
8 | getElement,
9 | name,
10 | runs,
11 | flush
12 | }) => () => {
13 | const setup = () => {}
14 | const teardown = () => {
15 | ReactDOM.unmountComponentAtNode(node)
16 | }
17 |
18 | return benchmark({
19 | name,
20 | description,
21 | runs,
22 | setup,
23 | teardown,
24 | task: () => {
25 | ReactDOM.render(getElement(), node)
26 | }
27 | })
28 | }
29 |
30 | export default createRenderBenchmark
31 |
--------------------------------------------------------------------------------
/packages/create-emotion-server/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { Emotion } from 'create-emotion'
3 | import createExtractCritical from './extract-critical'
4 | import createRenderStylesToString from './inline'
5 | import createRenderStylesToStream from './stream'
6 |
7 | module.exports = function(emotion: Emotion) {
8 | const nonceString =
9 | emotion.caches.nonce !== undefined ? ` nonce="${emotion.caches.nonce}"` : ''
10 | return {
11 | extractCritical: createExtractCritical(emotion),
12 | renderStylesToString: createRenderStylesToString(emotion, nonceString),
13 | renderStylesToNodeStream: createRenderStylesToStream(emotion, nonceString)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/emotion/test/label-pattern.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'react-emotion'
3 | import renderer from 'react-test-renderer'
4 | import { flush } from 'emotion'
5 |
6 | describe('label pattern', () => {
7 | afterEach(() => flush())
8 |
9 | test('input + label styled', () => {
10 | const Input = styled.input`
11 | & + label::after {
12 | color: pink;
13 | background: orange;
14 | }
15 | `
16 |
17 | const tree = renderer
18 | .create(
19 |
20 |
21 | Label
22 |
23 | )
24 | .toJSON()
25 |
26 | expect(tree).toMatchSnapshot()
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/docs/inject-global.md:
--------------------------------------------------------------------------------
1 | ## Inject Global
2 |
3 | Sometimes it's useful to insert global css like resets or font faces. `injectGlobal` can be used for this.
4 |
5 | ```jsx
6 | import { injectGlobal } from 'emotion'
7 |
8 | injectGlobal`
9 | * {
10 | box-sizing: border-box;
11 | }
12 | @font-face {
13 | font-family: 'Patrick Hand SC';
14 | font-style: normal;
15 | font-weight: 400;
16 | src: local('Patrick Hand SC'), local('PatrickHandSC-Regular'), url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2) format('woff2');
17 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
18 | }
19 | `
20 | ```
21 |
--------------------------------------------------------------------------------
/packages/create-emotion/test/instance.test.js:
--------------------------------------------------------------------------------
1 | import createEmotion from 'create-emotion'
2 | import { container, css, sheet } from './emotion-instance'
3 |
4 | describe('general instance tests', () => {
5 | test('inserts style tags into container', () => {
6 | css`
7 | display: flex;
8 | `
9 | sheet.tags.forEach(tag => {
10 | expect(tag.getAttribute('data-emotion')).toBe('some-key')
11 | expect(tag.getAttribute('nonce')).toBe('some-nonce')
12 | expect(tag.parentNode).toBe(container)
13 | })
14 | })
15 | test('throws with invalid key', () => {
16 | expect(() => {
17 | createEmotion({}, { key: 'css1' })
18 | }).toThrowErrorMatchingSnapshot()
19 | })
20 | })
21 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Prerequisites
2 |
3 | - [Node.js](http://nodejs.org/) >= v7 must be installed.
4 |
5 | - [Yarn](https://yarnpkg.com/en/docs/install)
6 |
7 | ## Installation
8 |
9 | - Running `yarn` in the module's root directory will install everything you need for development.
10 | - Run `lerna bootstrap` in the module's root directory
11 |
12 | ## Running Tests
13 |
14 | - `yarn test` will run the tests once.
15 |
16 | - `yarn test:coverage` will run the tests and produce a coverage report in `coverage/`.
17 |
18 | - `yarn test:watch` will run the tests on every change.
19 |
20 | ## Building
21 |
22 | - `yarn build` will build the module for publishing to npm.
23 |
24 | - `yarn clean` will delete built resources.
25 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/View/emotion-css.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import { css } from 'emotion'
3 | import React from 'react'
4 |
5 | class View extends React.Component {
6 | render() {
7 | const { style, ...other } = this.props
8 | return
9 | }
10 | }
11 |
12 | const viewStyle = {
13 | alignItems: 'stretch',
14 | borderWidth: 0,
15 | borderStyle: 'solid',
16 | boxSizing: 'border-box',
17 | display: 'flex',
18 | flexBasis: 'auto',
19 | flexDirection: 'column',
20 | flexShrink: 0,
21 | margin: 0,
22 | padding: 0,
23 | position: 'relative',
24 | // fix flexbox bugs
25 | minHeight: 0,
26 | minWidth: 0
27 | }
28 |
29 | export default View
30 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/View/glamor.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import { css } from 'glamor'
3 | import React from 'react'
4 |
5 | class View extends React.Component {
6 | render() {
7 | const { style, ...other } = this.props
8 | return
9 | }
10 | }
11 |
12 | const viewStyle = {
13 | alignItems: 'stretch',
14 | borderWidth: 0,
15 | borderStyle: 'solid',
16 | boxSizing: 'border-box',
17 | display: 'flex',
18 | flexBasis: 'auto',
19 | flexDirection: 'column',
20 | flexShrink: 0,
21 | margin: 0,
22 | padding: 0,
23 | position: 'relative',
24 | // fix flexbox bugs
25 | minHeight: 0,
26 | minWidth: 0
27 | }
28 |
29 | export default View
30 |
--------------------------------------------------------------------------------
/packages/site/src/blocks/object-styles.example:
--------------------------------------------------------------------------------
1 | const imageStyles = css({
2 | width: 96,
3 | height: 96
4 | })
5 |
6 | const fakeBlue = css([
7 | {
8 | color: 'blue'
9 | }
10 | ])
11 |
12 | const red = css([
13 | {
14 | color: 'red'
15 | }
16 | ])
17 |
18 | const blue = css([
19 | red,
20 | {
21 | color: 'blue'
22 | }
23 | ])
24 |
25 | const prettyStyles = css([
26 | {
27 | borderRadius: '50%',
28 | transition: 'transform 400ms ease-in-out',
29 | ':hover': {
30 | transform: 'scale(1.2)'
31 | }
32 | },
33 | { border: '3px solid currentColor' }
34 | ])
35 |
36 | const Avatar = styled('img')`
37 | ${prettyStyles};
38 | ${imageStyles};
39 | ${blue};
40 | `
41 |
42 | render( )
43 |
--------------------------------------------------------------------------------
/packages/create-emotion-server/README.md:
--------------------------------------------------------------------------------
1 | # create-emotion-server
2 |
3 | > Create Server-Side-Rendering APIs for emotion instances
4 |
5 | `create-emotion-styled` allows you create various APIs for Server-Side Rendering with instances of emotion. This is **only** needed if you use a custom instance of emotion from `create-emotion` and you want to do Server-Side Rendering.
6 |
7 | ```jsx
8 | import createEmotionServer from 'create-emotion-server'
9 | import * as emotion from 'my-emotion-instance'
10 |
11 | export const {
12 | extractCritical,
13 | renderStylesToString,
14 | renderStylesToNodeStream
15 | } = createEmotionServer(emotion)
16 | ```
17 |
18 | [All of emotion's SSR APIs are documented in their own doc.](https://github.com/emotion-js/emotion/blob/master/docs/ssr.md)
--------------------------------------------------------------------------------
/packages/create-emotion-styled/README.md:
--------------------------------------------------------------------------------
1 | # create-emotion-styled
2 |
3 | > Create the styled API with emotion for React-like libraries
4 |
5 | `create-emotion-styled` allows you to use the `styled` API with instances of emotion. This is **only** needed if you use a custom instance of emotion from `create-emotion` and you want to use the `styled` API. `create-emotion-styled` accepts an instance of emotion from `create-emotion` and a React-like view library.
6 |
7 |
8 | ```jsx
9 | import React from 'react'
10 | import * as emotion from 'my-emotion-instance'
11 | import createEmotionStyled from 'create-emotion-styled'
12 |
13 | export default createEmotionStyled(emotion, React)
14 |
15 | // Exporting emotion isn't required but generally recommended
16 | export * from 'my-emotion-instance'
17 | ```
18 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/Box/glamorous.js:
--------------------------------------------------------------------------------
1 | import glamorous from 'glamorous'
2 | import View from '../View/glamorous'
3 |
4 | const getColor = color => {
5 | switch (color) {
6 | case 0:
7 | return '#222'
8 | case 1:
9 | return '#666'
10 | case 2:
11 | return '#999'
12 | case 3:
13 | return 'blue'
14 | case 4:
15 | return 'orange'
16 | case 5:
17 | return 'red'
18 | default:
19 | return 'transparent'
20 | }
21 | }
22 |
23 | const Box = glamorous(View)(props => ({
24 | flexDirection: props.layout === 'column' ? 'column' : 'row',
25 | padding: props.outer ? 4 : 0,
26 | height: props.fixed ? 20 : 'auto',
27 | width: props.fixed ? 20 : 'auto',
28 | backgroundColor: getColor(props.color)
29 | }))
30 |
31 | export default Box
32 |
--------------------------------------------------------------------------------
/flow-typed/npm/jest-glamor-react_vx.x.x.js:
--------------------------------------------------------------------------------
1 | // flow-typed signature: 22afe792d07ce86b99954c5d2a83c46e
2 | // flow-typed version: <>/jest-glamor-react_v^3.1.1/flow_v0.59.0
3 |
4 | /**
5 | * This is an autogenerated libdef stub for:
6 | *
7 | * 'jest-glamor-react'
8 | *
9 | * Fill this stub out by replacing all the `any` types.
10 | *
11 | * Once filled out, we encourage you to share your work with the
12 | * community by sending a pull request to:
13 | * https://github.com/flowtype/flow-typed
14 | */
15 |
16 | import type {JestSnapshotSerializer} from 'jest'
17 |
18 |
19 | interface GlamorStyleSheet {
20 | tags: Array;
21 | }
22 | declare module 'jest-glamor-react' {
23 | declare module.exports: JestSnapshotSerializer & (sheet: GlamorStyleSheet) => JestSnapshotSerializer
24 | }
25 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/Box/emotion-obj.js:
--------------------------------------------------------------------------------
1 | import styled from 'react-emotion/macro'
2 | import View from '../View/emotion-obj'
3 |
4 | const getColor = color => {
5 | switch (color) {
6 | case 0:
7 | return '#222'
8 | case 1:
9 | return '#666'
10 | case 2:
11 | return '#999'
12 | case 3:
13 | return 'blue'
14 | case 4:
15 | return 'orange'
16 | case 5:
17 | return 'red'
18 | default:
19 | return 'transparent'
20 | }
21 | }
22 |
23 | const Box = styled(View)(props => ({
24 | flexDirection: props.layout === 'column' ? 'column' : 'row',
25 | padding: props.outer ? 4 : 0,
26 | height: props.fixed ? 20 : 'auto',
27 | width: props.fixed ? 20 : 'auto',
28 | backgroundColor: getColor(props.color)
29 | }))
30 |
31 | export default Box
32 |
--------------------------------------------------------------------------------
/packages/emotion/test/__snapshots__/component-selector.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`component selector should be converted to use the emotion target className 1`] = `
4 | .emotion-2 .emotion-1 {
5 | color: red;
6 | }
7 |
8 | .emotion-0 {
9 | color: blue;
10 | }
11 |
12 |
19 | `;
20 |
21 | exports[`component selector should be converted to use the emotion target className 2`] = `
22 | ".css-jww4zs .e42j9n60 {
23 | color: red;
24 | }
25 |
26 | .css-14ksm7b {
27 | color: blue;
28 | }"
29 | `;
30 |
31 | exports[`component selector should throw if the missing the static targeting property 1`] = `"Component selectors can only be used in conjunction with babel-plugin-emotion."`;
32 |
--------------------------------------------------------------------------------
/docs/instances.md:
--------------------------------------------------------------------------------
1 | # Instances
2 |
3 | emotion allows creating custom instances of emotion to provide special options. Instances are created with the [`create-emotion`](https://github.com/emotion-js/emotion/tree/master/packages/create-emotion), [`create-emotion-styled`](https://github.com/emotion-js/emotion/tree/master/packages/create-emotion-styled) and [`create-emotion-server`](https://github.com/emotion-js/emotion/tree/master/packages/create-emotion-server) packages which create instances of `emotion`, `react-emotion`/`preact-emotion` and `emotion-server` respectively. They are documented in their own respective READMEs linked above.
4 |
5 | The instances' and primary instance's paths should be added as options to `babel-plugin-emotion` [as shown in `babel-plugin-emotion`'s README](https://github.com/emotion-js/emotion/tree/master/packages/babel-plugin-emotion#instances).
--------------------------------------------------------------------------------
/docs/theming.md:
--------------------------------------------------------------------------------
1 | ## Theming
2 |
3 | Themes are provided by the library [`emotion-theming`](https://github.com/emotion-js/emotion/tree/master/packages/emotion-theming).
4 |
5 |
6 | ```bash
7 | npm install -S emotion-theming
8 | ```
9 |
10 | Add `ThemeProvider` to the top level of your app and access the theme with `props.theme` in a styled component. The api is laid out in detail [in the documentation](https://github.com/emotion-js/emotion/tree/master/packages/emotion-theming/README.md#api).
11 |
12 | ```jsx
13 | import styled from 'react-emotion'
14 | import { ThemeProvider } from 'emotion-theming'
15 |
16 | const H1 = styled(Heading)`
17 | color: ${p => p.theme.purple};
18 | `
19 |
20 |
21 | const App = () => (
22 |
23 |
24 | emotion
25 |
26 |
27 | )
28 | ```
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/Box/emotion.js:
--------------------------------------------------------------------------------
1 | import styled from 'react-emotion/macro'
2 | import View from '../View/emotion'
3 |
4 | const getColor = color => {
5 | switch (color) {
6 | case 0:
7 | return '#222'
8 | case 1:
9 | return '#666'
10 | case 2:
11 | return '#999'
12 | case 3:
13 | return 'blue'
14 | case 4:
15 | return 'orange'
16 | case 5:
17 | return 'red'
18 | default:
19 | return 'transparent'
20 | }
21 | }
22 |
23 | const Box = styled(View)`
24 | flex-direction: ${props => (props.layout === 'column' ? 'column' : 'row')};
25 | padding: ${props => (props.outer ? '4px' : '0')};
26 | height: ${props => (props.fixed ? '20px' : 'auto')};
27 | width: ${props => (props.fixed ? '20px' : 'auto')};
28 | background-color: ${props => getColor(props.color)};
29 | `
30 |
31 | export default Box
32 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/Box/styled-components.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import View from '../View/styled-components'
3 |
4 | const getColor = color => {
5 | switch (color) {
6 | case 0:
7 | return '#222'
8 | case 1:
9 | return '#666'
10 | case 2:
11 | return '#999'
12 | case 3:
13 | return 'blue'
14 | case 4:
15 | return 'orange'
16 | case 5:
17 | return 'red'
18 | default:
19 | return 'transparent'
20 | }
21 | }
22 |
23 | const Box = styled(View)`
24 | flex-direction: ${props => (props.layout === 'column' ? 'column' : 'row')};
25 | padding: ${props => (props.outer ? '4px' : '0')};
26 | height: ${props => (props.fixed ? '20px' : 'auto')};
27 | width: ${props => (props.fixed ? '20px' : 'auto')};
28 | background-color: ${props => getColor(props.color)};
29 | `
30 |
31 | export default Box
32 |
--------------------------------------------------------------------------------
/packages/emotion/test/extract/__snapshots__/extract.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`styled basic render nested 1`] = `
4 |
7 | hello world
8 |
9 | `;
10 |
11 | exports[`styled className prop on styled 1`] = `
12 |
15 | hello world
16 |
17 | `;
18 |
19 | exports[`styled no dynamic 1`] = `
20 |
23 | hello world
24 |
25 | `;
26 |
27 | exports[`styled writes the correct css 1`] = `
28 | ".css-dae0kw{font-size:12px;}
29 | .css-s9f816{font-size:20px;}.css-s9f816 span{color:blue;}.css-s9f816 span:hover{color:green;}.css-s9f816 span:hover:after{content:'after';}
30 | .css-mdajr1{font-size:20px;}
31 | html{background:pink;}
32 | .css-14yvnlz{font-family:sans-serif;color:yellow;background-color:purple;}"
33 | `;
34 |
--------------------------------------------------------------------------------
/packages/emotion-theming/types/index.d.ts:
--------------------------------------------------------------------------------
1 | // TypeScript Version: 2.3
2 | import { ComponentClass, SFC } from "react";
3 |
4 | export type OptionalThemeProps = Props & { theme?: Theme };
5 |
6 | export interface ThemeProviderProps {
7 | theme: Partial | ((theme: Theme) => Theme);
8 | }
9 |
10 | export type ThemeProviderComponent = ComponentClass>;
11 | export const ThemeProvider: ThemeProviderComponent;
12 |
13 | /**
14 | * Inject theme into component
15 | */
16 | // tslint:disable-next-line:no-unnecessary-generics
17 | export function withTheme(component: ComponentClass | SFC): ComponentClass>;
18 |
19 | export interface EmotionThemingModule {
20 | ThemeProvider: ThemeProviderComponent;
21 | withTheme(component: ComponentClass | SFC): ComponentClass>;
22 | }
23 |
--------------------------------------------------------------------------------
/packages/create-emotion-server/src/extract-critical.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { Emotion } from 'create-emotion'
3 |
4 | const createExtractCritical = (emotion: Emotion) => (html: string) => {
5 | // parse out ids from html
6 | // reconstruct css/rules/cache to pass
7 | let RGX = new RegExp(`${emotion.caches.key}-([a-zA-Z0-9-]+)`, 'gm')
8 |
9 | let o = { html, ids: [], css: '' }
10 | let match
11 | let ids = {}
12 | while ((match = RGX.exec(html)) !== null) {
13 | if (ids[match[1]] === undefined) {
14 | ids[match[1]] = true
15 | }
16 | }
17 |
18 | o.ids = Object.keys(emotion.caches.inserted).filter(id => {
19 | if (
20 | (ids[id] === true ||
21 | emotion.caches.registered[`${emotion.caches.key}-${id}`] ===
22 | undefined) &&
23 | emotion.caches.inserted[id] !== true
24 | ) {
25 | o.css += emotion.caches.inserted[id]
26 | return true
27 | }
28 | })
29 |
30 | return o
31 | }
32 |
33 | export default createExtractCritical
34 |
--------------------------------------------------------------------------------
/packages/emotion-theming/src/create-broadcast.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | // https://github.com/styled-components/styled-components/blob/e05b3fe247e9d956bcde786cec376e32afb85bca/src/utils/create-broadcast.js
3 | const createBroadcast = (initialState: any) => {
4 | const listeners: { [number]: Function | void } = {}
5 | let id = 0
6 | let state = initialState
7 |
8 | function publish(nextState: any) {
9 | state = nextState
10 |
11 | for (const key in listeners) {
12 | // $FlowFixMe
13 | const listener = listeners[key]
14 | if (listener === undefined) {
15 | continue
16 | }
17 |
18 | listener(state)
19 | }
20 | }
21 |
22 | function subscribe(listener: any) {
23 | const currentId = id
24 | listeners[currentId] = listener
25 | id += 1
26 | listener(state)
27 | return currentId
28 | }
29 |
30 | function unsubscribe(unsubID: number) {
31 | listeners[unsubID] = undefined
32 | }
33 |
34 | return { publish, subscribe, unsubscribe }
35 | }
36 |
37 | export default createBroadcast
38 |
--------------------------------------------------------------------------------
/packages/jest-emotion/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jest-emotion",
3 | "version": "9.0.0-1",
4 | "description": "Jest utilities for emotion",
5 | "main": "lib/index.js",
6 | "files": [
7 | "src",
8 | "lib"
9 | ],
10 | "scripts": {
11 | "build": "npm-run-all clean babel",
12 | "babel": "babel src -d lib",
13 | "watch": "babel src -d lib --watch",
14 | "clean": "rimraf lib"
15 | },
16 | "dependencies": {
17 | "css": "^2.2.1"
18 | },
19 | "devDependencies": {
20 | "babel-cli": "^6.24.1",
21 | "npm-run-all": "^4.0.2",
22 | "rimraf": "^2.6.1"
23 | },
24 | "author": "Kye Hohenberger",
25 | "homepage": "https://emotion.sh",
26 | "license": "MIT",
27 | "repository": "https://github.com/emotion-js/emotion/tree/master/packages/jest-emotion-react",
28 | "keywords": [
29 | "styles",
30 | "emotion",
31 | "react",
32 | "css",
33 | "css-in-js",
34 | "jest",
35 | "snapshot"
36 | ],
37 | "bugs": {
38 | "url": "https://github.com/emotion-js/emotion/issues"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/packages/emotion-utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "emotion-utils",
3 | "version": "9.0.0-0",
4 | "description": "Shared utilities used by emotion, The Next Generation of CSS-in-JS.",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "files": [
8 | "src",
9 | "dist"
10 | ],
11 | "scripts": {
12 | "build": "npm-run-all clean rollup",
13 | "clean": "rimraf dist",
14 | "watch": "rollup -c ../../rollup.config.js --watch",
15 | "rollup": "rollup -c ../../rollup.config.js"
16 | },
17 | "author": "Kye Hohenberger",
18 | "homepage": "https://emotion.sh",
19 | "license": "MIT",
20 | "repository": "https://github.com/emotion-js/emotion/tree/master/packages/emotion-utils",
21 | "keywords": [
22 | "styles",
23 | "emotion",
24 | "react",
25 | "css",
26 | "css-in-js"
27 | ],
28 | "bugs": {
29 | "url": "https://github.com/emotion-js/emotion/issues"
30 | },
31 | "devDependencies": {
32 | "npm-run-all": "^4.0.2",
33 | "rimraf": "^2.6.1",
34 | "rollup": "^0.51.3"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/Box/glamor.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React from 'react'
3 | import View from '../View/glamor'
4 |
5 | const Box = ({
6 | color,
7 | fixed = false,
8 | layout = 'column',
9 | outer = false,
10 | ...other
11 | }) => (
12 |
21 | )
22 |
23 | const styles = {
24 | outer: {
25 | padding: 4
26 | },
27 | row: {
28 | flexDirection: 'row'
29 | },
30 | color0: {
31 | backgroundColor: '#222'
32 | },
33 | color1: {
34 | backgroundColor: '#666'
35 | },
36 | color2: {
37 | backgroundColor: '#999'
38 | },
39 | color3: {
40 | backgroundColor: 'blue'
41 | },
42 | color4: {
43 | backgroundColor: 'orange'
44 | },
45 | color5: {
46 | backgroundColor: 'red'
47 | },
48 | fixed: {
49 | width: 20,
50 | height: 20
51 | }
52 | }
53 |
54 | export default Box
55 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/Box/emotion-css.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React from 'react'
3 | import View from '../View/emotion-css'
4 |
5 | const Box = ({
6 | color,
7 | fixed = false,
8 | layout = 'column',
9 | outer = false,
10 | ...other
11 | }) => (
12 |
21 | )
22 |
23 | const styles = {
24 | outer: {
25 | padding: 4
26 | },
27 | row: {
28 | flexDirection: 'row'
29 | },
30 | color0: {
31 | backgroundColor: '#222'
32 | },
33 | color1: {
34 | backgroundColor: '#666'
35 | },
36 | color2: {
37 | backgroundColor: '#999'
38 | },
39 | color3: {
40 | backgroundColor: 'blue'
41 | },
42 | color4: {
43 | backgroundColor: 'orange'
44 | },
45 | color5: {
46 | backgroundColor: 'red'
47 | },
48 | fixed: {
49 | width: 20,
50 | height: 20
51 | }
52 | }
53 |
54 | export default Box
55 |
--------------------------------------------------------------------------------
/packages/jest-emotion/src/replace-class-names.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | function defaultClassNameReplacer(className, index) {
3 | return `emotion-${index}`
4 | }
5 |
6 | export type ClassNameReplacer = (className: string, index: number) => string
7 |
8 | const componentSelectorClassNamePattern = /\.e[a-zA-Z0-9-]+[0-9]+/
9 |
10 | export const replaceClassNames = (
11 | selectors: Array,
12 | styles: string,
13 | code: string,
14 | key: string,
15 | replacer: ClassNameReplacer = defaultClassNameReplacer
16 | ) => {
17 | let index = 0
18 | const classRegex = new RegExp(`^\\.${key}-([a-zA-Z0-9-]+)`)
19 |
20 | return selectors.reduce((acc, className) => {
21 | if (
22 | classRegex.test(className) ||
23 | componentSelectorClassNamePattern.test(className)
24 | ) {
25 | const escapedRegex = new RegExp(
26 | className.replace('.', '').replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'),
27 | 'g'
28 | )
29 | return acc.replace(escapedRegex, replacer(className, index++))
30 | }
31 | return acc
32 | }, `${styles}${styles ? '\n\n' : ''}${code}`)
33 | }
34 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/NestedTree.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | class DeepTree extends Component {
4 | render() {
5 | const { breadth, components, depth, id, wrap } = this.props
6 | const { Box } = components
7 |
8 | let result = (
9 |
15 | {depth === 0 && (
16 |
17 | )}
18 | {depth !== 0 &&
19 | Array.from({ length: breadth }).map((el, i) => (
20 |
28 | ))}
29 |
30 | )
31 | for (let i = 0; i < wrap; i++) {
32 | result = {result}
33 | }
34 | return result
35 | }
36 | }
37 |
38 | export default DeepTree
39 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [version]
2 | 0.61.0
3 |
4 | [ignore]
5 | .*/node_modules/styled-components/.*
6 |
7 |
8 | [include]
9 |
10 | [libs]
11 |
12 | [options]
13 | suppress_comment=.*\\$FlowFixMe
14 | module.name_mapper='^\(emotion-utils\)$' -> '/packages/\1/src'
15 | module.name_mapper='^\(create-emotion\)$' -> '/packages/\1/src'
16 | module.name_mapper='^\(create-emotion-styled\)$' -> '/packages/\1/src'
17 | module.name_mapper='^\(react-emotion\)$' -> '/packages/\1/src'
18 | module.name_mapper='^\(preact-emotion\)$' -> '/packages/\1/src'
19 | module.name_mapper='^\(emotion\)$' -> '/packages/\1/src'
20 | module.name_mapper='^\(babel-plugin-emotion\)$' -> '/packages/\1/src'
21 | module.name_mapper='^\(emotion-theming\)$' -> '/packages/\1/src'
22 | module.name_mapper='^\(emotion-server\)$' -> '/packages/\1/src'
23 | module.name_mapper='^\(create-emotion-server\)$' -> '/packages/\1/src'
24 | module.name_mapper='^\(jest-emotion\)$' -> '/packages/\1/src'
25 | unsafe.enable_getters_and_setters=true
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Kye Hohenberger
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/nested.md:
--------------------------------------------------------------------------------
1 | ## Nested Selectors
2 |
3 | Sometimes you will want to nest selectors to target only elements inside the current class or React component. Here is an example of a simple element selector nested in the class generated with `css`:
4 |
5 | ```jsx
6 | import { css } from 'emotion';
7 |
8 | const paragraph = css`
9 | color: gray;
10 |
11 | a {
12 | border-bottom: 1px solid currentColor;
13 | }
14 | `
15 | ```
16 |
17 | You can use `&` to select the current class nested in another element:
18 |
19 | ```jsx
20 | const paragraph = css`
21 | color: gray;
22 |
23 | header & {
24 | color: black;
25 | }
26 | `
27 | ```
28 |
29 | To nest a class selector using the class generated with `css` you can interpolate it but be aware than emotion merges styles from `css` together when composing so that class name may not always be there:
30 |
31 | ```jsx
32 | const link = css`
33 | color: hotpink;
34 | `
35 |
36 | const paragraph = css`
37 | color: gray;
38 |
39 | .${link} {
40 | border-bottom: 1px solid currentColor;
41 | }
42 | `
43 | ```
44 |
45 | The result of `css` is a class name _without_ the dot (`.`), so we prepended it.
46 |
--------------------------------------------------------------------------------
/packages/benchmarks/send-results.js:
--------------------------------------------------------------------------------
1 | const { createApolloFetch } = require('apollo-fetch')
2 |
3 | const { GRAPH_TOKEN } = process.env
4 |
5 | const client = createApolloFetch({
6 | uri: 'https://api.graph.cool/simple/v1/cj83urcdm0u420180bqw9w4mu'
7 | })
8 |
9 | client.use(({ request, options }, next) => {
10 | if (!options.headers) {
11 | options.headers = {} // Create the headers object if needed.
12 | }
13 | options.headers['Authorization'] = 'Bearer ' + GRAPH_TOKEN || ''
14 |
15 | next()
16 | })
17 |
18 | module.exports = function sendResult(vars) {
19 | return client({
20 | query: `
21 | mutation createRunAndResults($branch: String, $pr: String, $commit: String, $commitMessage: String, $results: [RunresultsResult!]) {
22 | createRun(results: $results, branch: $branch, pr: $pr, commit: $commit, commitMessage: $commitMessage) {
23 | createdAt
24 | branch
25 | pr
26 | commit
27 | commitMessage
28 | results {
29 | name
30 | duration
31 | type
32 | }
33 | }
34 | }
35 | `,
36 | variables: vars
37 | })
38 | }
39 |
--------------------------------------------------------------------------------
/packages/benchmarks/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "benchmarks",
3 | "private": true,
4 | "dependencies": {
5 | "apollo-fetch": "^0.6.0",
6 | "classnames": "^2.2.5",
7 | "d3-scale-chromatic": "^1.1.1",
8 | "emotion": "^9.0.0-1",
9 | "glamor": "^2.20.37",
10 | "glamorous": "^4.1.0",
11 | "marky": "^1.2.0",
12 | "postcss": "^6.0.8",
13 | "react": "^16.0.0",
14 | "react-dom": "^16.0.0",
15 | "react-emotion": "^9.0.0-1",
16 | "styled-components": "^2.2.1"
17 | },
18 | "devDependencies": {
19 | "babel-loader": "^7.1.1",
20 | "babel-macros": "^1.0.2",
21 | "babel-polyfill": "^6.26.0",
22 | "cli-chart": "^0.3.1",
23 | "cli-table": "^0.3.1",
24 | "css-loader": "^0.28.4",
25 | "html-webpack-plugin": "^2.30.1",
26 | "puppeteer": "^0.10.1",
27 | "react-addons-perf": "^15.6.0-rc.1",
28 | "serve": "^6.0.6",
29 | "style-loader": "^0.18.2",
30 | "webpack": "^3.4.1",
31 | "webpack-bundle-analyzer": "^2.9.0"
32 | },
33 | "scripts": {
34 | "build:benchmark": "webpack",
35 | "benchmark": "npm run build:benchmark && node run-headless.js"
36 | },
37 | "version": "9.0.0-1"
38 | }
39 |
--------------------------------------------------------------------------------
/docs/keyframes.md:
--------------------------------------------------------------------------------
1 | ## Keyframes
2 |
3 | If you need more control over an animation, you can use `keyframes` with the same JS interpolation as `css`.
4 | The `keyframes` function takes in the css keyframes definition and returns the animation name so that you can include it in other styles. This is similar to how `css` takes in styles and returns the className that you can use to apply the styles.
5 |
6 | ```jsx
7 | import { keyframes, css } from 'emotion'
8 | import styled from 'react-emotion'
9 |
10 | const bounceHeight = 30;
11 |
12 | // This returns a animation
13 | const bounce = keyframes`
14 | from, 20%, 53%, 80%, to {
15 | transform: translate3d(0,0,0);
16 | }
17 |
18 | 40%, 43% {
19 | transform: translate3d(0, -${bounceHeight}px, 0);
20 | }
21 |
22 | 70% {
23 | transform: translate3d(0, -${bounceHeight / 2}px, 0);
24 | }
25 |
26 | 90% {
27 | transform: translate3d(0, -${bounceHeight / 4}px, 0);
28 | }
29 | `
30 |
31 | // You can use them in styled components or anything else
32 | const AnimatedDiv = styled.div`
33 | animation: ${bounce} 1s ease infinite;
34 | `
35 |
36 | const slowBounce = css`
37 | animation: ${bounce} 5s ease 1;
38 | `
39 | ```
40 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/test/__snapshots__/fs.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`babel plugin fs creates and writes to the css file when it does not exist 1`] = `".css-1yfv4zm{margin:12px 48px;color:#ffffff;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;color:blue;name:class;}"`;
4 |
5 | exports[`babel plugin fs creates and writes to the css file when it does not exist 2`] = `
6 | "require(\\"./fs.test.emotion.css\\");
7 |
8 | \\"css-1yfv4zm\\";"
9 | `;
10 |
11 | exports[`babel plugin fs does not write to the css file when it is the same as is already written 1`] = `
12 | "require(\\"./fs.test.emotion.css\\");
13 |
14 | \\"css-1yfv4zm\\";"
15 | `;
16 |
17 | exports[`babel plugin fs writes to the css file when it does exist 1`] = `".css-1yfv4zm{margin:12px 48px;color:#ffffff;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;color:blue;name:class;}"`;
18 |
19 | exports[`babel plugin fs writes to the css file when it does exist 2`] = `
20 | "require(\\"./fs.test.emotion.css\\");
21 |
22 | \\"css-1yfv4zm\\";"
23 | `;
24 |
--------------------------------------------------------------------------------
/packages/emotion/test/component-selector.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'react-emotion'
3 | import renderer from 'react-test-renderer'
4 | import { css, flush, sheet } from 'emotion'
5 | import { TARGET_KEY } from 'emotion-utils'
6 |
7 | describe('component selector', () => {
8 | afterEach(() => flush())
9 |
10 | test('should be converted to use the emotion target className', () => {
11 | const FakeComponent = styled.div`
12 | color: blue;
13 | `
14 |
15 | const cls2 = css`
16 | ${FakeComponent} {
17 | color: red;
18 | }
19 | `
20 | const tree = renderer
21 | .create(
22 |
23 |
24 |
25 | )
26 | .toJSON()
27 | expect(tree).toMatchSnapshot()
28 | expect(sheet).toMatchSnapshot()
29 | })
30 |
31 | test('should throw if the missing the static targeting property', () => {
32 | const FakeComponent = styled.div`
33 | color: blue;
34 | `
35 |
36 | delete FakeComponent[TARGET_KEY]
37 |
38 | expect(() => {
39 | css`
40 | ${FakeComponent} {
41 | color: red;
42 | }
43 | `
44 | }).toThrowErrorMatchingSnapshot()
45 | })
46 | })
47 |
--------------------------------------------------------------------------------
/packages/create-emotion-styled/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-emotion-styled",
3 | "version": "9.0.0-0",
4 | "description": "The styled API for emotion",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "types": "types/index.d.ts",
8 | "files": [
9 | "src",
10 | "dist"
11 | ],
12 | "scripts": {
13 | "build": "npm-run-all clean rollup",
14 | "clean": "rimraf dist",
15 | "rollup": "rollup -c ../../rollup.config.js",
16 | "watch": "rollup -c ../../rollup.config.js --watch"
17 | },
18 | "dependencies": {
19 | "emotion-utils": "^9.0.0-0"
20 | },
21 | "peerDependencies": {
22 | "prop-types": ">= 15"
23 | },
24 | "devDependencies": {
25 | "npm-run-all": "^4.0.2",
26 | "prop-types": "^15.6.0",
27 | "rimraf": "^2.6.1",
28 | "rollup": "^0.51.3"
29 | },
30 | "author": "Kye Hohenberger",
31 | "homepage": "https://emotion.sh",
32 | "license": "MIT",
33 | "repository": "https://github.com/emotion-js/emotion/tree/master/packages/create-emotion-styled",
34 | "keywords": [
35 | "styles",
36 | "emotion",
37 | "react",
38 | "css",
39 | "css-in-js"
40 | ],
41 | "bugs": {
42 | "url": "https://github.com/emotion-js/emotion/issues"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/emotion-server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "emotion-server",
3 | "version": "9.0.0-1",
4 | "description": "SSR and style extraction tooling for emotion, The Next Generation of CSS-in-JS.",
5 | "main": "lib/index.js",
6 | "files": [
7 | "src",
8 | "lib"
9 | ],
10 | "scripts": {
11 | "build": "npm-run-all clean babel",
12 | "babel": "babel src -d lib",
13 | "watch": "babel src -d lib --watch",
14 | "clean": "rimraf lib"
15 | },
16 | "dependencies": {
17 | "create-emotion-server": "^9.0.0-1"
18 | },
19 | "peerDependencies": {
20 | "emotion": "^9.0.0-1"
21 | },
22 | "devDependencies": {
23 | "babel-cli": "^6.24.1",
24 | "emotion": "^9.0.0-1",
25 | "npm-run-all": "^4.0.2",
26 | "react-emotion": "^9.0.0-1",
27 | "rimraf": "^2.6.1"
28 | },
29 | "author": "Kye Hohenberger",
30 | "homepage": "https://emotion.sh",
31 | "license": "MIT",
32 | "repository": "https://github.com/emotion-js/emotion/tree/master/packages/emotion-server",
33 | "keywords": [
34 | "styles",
35 | "emotion",
36 | "react",
37 | "css",
38 | "css-in-js",
39 | "ssr",
40 | "server-side-rendering"
41 | ],
42 | "bugs": {
43 | "url": "https://github.com/emotion-js/emotion/issues"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | **What**:
19 |
20 |
21 | **Why**:
22 |
23 |
24 | **How**:
25 |
26 |
27 | **Checklist**:
28 |
29 |
30 | - [ ] Documentation
31 | - [ ] Tests
32 | - [ ] Code complete
33 |
34 |
35 |
--------------------------------------------------------------------------------
/docs/install.md:
--------------------------------------------------------------------------------
1 | ## Install
2 |
3 | ```bash
4 | npm install --save emotion react-emotion babel-plugin-emotion
5 | ```
6 |
7 | All `emotion` APIs are available from the `react-emotion` package.
8 |
9 | ```javascript
10 | import styled, { css, injectGlobal } from 'react-emotion';
11 | ```
12 |
13 | ### .babelrc
14 |
15 | [More information on the babel plugin](babel.md)
16 |
17 | _`"emotion"` must be the **first plugin** in your babel config `plugins` list._
18 |
19 | Takes optional configs:
20 | - [extractStatic](babel.md#Static-Extraction) _{boolean}_
21 | - [sourceMap](babel.md#Static-Extraction) _{boolean}_
22 | ```json
23 | {
24 | "plugins": [
25 | ["emotion"]
26 | ]
27 | }
28 | ```
29 | If using Babel's env option emotion must also be first for each environment.
30 | ```json
31 | {
32 | "env": {
33 | "production": {
34 | "plugins": [
35 | "emotion",
36 | [...all other babel plugins...]
37 | ]
38 | }
39 | },
40 | "plugins": ["emotion"]
41 | }
42 | ```
43 | ### Preact
44 |
45 | Import `preact-emotion` instead of `react-emotion` and use it the same way you would with React.
46 |
47 | ```jsx
48 | import styled from 'preact-emotion'
49 |
50 | const SomeComponent = styled.div`
51 | display: flex;
52 | `
53 | ```
54 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/src/source-map.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { SourceMapGenerator } from 'source-map'
3 | import convert from 'convert-source-map'
4 | import type { EmotionBabelPluginPass } from './index'
5 | import type { BabelFile } from 'babel-flow-types'
6 |
7 | function getGeneratorOpts(file) {
8 | return file.opts.generatorOpts ? file.opts.generatorOpts : file.opts
9 | }
10 |
11 | export function makeSourceMapGenerator(file: BabelFile) {
12 | const generatorOpts = getGeneratorOpts(file)
13 | const filename = generatorOpts.sourceFileName
14 | const generator = new SourceMapGenerator({
15 | file: filename,
16 | sourceRoot: generatorOpts.sourceRoot
17 | })
18 |
19 | generator.setSourceContent(filename, file.code)
20 | return generator
21 | }
22 |
23 | export function addSourceMaps(
24 | offset: { line: number, column: number },
25 | state: EmotionBabelPluginPass
26 | ) {
27 | const generator = makeSourceMapGenerator(state.file)
28 | const generatorOpts = getGeneratorOpts(state.file)
29 | generator.addMapping({
30 | generated: {
31 | line: 1,
32 | column: 0
33 | },
34 | source: generatorOpts.sourceFileName,
35 | original: offset
36 | })
37 | return '\n' + convert.fromObject(generator).toComment({ multiline: true })
38 | }
39 |
--------------------------------------------------------------------------------
/docs/source-maps.md:
--------------------------------------------------------------------------------
1 | ## Source Maps
2 |
3 | **babel plugin required**
4 |
5 | emotion supports source maps for styles authored in javascript.
6 |
7 |
8 |
9 | 
10 |
11 |
12 |
13 | Required For Source Maps:
14 | 1. `babel-plugin-emotion` must be in your Babel setup. [[documentation]](https://github.com/emotion-js/emotion/blob/master/docs/install.md)
15 | 2. `process.env.NODE_ENV` must be any value except `"production"`
16 |
17 | ---
18 |
19 | **We do not advise using sourceMaps in production. The source maps can add significant size to your bundle.**
20 |
21 | **Babel setup**
22 |
23 | **.babelrc**
24 | ```json
25 | {
26 | "plugins": [
27 | ["emotion", { "sourceMap": true }]
28 | ]
29 | }
30 | ```
31 |
32 | **Recommended Setup**
33 |
34 | Use [Babel's `env` property](https://babeljs.io/docs/usage/babelrc/#env-option) and only set source maps in your development environment.
35 |
36 | **.babelrc**
37 | ```json
38 | {
39 | "env": {
40 | "production": {
41 | "plugins": [["emotion", { "sourceMap": false }]]
42 | },
43 | "development": {
44 | "plugins": [["emotion", { "sourceMap": true }]]
45 | }
46 | }
47 | }
48 | ```
49 |
--------------------------------------------------------------------------------
/packages/create-emotion-server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-emotion-server",
3 | "version": "9.0.0-1",
4 | "description": "SSR and style extraction tooling for emotion, The Next Generation of CSS-in-JS.",
5 | "main": "lib/index.js",
6 | "files": [
7 | "src",
8 | "lib"
9 | ],
10 | "scripts": {
11 | "build": "npm-run-all clean babel",
12 | "babel": "babel src -d lib",
13 | "watch": "babel src -d lib --watch",
14 | "clean": "rimraf lib"
15 | },
16 | "dependencies": {
17 | "emotion-utils": "^9.0.0-0",
18 | "html-tokenize": "^2.0.0",
19 | "multipipe": "^1.0.2",
20 | "through": "^2.3.8"
21 | },
22 | "devDependencies": {
23 | "babel-cli": "^6.24.1",
24 | "emotion": "^9.0.0-1",
25 | "npm-run-all": "^4.0.2",
26 | "react-emotion": "^9.0.0-1",
27 | "rimraf": "^2.6.1"
28 | },
29 | "author": "Kye Hohenberger",
30 | "homepage": "https://emotion.sh",
31 | "license": "MIT",
32 | "repository": "https://github.com/emotion-js/emotion/tree/master/packages/create-emotion-server",
33 | "keywords": [
34 | "styles",
35 | "emotion",
36 | "react",
37 | "css",
38 | "css-in-js",
39 | "ssr",
40 | "server-side-rendering"
41 | ],
42 | "bugs": {
43 | "url": "https://github.com/emotion-js/emotion/issues"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/packages/emotion/test/__snapshots__/cx.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`cx all types 1`] = `
4 | .emotion-0 {
5 | font-size: 20px;
6 | background: green;
7 | font-size: 20px;
8 | background: darkorange;
9 | font-size: 20px;
10 | background: darkgreen;
11 | }
12 |
13 |
16 | `;
17 |
18 | exports[`cx fun fun functions 1`] = `
19 | .emotion-0 {
20 | font-size: 20px;
21 | background: green;
22 | font-size: 20px;
23 | background: darkorange;
24 | font-size: 20px;
25 | background: darkgreen;
26 | }
27 |
28 |
31 | `;
32 |
33 | exports[`cx merge 2 1`] = `
34 | .emotion-0 {
35 | font-size: 20px;
36 | background: green;
37 | }
38 |
39 |
42 | `;
43 |
44 | exports[`cx merge 3 1`] = `
45 | .emotion-0 {
46 | font-size: 20px;
47 | background: green;
48 | font-size: 20px;
49 | background: blue;
50 | }
51 |
52 |
55 | `;
56 |
57 | exports[`cx merge 4 1`] = `
58 | .emotion-0 {
59 | font-size: 20px;
60 | background: green;
61 | font-size: 20px;
62 | background: blue;
63 | }
64 |
65 |
68 | `;
69 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
13 |
14 | - `emotion` version:
15 | - `react` version:
16 |
17 | Relevant code.
18 |
19 | ```javascript
20 |
21 | ```
22 |
23 |
24 | What you did:
25 |
26 |
27 |
28 | What happened:
29 |
30 |
36 |
37 | Reproduction:
38 |
39 |
40 |
46 | Problem description:
47 |
48 |
49 |
50 | Suggested solution:
51 |
--------------------------------------------------------------------------------
/docs/composition.md:
--------------------------------------------------------------------------------
1 | ## Composition
2 |
3 | `css` can be used in emotion to build styles that can compose with other styles.
4 |
5 | ```javascript
6 | import { css } from 'emotion'
7 | import styled from 'react-emotion'
8 |
9 | // Define a class
10 | const flexCenter = css`
11 | display: flex;
12 | justify-content: center;
13 | align-items: center;
14 | `
15 |
16 | // interpolate it where you want to apply the styles
17 | const flexCenterClass = css`
18 | ${flexCenter};
19 | flex-direction: column;
20 | `
21 |
22 | // You can also use it in styled and the css prop
23 | const FlexCenterComponent = styled.div`
24 | ${flexCenter};
25 | `
26 |
27 |
28 | const flexWrap = props => css`
29 | flex-wrap: ${props.wrap ? 'wrap' : 'nowrap'};
30 | `
31 |
32 | // You can compose with multiple classes
33 | const ColumnCenteredComponent = styled.div`
34 | ${flexCenter};
35 | ${flexWrap};
36 | `
37 |
38 | // Composition can be very powerful. For example, styles are expanded where you interpolate,
39 | // so the following class has flex-direction: column because ${flexCenterClass} is interpolated
40 | // after flex-direction: row
41 | const stillColumn = css`
42 | flex-direction: row;
43 | ${flexCenterClass}
44 | `
45 |
46 | // Nested composing is supported
47 | const cls = css`
48 | & .flex {
49 | ${flexCenter};
50 | }
51 | `
52 |
53 | ```
54 |
--------------------------------------------------------------------------------
/packages/create-emotion/test/emotion-instance.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import createEmotion from 'create-emotion'
3 | import createEmotionStyled from 'create-emotion-styled'
4 | import createEmotionServer from 'create-emotion-server'
5 | import { transform } from 'cssjanus'
6 | import React from 'react'
7 |
8 | function stylisPlugin(context, content) {
9 | if (context === 2) {
10 | return transform(content)
11 | }
12 | }
13 |
14 | export const container = document.createElement('div')
15 |
16 | // $FlowFixMe
17 | document.head.appendChild(container)
18 |
19 | const emotion = createEmotion(
20 | // don't use a global so the options aren't cached
21 | {},
22 | {
23 | stylisPlugins: stylisPlugin,
24 | prefix: (key, value) => {
25 | if (key === 'display' && value === 'flex') {
26 | return false
27 | }
28 | return true
29 | },
30 | nonce: 'some-nonce',
31 | key: 'some-key',
32 | container
33 | }
34 | )
35 |
36 | export const {
37 | flush,
38 | hydrate,
39 | cx,
40 | merge,
41 | getRegisteredStyles,
42 | injectGlobal,
43 | keyframes,
44 | css,
45 | sheet,
46 | caches
47 | } = emotion
48 |
49 | export const {
50 | extractCritical,
51 | renderStylesToString,
52 | renderStylesToNodeStream
53 | } = createEmotionServer(emotion)
54 |
55 | export default createEmotionStyled(emotion, React)
56 |
--------------------------------------------------------------------------------
/packages/preact-emotion/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "preact-emotion",
3 | "version": "9.0.0-1",
4 | "description": "The Next Generation of CSS-in-JS, for Preact projects.",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "files": [
8 | "dist",
9 | "src",
10 | "macro.js"
11 | ],
12 | "scripts": {
13 | "build": "npm-run-all clean rollup",
14 | "clean": "rimraf dist",
15 | "watch": "rollup -c ../../rollup.config.js --watch",
16 | "rollup": "rollup -c ../../rollup.config.js"
17 | },
18 | "dependencies": {
19 | "babel-plugin-emotion": "^9.0.0-1",
20 | "create-emotion-styled": "^9.0.0-0"
21 | },
22 | "peerDependencies": {
23 | "emotion": "^9.0.0-1"
24 | },
25 | "devDependencies": {
26 | "emotion": "^9.0.0-1",
27 | "npm-run-all": "^4.0.2",
28 | "react-emotion": "^9.0.0-1",
29 | "rimraf": "^2.6.1",
30 | "rollup": "^0.51.3"
31 | },
32 | "author": "Kye Hohenberger",
33 | "homepage": "https://emotion.sh",
34 | "license": "MIT",
35 | "repository": "https://github.com/emotion-js/emotion/tree/master/packages/preact-emotion",
36 | "keywords": [
37 | "styles",
38 | "emotion",
39 | "react",
40 | "preact",
41 | "css",
42 | "css-in-js"
43 | ],
44 | "bugs": {
45 | "url": "https://github.com/emotion-js/emotion/issues"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/create-emotion/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-emotion",
3 | "version": "9.0.0-1",
4 | "description": "The Next Generation of CSS-in-JS.",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "files": [
8 | "src",
9 | "dist",
10 | "types"
11 | ],
12 | "scripts": {
13 | "build": "npm-run-all clean rollup",
14 | "clean": "rimraf dist",
15 | "rollup": "rollup -c ../../rollup.config.js",
16 | "watch": "rollup -c ../../rollup.config.js --watch"
17 | },
18 | "dependencies": {
19 | "emotion-utils": "^9.0.0-0",
20 | "stylis": "^3.3.2",
21 | "stylis-rule-sheet": "^0.0.5"
22 | },
23 | "devDependencies": {
24 | "@types/react": "16.0.16",
25 | "babel-cli": "^6.24.1",
26 | "babel-plugin-transform-define": "^1.3.0",
27 | "cross-env": "^5.0.5",
28 | "dtslint": "^0.2.0",
29 | "npm-run-all": "^4.0.2",
30 | "rimraf": "^2.6.1",
31 | "rollup": "^0.51.3"
32 | },
33 | "author": "Kye Hohenberger",
34 | "homepage": "https://emotion.sh",
35 | "license": "MIT",
36 | "repository": "https://github.com/emotion-js/emotion/tree/master/packages/create-emotion",
37 | "keywords": [
38 | "styles",
39 | "emotion",
40 | "react",
41 | "css",
42 | "css-in-js"
43 | ],
44 | "bugs": {
45 | "url": "https://github.com/emotion-js/emotion/issues"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/flow-typed/npm/react-test-renderer_vx.x.x.js:
--------------------------------------------------------------------------------
1 | // flow-typed signature: b39646fcd6b54764b610c38b101a4fdd
2 | // flow-typed version: <>/react-test-renderer_v^16.0.0/flow_v0.59.0
3 |
4 | /**
5 | * This is an autogenerated libdef stub for:
6 | *
7 | * 'react-test-renderer'
8 | *
9 | * Fill this stub out by replacing all the `any` types.
10 | *
11 | * Once filled out, we encourage you to share your work with the
12 | * community by sending a pull request to:
13 | * https://github.com/flowtype/flow-typed
14 | */
15 |
16 | type TestRendererOptions = {
17 | createNodeMock: (element: React$Element) => any
18 | }
19 |
20 | type ReactTestRendererNode = ReactTestRendererJSON | string
21 |
22 | type ReactTestRendererJSON = {|
23 | type: string,
24 | props: { [propName: string]: any },
25 | children: null | Array,
26 | $$typeof?: Symbol // Optional because we add it with defineProperty().
27 | |}
28 |
29 | declare module 'react-test-renderer' {
30 | declare module.exports: {
31 | create: (
32 | element: React$Element,
33 | options?: TestRendererOptions
34 | ) => {
35 | toJSON: () => ReactTestRendererJSON,
36 | update: (newElement: React$Element) => void,
37 | unmount: () => void,
38 | toTree: () => any,
39 | getInstance: () => any,
40 | root: any
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "babel-plugin-emotion",
3 | "version": "9.0.0-1",
4 | "description": "A recommended babel preprocessing plugin for emotion, The Next Generation of CSS-in-JS.",
5 | "main": "lib/index.js",
6 | "files": [
7 | "src",
8 | "lib"
9 | ],
10 | "scripts": {
11 | "build": "npm-run-all clean babel",
12 | "babel": "babel src -d lib",
13 | "watch": "babel src -d lib --watch",
14 | "clean": "rimraf lib"
15 | },
16 | "dependencies": {
17 | "@babel/helper-module-imports": "7.0.0-beta.32",
18 | "babel-macros": "^1.2.0",
19 | "babel-plugin-syntax-jsx": "^6.18.0",
20 | "convert-source-map": "^1.5.0",
21 | "emotion-utils": "^9.0.0-0",
22 | "find-root": "^1.1.0",
23 | "source-map": "^0.5.7",
24 | "touch": "^1.0.0"
25 | },
26 | "devDependencies": {
27 | "@babel/core": "7.0.0-beta.32",
28 | "babel-cli": "^6.24.1",
29 | "npm-run-all": "^4.0.2",
30 | "rimraf": "^2.6.1"
31 | },
32 | "author": "Kye Hohenberger",
33 | "homepage": "https://emotion.sh",
34 | "license": "MIT",
35 | "repository": "https://github.com/emotion-js/emotion/tree/master/packages/babel-plugin-emotion",
36 | "keywords": [
37 | "styles",
38 | "emotion",
39 | "react",
40 | "css",
41 | "css-in-js"
42 | ],
43 | "bugs": {
44 | "url": "https://github.com/emotion-js/emotion/issues"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/emotion-theming/types/tests.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import * as emotionTheming from '../';
3 | // tslint:disable-next-line:no-duplicate-imports
4 | import { ThemeProvider, withTheme, EmotionThemingModule } from '../';
5 |
6 | const theme = { primary: "green", secondary: "white" };
7 | const CompSFC = (props: { prop: boolean }) =>
;
8 | declare class CompC extends React.Component<{ prop: boolean }> { }
9 |
10 | /**
11 | * Theme Provider with no type
12 | */
13 | ;
14 | theme} />;
15 |
16 | /**
17 | * withTheme with no type
18 | */
19 | const ThemedSFC = withTheme(CompSFC);
20 | ;
21 | ;
22 |
23 | const ThemedComp = withTheme(CompC);
24 | ;
25 | ;
26 |
27 | const { ThemeProvider: TypedThemeProvider, withTheme: typedWithTheme } = emotionTheming as EmotionThemingModule;
28 |
29 | ;
30 | ;
31 | ({ primary: theme.primary, secondary: theme.secondary })} />;
32 |
33 | const TypedThemedSFC = typedWithTheme(ThemedSFC);
34 | ;
35 | ;
36 |
37 | const TypedCompSFC = typedWithTheme(CompSFC);
38 | ;
39 | ;
40 |
--------------------------------------------------------------------------------
/packages/emotion/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "emotion",
3 | "version": "9.0.0-1",
4 | "description": "The Next Generation of CSS-in-JS.",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "types": "types/index.d.ts",
8 | "files": [
9 | "src",
10 | "dist",
11 | "macro.js",
12 | "types"
13 | ],
14 | "scripts": {
15 | "build": "npm-run-all clean rollup",
16 | "test:typescript": "dtslint types",
17 | "clean": "rimraf dist",
18 | "rollup": "rollup -c ../../rollup.config.js",
19 | "watch": "rollup -c ../../rollup.config.js --watch"
20 | },
21 | "dependencies": {
22 | "babel-plugin-emotion": "^9.0.0-1",
23 | "create-emotion": "^9.0.0-1"
24 | },
25 | "devDependencies": {
26 | "@types/react": "16.0.16",
27 | "babel-cli": "^6.24.1",
28 | "babel-plugin-transform-define": "^1.3.0",
29 | "cross-env": "^5.0.5",
30 | "dtslint": "^0.2.0",
31 | "npm-run-all": "^4.0.2",
32 | "rimraf": "^2.6.1",
33 | "rollup": "^0.51.3"
34 | },
35 | "author": "Kye Hohenberger",
36 | "homepage": "https://emotion.sh",
37 | "license": "MIT",
38 | "repository": "https://github.com/emotion-js/emotion/tree/master/packages/emotion",
39 | "keywords": [
40 | "styles",
41 | "emotion",
42 | "react",
43 | "css",
44 | "css-in-js"
45 | ],
46 | "bugs": {
47 | "url": "https://github.com/emotion-js/emotion/issues"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/packages/emotion/test/__snapshots__/inject-global.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`injectGlobal basic 1`] = `
4 | "html {
5 | background: pink;
6 | }
7 |
8 | html.active {
9 | background: red;
10 | }"
11 | `;
12 |
13 | exports[`injectGlobal interpolated value 1`] = `
14 | "body {
15 | color: yellow;
16 | margin: 0;
17 | padding: 0;
18 | }"
19 | `;
20 |
21 | exports[`injectGlobal nested interpolated media query 1`] = `
22 | "@media (max-width:600px) {
23 | body {
24 | display: -webkit-box;
25 | display: -webkit-flex;
26 | display: -ms-flexbox;
27 | display: flex;
28 | }
29 | }"
30 | `;
31 |
32 | exports[`injectGlobal random interpolation 1`] = `
33 | ".css-k008qs {
34 | display: -webkit-box;
35 | display: -webkit-flex;
36 | display: -ms-flexbox;
37 | display: flex;
38 | }
39 |
40 | body {
41 | display: -webkit-box;
42 | display: -webkit-flex;
43 | display: -ms-flexbox;
44 | display: flex;
45 | }"
46 | `;
47 |
48 | exports[`injectGlobal with @font-face 1`] = `
49 | "@font-face {
50 | font-family: 'Patrick Hand SC';
51 | font-style: normal;
52 | font-weight: 400;
53 | src: local('Patrick Hand SC'),local('PatrickHandSC-Regular'),url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2) format('woff2');
54 | unicode-range: U+0100-024f,U+1-1eff,U+20a0-20ab,U+20ad-20cf,U+2c60-2c7f,U+A720-A7FF;
55 | }"
56 | `;
57 |
--------------------------------------------------------------------------------
/packages/emotion-theming/src/with-theme.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as React from 'react'
3 | import hoistNonReactStatics from 'hoist-non-react-statics'
4 | import { channel, contextTypes } from './utils'
5 |
6 | type Props = { theme: Object }
7 |
8 | const withTheme = (Component: React.ComponentType) => {
9 | const componentName = Component.displayName || Component.name || 'Component'
10 |
11 | class WithTheme extends React.Component<{}, { theme: Object }> {
12 | unsubscribeId: number
13 | componentWillMount() {
14 | const themeContext = this.context[channel]
15 | if (themeContext === undefined) {
16 | // eslint-disable-next-line no-console
17 | console.error(
18 | '[withTheme] Please use ThemeProvider to be able to use withTheme'
19 | )
20 | return
21 | }
22 | this.unsubscribeId = themeContext.subscribe(theme => {
23 | this.setState({ theme })
24 | })
25 | }
26 |
27 | componentWillUnmount() {
28 | if (this.unsubscribeId !== -1) {
29 | this.context[channel].unsubscribe(this.unsubscribeId)
30 | }
31 | }
32 |
33 | render() {
34 | return
35 | }
36 | }
37 | WithTheme.displayName = `WithTheme(${componentName})`
38 | WithTheme.contextTypes = contextTypes
39 |
40 | return hoistNonReactStatics(WithTheme, Component)
41 | }
42 |
43 | export default withTheme
44 |
--------------------------------------------------------------------------------
/packages/emotion-utils/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export const memoize = (fn: string => any) => {
4 | const cache = {}
5 | return (arg: string) => {
6 | if (cache[arg] === undefined) cache[arg] = fn(arg)
7 | return cache[arg]
8 | }
9 | }
10 |
11 | export const STYLES_KEY = '__emotion_styles'
12 | export const TARGET_KEY = '__emotion_target'
13 |
14 | export const unitless: { [string]: 1 } = {
15 | animationIterationCount: 1,
16 | borderImageOutset: 1,
17 | borderImageSlice: 1,
18 | borderImageWidth: 1,
19 | boxFlex: 1,
20 | boxFlexGroup: 1,
21 | boxOrdinalGroup: 1,
22 | columnCount: 1,
23 | columns: 1,
24 | flex: 1,
25 | flexGrow: 1,
26 | flexPositive: 1,
27 | flexShrink: 1,
28 | flexNegative: 1,
29 | flexOrder: 1,
30 | gridRow: 1,
31 | gridRowEnd: 1,
32 | gridRowSpan: 1,
33 | gridRowStart: 1,
34 | gridColumn: 1,
35 | gridColumnEnd: 1,
36 | gridColumnSpan: 1,
37 | gridColumnStart: 1,
38 | fontWeight: 1,
39 | lineClamp: 1,
40 | lineHeight: 1,
41 | opacity: 1,
42 | order: 1,
43 | orphans: 1,
44 | tabSize: 1,
45 | widows: 1,
46 | zIndex: 1,
47 | zoom: 1,
48 |
49 | // SVG-related properties
50 | fillOpacity: 1,
51 | floodOpacity: 1,
52 | stopOpacity: 1,
53 | strokeDasharray: 1,
54 | strokeDashoffset: 1,
55 | strokeMiterlimit: 1,
56 | strokeOpacity: 1,
57 | strokeWidth: 1
58 | }
59 |
60 | export { hashString } from './hash'
61 | export { default as Stylis } from './stylis'
62 |
--------------------------------------------------------------------------------
/packages/emotion/test/sheet.dom.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { sheet } from 'emotion'
3 |
4 | describe('sheet', () => {
5 | beforeEach(() => {
6 | sheet.flush()
7 | sheet.inject()
8 | })
9 |
10 | test('speedy', () => {
11 | expect(sheet.isSpeedy).toBe(false)
12 | sheet.speedy(true)
13 | expect(sheet.isSpeedy).toBe(true)
14 | sheet.speedy(false)
15 | expect(sheet.isSpeedy).toBe(false)
16 | })
17 |
18 | test('tags', () => {
19 | sheet.speedy(true)
20 | const rule = '.foo { color: blue; }'
21 | sheet.insert(rule)
22 | expect(sheet.tags).toMatchSnapshot()
23 | expect(sheet.tags.length).toBe(1)
24 | })
25 |
26 | test('flush', () => {
27 | sheet.speedy(true)
28 | sheet.insert('.foo { color: blue; }')
29 | sheet.flush()
30 | expect(sheet.tags.length).toBe(0)
31 | })
32 |
33 | test('throws', () => {
34 | sheet.speedy(true)
35 | const spy = jest.spyOn(global.console, 'warn')
36 | sheet.insert('.asdfasdf4###112121211{')
37 | expect(spy).toHaveBeenCalled()
38 | })
39 |
40 | test('inject method throws if the sheet is already injected', () => {
41 | expect(() => {
42 | sheet.inject()
43 | }).toThrowErrorMatchingSnapshot()
44 | })
45 | test('.speedy throws when a rule has already been inserted', () => {
46 | sheet.insert('.foo { color: blue; }')
47 | expect(() => {
48 | sheet.speedy(true)
49 | }).toThrowErrorMatchingSnapshot()
50 | })
51 | })
52 |
--------------------------------------------------------------------------------
/docs/extract-static.md:
--------------------------------------------------------------------------------
1 | ## Extract Static
2 | ###### [requires babel plugin](babel.md)
3 |
4 | Extract styles with no interpolations into external css files.
5 |
6 | While there are some beneficial use cases for `extractStatic`, emotion's inherent performance since version 8 makes the added complexity of this feature somewhat disadvantageous.
7 |
8 |
9 | **does NOT work with object styles**
10 |
11 | **If you want to use dynamic values you must use css variables.**
12 |
13 | ```javascript
14 | const Button = styled('button')`
15 | background-color: var(--bg);
16 | padding: 10px;
17 | `
18 |
19 | ```
20 |
21 |
22 | Configure babel
23 |
24 | ```bash
25 | npm install babel-plugin-emotion -D
26 | ```
27 |
28 | **.babelrc**
29 | ```json
30 | {
31 | "plugins": [
32 | ["emotion", { "extractStatic": true }]
33 | ]
34 | }
35 | ```
36 |
37 | This js file, `h1.js`
38 |
39 | ```jsx harmony
40 | import styled from 'react-emotion'
41 |
42 | const H1 = styled('h1')`
43 | color: #ffd43b;
44 | `
45 | ```
46 |
47 | During babel compilation emotion will create `h1.emotion.css` and add `import './h1.emotion.css'` to the top of `h1.js`
48 |
49 | ```css
50 | // h1.emotion.css
51 | .css-H1-duiy4a {
52 | color: blue
53 | }
54 | ```
55 |
56 | `h1.js` after babel compilation
57 |
58 | ```jsx
59 | import './h1.emotion.css'
60 | import styled from 'react-emotion'
61 |
62 | const H1 = styled('h1', 'css-duiy4a')
63 | ```
64 |
--------------------------------------------------------------------------------
/packages/site/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "emotion-example-project",
3 | "private": true,
4 | "version": "9.0.0-1",
5 | "description": "emotion example project",
6 | "scripts": {
7 | "start:site": "webpack-dev-server --content-base build/ --port 3000",
8 | "build:site": "webpack --env production --progress"
9 | },
10 | "dependencies": {
11 | "babel-plugin-emotion": "^9.0.0-1",
12 | "emotion": "^9.0.0-1",
13 | "open-color": "^1.5.1",
14 | "react": "^15.5.4",
15 | "react-dom": "^15.5.4",
16 | "react-emotion": "^9.0.0-1",
17 | "react-markdown": "^2.5.0",
18 | "uglifyjs-webpack-plugin": "^1.1.1"
19 | },
20 | "devDependencies": {
21 | "babel-core": "^6.23.1",
22 | "babel-eslint": "^7.2.3",
23 | "babel-loader": "^7.1.1",
24 | "babel-plugin-transform-class-properties": "^6.24.1",
25 | "babel-preset-env": "^1.6.0",
26 | "babel-preset-react": "^6.24.1",
27 | "babel-preset-stage-0": "^6.24.1",
28 | "babel-standalone": "^6.24.2",
29 | "component-playground": "^2.0.0",
30 | "css-loader": "^0.28.4",
31 | "emotion-theming": "^9.0.0-1",
32 | "extract-text-webpack-plugin": "^2.1.0",
33 | "file-loader": "^0.11.2",
34 | "html-webpack-plugin": "^2.28.0",
35 | "raw-loader": "^0.5.1",
36 | "style-loader": "^0.18.1",
37 | "url-loader": "^0.5.9",
38 | "webpack": "^2.2.0",
39 | "webpack-dev-server": "^2.4.5"
40 | },
41 | "author": "Kye Hohenberger",
42 | "license": "MIT"
43 | }
44 |
--------------------------------------------------------------------------------
/packages/create-emotion-styled/src/utils.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { memoize } from 'emotion-utils'
3 | import type { Interpolations } from 'create-emotion'
4 |
5 | export function setTheme(theme: Object) {
6 | this.setState({ theme })
7 | }
8 |
9 | declare var codegen: { require: (path: string) => * }
10 |
11 | const reactPropsRegex: RegExp = codegen.require('./props')
12 | export const testOmitPropsOnStringTag: (key: string) => boolean = memoize(key =>
13 | reactPropsRegex.test(key)
14 | )
15 | export const testOmitPropsOnComponent = (key: string) =>
16 | key !== 'theme' && key !== 'innerRef'
17 | export const testAlwaysTrue = () => true
18 |
19 | export const omitAssign: (
20 | testFn: (key: string) => boolean,
21 | target: {},
22 | ...sources: Array<{}>
23 | ) => Object = function(testFn, target) {
24 | let i = 2
25 | let length = arguments.length
26 | for (; i < length; i++) {
27 | let source = arguments[i]
28 | let key
29 | for (key in source) {
30 | if (testFn(key)) {
31 | target[key] = source[key]
32 | }
33 | }
34 | }
35 | return target
36 | }
37 |
38 | export type StyledOptions = { e: string, label: string, target: string }
39 |
40 | type CreateStyledComponent = (...args: Interpolations) => *
41 |
42 | type BaseCreateStyled = (
43 | tag: any,
44 | options?: StyledOptions
45 | ) => CreateStyledComponent
46 |
47 | export type CreateStyled = {
48 | $call: BaseCreateStyled,
49 | [key: string]: CreateStyledComponent
50 | }
51 |
--------------------------------------------------------------------------------
/packages/react-emotion/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-emotion",
3 | "version": "9.0.0-1",
4 | "description": "The Next Generation of CSS-in-JS, for React projects.",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "types": "types/index.d.ts",
8 | "files": [
9 | "src",
10 | "dist",
11 | "macro.js",
12 | "types"
13 | ],
14 | "scripts": {
15 | "build": "npm-run-all clean rollup",
16 | "test:typescript": "dtslint types",
17 | "clean": "rimraf dist",
18 | "rollup": "rollup -c ../../rollup.config.js",
19 | "watch": "rollup -c ../../rollup.config.js --watch"
20 | },
21 | "dependencies": {
22 | "babel-plugin-emotion": "^9.0.0-1",
23 | "create-emotion-styled": "^9.0.0-0"
24 | },
25 | "peerDependencies": {
26 | "emotion": "^9.0.0-1"
27 | },
28 | "devDependencies": {
29 | "@types/react": "16.0.16",
30 | "cross-env": "^5.0.5",
31 | "dtslint": "^0.2.0",
32 | "emotion": "^9.0.0-1",
33 | "npm-run-all": "^4.0.2",
34 | "rimraf": "^2.6.1",
35 | "rollup": "^0.51.3"
36 | },
37 | "author": "Kye Hohenberger",
38 | "homepage": "https://emotion.sh",
39 | "license": "MIT",
40 | "repository": "https://github.com/emotion-js/emotion/tree/master/packages/react-emotion",
41 | "keywords": [
42 | "styles",
43 | "emotion",
44 | "react",
45 | "css",
46 | "css-in-js"
47 | ],
48 | "bugs": {
49 | "url": "https://github.com/emotion-js/emotion/issues"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/docs/media-queries.md:
--------------------------------------------------------------------------------
1 | # Media Queries
2 |
3 | ```jsx
4 | const Avatar = styled('img')`
5 | width: 32px;
6 | height: 32px;
7 | border-radius: 50%;
8 |
9 | @media (min-width: 420px) {
10 | width: 96px;
11 | height: 96px;
12 | }
13 | `
14 |
15 | render( )
16 | ```
17 |
18 | ## Reusable Media Queries
19 |
20 | [Demo](https://stackblitz.com/edit/react-wudbyn)
21 |
22 | ```jsx
23 | const breakpoints = {
24 | // Numerical values will result in a min-width query
25 | small: 576,
26 | medium: 768,
27 | large: 992,
28 | xLarge: 1200,
29 | // String values will be used as is
30 | tallPhone: '(max-width: 360px) and (min-height: 740px)'
31 | };
32 |
33 | export const queries = Object.keys(breakpoints).reduce((accumulator, label) => {
34 | if (typeof breakpoints[label] === 'string') {
35 | accumulator[label] = (...args) =>
36 | css`
37 | @media (${breakpoints[label]}) {
38 | ${css(...args)};
39 | }
40 | `;
41 | } else {
42 | accumulator[label] = (...args) =>
43 | css`
44 | @media (min-width: ${breakpoints[label]}px) {
45 | ${css(...args)};
46 | }
47 | `;
48 | }
49 |
50 | return accumulator;
51 | }, {});
52 | ```
53 |
54 | ```jsx
55 | import { queries } from './mediaQueries.js`;
56 |
57 | const paragraph = css`
58 | font-size: 12px;
59 |
60 | ${queries.medium`
61 | font-size: 14px;
62 | `}
63 |
64 | ${queries.large`
65 | font-size: 16px;
66 | `}
67 | `;
68 | ```
69 |
--------------------------------------------------------------------------------
/docs/object-styles.md:
--------------------------------------------------------------------------------
1 | ## Object Styles
2 |
3 | Writing styles with objects is a powerful pattern built directly into the core of emotion.
4 |
5 | ### Examples
6 |
7 | #### With `css`
8 |
9 | ```javascript
10 | import { css } from 'emotion'
11 |
12 | const className = css({ color: 'darkorchid' })
13 | ```
14 |
15 |
16 | #### With `styled`
17 |
18 | `styled` is a thin wrapper around `css` and accepts the same arguments.
19 |
20 | ```javascript
21 | import styled from 'react-emotion'
22 |
23 | const A = styled('a')({ color: 'darkorchid' })
24 | ```
25 |
26 |
27 |
28 | ### Child Selectors
29 |
30 | ```javascript
31 | import { css } from 'emotion'
32 |
33 | const className = css({
34 | color: 'darkorchid',
35 | '& .name': {
36 | color: 'orange'
37 | }
38 | })
39 | ```
40 |
41 | ### Media Queries
42 |
43 | ```javascript
44 | import { css } from 'emotion'
45 |
46 | const className = css({
47 | color: 'darkorchid',
48 | '@media(min-width: 420px)': {
49 | color: 'orange'
50 | }
51 | })
52 | ```
53 |
54 | ### Multiple Arguments
55 |
56 | ```javascript
57 | import { css } from 'emotion'
58 |
59 | const className = css({
60 | color: 'darkorchid'
61 | }, {
62 | backgroundColor: 'hotpink'
63 | }, {
64 | height: 20
65 | })
66 | ```
67 |
68 | ### Arrays
69 |
70 | Nested arrays are flattened.
71 |
72 | ```javascript
73 | import { css } from 'emotion'
74 |
75 | const className = css([{
76 | color: 'darkorchid'
77 | }, {
78 | backgroundColor: 'hotpink'
79 | }, {
80 | height: 20
81 | }])
82 | ```
83 |
--------------------------------------------------------------------------------
/packages/benchmarks/src/components/SierpinskiTriangle.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | interpolatePurples,
4 | interpolateBuPu,
5 | interpolateRdPu
6 | } from 'd3-scale-chromatic'
7 |
8 | let targetSize = 25
9 |
10 | export default function SierpinskiTriangle({
11 | x,
12 | y,
13 | s,
14 | depth = 0,
15 | renderCount = 0,
16 | Dot
17 | }) {
18 | if (s <= targetSize) {
19 | let fn
20 | switch (depth) {
21 | case 1:
22 | fn = interpolatePurples
23 | break
24 | case 2:
25 | fn = interpolateBuPu
26 | break
27 | case 3:
28 | default:
29 | fn = interpolateRdPu
30 | }
31 |
32 | return (
33 |
39 | )
40 | }
41 |
42 | s /= 2
43 |
44 | return [
45 | ,
54 | ,
63 |
72 | ]
73 | }
74 |
--------------------------------------------------------------------------------
/packages/emotion/test/prod-mode/__snapshots__/prod-mode.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`production mode production mode basic 1`] = `
4 | .emotion-0 {
5 | color: yellow;
6 | }
7 |
8 | .emotion-0 .some-class {
9 | display: -webkit-box;
10 | display: -webkit-flex;
11 | display: -ms-flexbox;
12 | display: flex;
13 | }
14 |
15 | .emotion-0 .some-class .some-other-class {
16 | background-color: hotpink;
17 | }
18 |
19 | @media (max-width:600px) {
20 | .emotion-0 .some-class {
21 | background-color: pink;
22 | }
23 | }
24 |
25 |
36 | `;
37 |
38 | exports[`production mode production mode nested media queries 1`] = `
39 | "@media (max-width:600px) {
40 | .css-acogag h1 {
41 | font-size: 1.4rem;
42 | }
43 | }
44 |
45 | @media (max-width:400px),(max-height:420px) {
46 | .css-acogag h1 {
47 | font-size: 1.1rem;
48 | }
49 | }"
50 | `;
51 |
52 | exports[`production mode production mode nested styles 1`] = `
53 | ".css-s7aswl {
54 | color: blue;
55 | }
56 |
57 | .css-s7aswl:hover {
58 | color: green;
59 | }
60 |
61 | .css-s7aswl:hover .name {
62 | color: amethyst;
63 | }
64 |
65 | .css-s7aswl:hover .name:focus {
66 | color: burlywood;
67 | }
68 |
69 | @media (min-width:420px) {
70 | .css-s7aswl:hover .name:focus {
71 | color: rebeccapurple;
72 | }
73 | }"
74 | `;
75 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | emotion
5 |
6 |
7 | high performance css-in-js
8 |
9 | [github](https://github.com/emotion-js/emotion)
10 | [npm](https://npm.im/emotion)
11 |
12 | In order to learn more about emotion lets build this guide together. By following along below, we can get this markdown looking good.
13 |
14 | #### Heading
15 |
16 | 👈 Find the `Heading` declaration and modify it to match the example below.
17 |
18 | ```jsx
19 | const Heading = styled('h1')`
20 | color: #da77f2;
21 | letter-spacing: 1px;
22 | `
23 | ```
24 |
25 | #### Links
26 |
27 | Change them to green and fix up their font.
28 |
29 | 👈 Find the `Link` declaration and modify it to match the example below.
30 |
31 | ```jsx
32 | const Link = styled('a')`
33 | font-size: 1rem;
34 | margin-left: auto;
35 | margin-right: 8px;
36 | text-decoration: none;
37 | color: ${colors.green[4]};
38 |
39 | p & {
40 | margin: 0;
41 | }
42 |
43 | &:hover {
44 | color: ${colors.green[8]};
45 | }
46 | `
47 | ```
48 |
49 | #### Paragraphs
50 |
51 | Now lets clean up the paragraphs.
52 |
53 | 👈 Find the `Paragraph` declaration and modify it to match the example below.
54 |
55 | ```jsx
56 | const Paragraph = styled('div')`
57 | margin: 16px 0;
58 | padding: 2px;
59 | font-size: 0.85rem;
60 | color: ${colors.gray[8]};
61 | `
62 | ```
63 |
64 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Emotion Documentation
2 |
3 | - [emotion.sh](https://emotion.sh) Interactive Documentation
4 | - [Code Sandbox](https://codesandbox.io/s/pk1qjqpw67) Interactive Playground
5 |
6 | - [Install](https://github.com/emotion-js/emotion/tree/master/docs/install.md)
7 | - [Configurable Imports](https://github.com/emotion-js/emotion/tree/master/docs/configurable-imports.md) Useful when migrating from another css-in-js library such as styled-components or styled-jsx
8 | - [Nested Selectors](https://github.com/emotion-js/emotion/tree/master/docs/nested.md)
9 | - [Composition](https://github.com/emotion-js/emotion/tree/master/docs/composition.md)
10 | - [Injecting Global Styles](https://github.com/emotion-js/emotion/tree/master/docs/inject-global.md)
11 | - [Keyframe Animations](https://github.com/emotion-js/emotion/tree/master/docs/keyframes.md)
12 | - [Theming](https://github.com/emotion-js/emotion/tree/master/docs/theming.md)
13 | - [Server Side Rendering](https://github.com/emotion-js/emotion/tree/master/docs/ssr.md)
14 | - [Source Maps](https://github.com/emotion-js/emotion/tree/master/docs/source-maps.md)
15 | - [Extracting Static Styles](https://github.com/emotion-js/emotion/tree/master/docs/extract-static.md)
16 | - [Using `withProps` To Attach Props](https://github.com/emotion-js/emotion/tree/master/docs/with-props.md) (styled-components `.attrs` api)
17 | - [Usage with babel-macros](https://github.com/emotion-js/emotion/tree/master/docs/babel.md#usage-with-babel-macros)
18 | - [TypeScript](https://github.com/emotion-js/emotion/tree/master/docs/typescript.md)
19 |
--------------------------------------------------------------------------------
/packages/emotion-theming/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "emotion-theming",
3 | "version": "9.0.0-1",
4 | "description": "A CSS-in-JS theming solution, inspired by styled-components",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "types": "types/index.d.ts",
8 | "files": [
9 | "src",
10 | "dist",
11 | "types"
12 | ],
13 | "scripts": {
14 | "build": "npm-run-all clean rollup",
15 | "test:typescript": "dtslint types",
16 | "clean": "rimraf dist",
17 | "rollup": "rollup -c ../../rollup.config.js",
18 | "watch": "rollup -c ../../rollup.config.js --watch"
19 | },
20 | "repository": "https://github.com/emotion-js/emotion/tree/master/packages/emotion-theming",
21 | "keywords": [
22 | "react",
23 | "theme",
24 | "theming",
25 | "emotion",
26 | "cssinjs",
27 | "css-in-js"
28 | ],
29 | "author": "styled-components team",
30 | "contributors": [
31 | "Evan Scott (https://github.com/probablyup)"
32 | ],
33 | "license": "MIT",
34 | "bugs": {
35 | "url": "https://github.com/emotion-js/emotion/issues"
36 | },
37 | "homepage": "https://emotion.sh",
38 | "devDependencies": {
39 | "@types/react": "16.0.16",
40 | "cross-env": "^5.0.1",
41 | "dtslint": "^0.2.0",
42 | "prop-types": "^15.5.8",
43 | "rimraf": "^2.6.1",
44 | "rollup": "^0.51.3"
45 | },
46 | "dependencies": {
47 | "hoist-non-react-statics": "^2.3.1"
48 | },
49 | "peerDependencies": {
50 | "prop-types": ">= 15",
51 | "react": ">= 15"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/packages/emotion-server/test/index.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * @jest-environment node
4 | */
5 | import React from 'react'
6 | import { renderToString } from 'react-dom/server'
7 | import { getComponents, prettyifyCritical, getInjectedRules } from './util'
8 |
9 | let emotion = require('emotion')
10 | let reactEmotion = require('react-emotion')
11 | let emotionServer = require('emotion-server')
12 |
13 | describe('extractCritical', () => {
14 | test('returns static css', () => {
15 | const { Page1, Page2 } = getComponents(emotion, reactEmotion)
16 | expect(
17 | prettyifyCritical(
18 | emotionServer.extractCritical(renderToString( ))
19 | )
20 | ).toMatchSnapshot()
21 | expect(
22 | prettyifyCritical(
23 | emotionServer.extractCritical(renderToString( ))
24 | )
25 | ).toMatchSnapshot()
26 | })
27 | })
28 | describe('hydration', () => {
29 | test('only rules that are not in the critical css are inserted', () => {
30 | const { Page1 } = getComponents(emotion, reactEmotion)
31 | const { html, ids, css } = emotionServer.extractCritical(
32 | renderToString( )
33 | )
34 | expect(prettyifyCritical({ html, css, ids })).toMatchSnapshot()
35 | jest.resetModules()
36 | global.__SECRET_EMOTION__ = undefined
37 | emotion = require('emotion')
38 | emotionServer = require('emotion-server')
39 | expect(emotion.caches.inserted).toEqual({})
40 | emotion.hydrate(ids)
41 | const { Page1: NewPage1 } = getComponents(emotion, reactEmotion)
42 | renderToString( )
43 | expect(getInjectedRules(emotion)).toMatchSnapshot()
44 | })
45 | })
46 |
--------------------------------------------------------------------------------
/docs/testing.md:
--------------------------------------------------------------------------------
1 | ## Snapshot Testing
2 |
3 | Adding [snapshot tests with Jest](https://facebook.github.io/jest/docs/en/snapshot-testing.html) is a great way to help avoid unintended changes to your app's UI.
4 |
5 | By diffing the serialized value of your React tree Jest can show you what changed in your app and allow you to fix it or update the snapshot.
6 |
7 | By default snapshots with emotion show generated class names. Adding [jest-emotion](https://github.com/emotion-js/emotion/tree/master/packages/jest-emotion) allows you to output the actual styles being applied.
8 |
9 |
10 |
11 |
12 | ### Installation
13 |
14 | ```bash
15 | npm install --save-dev jest-emotion
16 | ```
17 |
18 | **testSetup.js** _or_ at the top of your test file
19 |
20 | ```javascript
21 | import * as emotion from 'emotion'
22 | import { createSerializer } from 'jest-emotion'
23 |
24 | expect.addSnapshotSerializer(createSerializer(emotion))
25 | ```
26 |
27 | **package.json**
28 |
29 | ```json
30 | "jest": {
31 | [...]
32 | "setupTestFrameworkScriptFile": "/testSetup.js",
33 | "testEnvironment": "jsdom"
34 | [...]
35 | }
36 | ```
37 |
38 | ### Adding a test
39 |
40 | ```javascript
41 | import React from 'react';
42 | import renderer from 'react-test-renderer';
43 | import App from './App';
44 |
45 | test('Link renders correctly', () => {
46 | const tree = renderer.create( ).toJSON();
47 | expect(tree).toMatchSnapshot();
48 | });
49 | ```
50 |
51 | ### Notes
52 |
53 | Your snapshot class names will appear as `emotion-[0...n]` instead of `css-[hash]`.
54 |
55 |
--------------------------------------------------------------------------------
/packages/emotion-utils/src/hash.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | // murmurhash2 via https://gist.github.com/raycmorgan/588423
3 |
4 | export function hashString(str: string) {
5 | return hash(str, str.length).toString(36)
6 | }
7 |
8 | function hash(str: string, seed: number) {
9 | let m = 0x5bd1e995
10 | let r = 24
11 | let h = seed ^ str.length
12 | let length = str.length
13 | let currentIndex = 0
14 |
15 | while (length >= 4) {
16 | let k = UInt32(str, currentIndex)
17 |
18 | k = Umul32(k, m)
19 | k ^= k >>> r
20 | k = Umul32(k, m)
21 |
22 | h = Umul32(h, m)
23 | h ^= k
24 |
25 | currentIndex += 4
26 | length -= 4
27 | }
28 |
29 | switch (length) {
30 | case 3:
31 | h ^= UInt16(str, currentIndex)
32 | h ^= str.charCodeAt(currentIndex + 2) << 16
33 | h = Umul32(h, m)
34 | break
35 |
36 | case 2:
37 | h ^= UInt16(str, currentIndex)
38 | h = Umul32(h, m)
39 | break
40 |
41 | case 1:
42 | h ^= str.charCodeAt(currentIndex)
43 | h = Umul32(h, m)
44 | break
45 | }
46 |
47 | h ^= h >>> 13
48 | h = Umul32(h, m)
49 | h ^= h >>> 15
50 |
51 | return h >>> 0
52 | }
53 |
54 | function UInt32(str, pos) {
55 | return (
56 | str.charCodeAt(pos++) +
57 | (str.charCodeAt(pos++) << 8) +
58 | (str.charCodeAt(pos++) << 16) +
59 | (str.charCodeAt(pos) << 24)
60 | )
61 | }
62 |
63 | function UInt16(str, pos) {
64 | return str.charCodeAt(pos++) + (str.charCodeAt(pos++) << 8)
65 | }
66 |
67 | function Umul32(n, m) {
68 | n = n | 0
69 | m = m | 0
70 | let nlo = n & 0xffff
71 | let nhi = n >>> 16
72 | let res = (nlo * m + (((nhi * m) & 0xffff) << 16)) | 0
73 | return res
74 | }
75 |
--------------------------------------------------------------------------------
/packages/benchmarks/webpack.config.js:
--------------------------------------------------------------------------------
1 | // const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
2 | // .BundleAnalyzerPlugin
3 | const path = require('path')
4 | const webpack = require('webpack')
5 | const HTMLWebpackPlugin = require('html-webpack-plugin')
6 |
7 | module.exports = {
8 | // context: __dirname,
9 | // devtool: 'eval',
10 | entry: ['babel-polyfill', './index'],
11 | output: {
12 | path: path.resolve(__dirname, 'dist'),
13 | filename: 'performance.bundle.js'
14 | },
15 | module: {
16 | rules: [
17 | {
18 | test: /\.css$/,
19 | use: [
20 | 'style-loader',
21 | {
22 | loader: 'css-loader',
23 | options: { module: true, localIdentName: '[hash:base64:8]' }
24 | }
25 | ]
26 | },
27 | {
28 | test: /\.js$/,
29 | exclude: /node_modules/,
30 | use: {
31 | loader: 'babel-loader',
32 | options: {
33 | babelrc: false,
34 | presets: [
35 | ['env', { modules: false, useBuiltIns: true, debug: true }],
36 | 'react',
37 | 'stage-0'
38 | ],
39 | plugins: ['babel-macros'],
40 | cacheDirectory: true
41 | }
42 | }
43 | }
44 | ]
45 | },
46 | plugins: [
47 | // new BundleAnalyzerPlugin({
48 | // analyzerMode: 'static',
49 | // openAnalyzer: false
50 | // }),
51 | new webpack.DefinePlugin({
52 | 'process.env.NODE_ENV': JSON.stringify('production')
53 | }),
54 | new webpack.optimize.UglifyJsPlugin(),
55 | new HTMLWebpackPlugin({ template: path.join(__dirname, './index.html') })
56 | ]
57 | }
58 |
--------------------------------------------------------------------------------
/packages/emotion/test/source-map/source-map.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import renderer from 'react-test-renderer'
3 | import { css, sheet, flush } from 'emotion'
4 |
5 | describe('css', () => {
6 | afterEach(() => flush())
7 | test('source-map nested styles', () => {
8 | const mq = [
9 | '@media(min-width: 420px)',
10 | '@media(min-width: 640px)',
11 | '@media(min-width: 960px)'
12 | ]
13 |
14 | css({
15 | color: 'blue',
16 | '&:hover': {
17 | '& .name': {
18 | color: 'amethyst',
19 | '&:focus': {
20 | color: 'burlywood',
21 | [mq[0]]: {
22 | color: 'rebeccapurple'
23 | }
24 | }
25 | },
26 | color: 'green'
27 | }
28 | })
29 | expect(sheet).toMatchSnapshot()
30 | })
31 |
32 | test('source-map nested media queries', () => {
33 | css`
34 | @media (max-width: 600px) {
35 | h1 {
36 | font-size: 1.4rem;
37 | }
38 | }
39 |
40 | @media (max-width: 400px), (max-height: 420px) {
41 | h1 {
42 | font-size: 1.1rem;
43 | }
44 | }
45 | `
46 |
47 | expect(sheet).toMatchSnapshot()
48 | })
49 | test('css prop with merge', () => {
50 | const tree = renderer
51 | .create(
52 |
53 | )
54 | .toJSON()
55 | expect(tree).toMatchSnapshot()
56 | expect(sheet).toMatchSnapshot()
57 | })
58 | test('css without newline or semicolon', () => {
59 | // eslint-disable-next-line
60 | const cls = css`color: hotpink`
61 | expect(sheet).toMatchSnapshot()
62 | })
63 | })
64 |
--------------------------------------------------------------------------------
/packages/emotion/types/tests.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | sheet,
3 | useStylisPlugin,
4 | injectGlobal,
5 | flush,
6 | css,
7 | fontFace,
8 | hydrate,
9 | cx
10 | } from '../';
11 | // tslint:disable-next-line:no-implicit-dependencies
12 | import React from 'react';
13 |
14 | sheet.speedy(true);
15 | sheet.inject();
16 | sheet.insert('.foo { font-size: 1 };', 'some source map');
17 | sheet.flush();
18 |
19 | useStylisPlugin(() => {})([() => {}, () => {}])(null);
20 |
21 | flush();
22 |
23 | const cssObject = {
24 | height: 100,
25 | width: '100%',
26 | display: (true as boolean) && 'block',
27 | position: undefined,
28 | color: null,
29 | ':hover': {
30 | display: 'block'
31 | }
32 | };
33 |
34 | const className: string = css`
35 | ${(true as boolean) && ''}
36 | ${'bar'}
37 | ${css``}
38 | ${1}
39 | ${cssObject}
40 | `;
41 |
42 | const className2: string = css(cssObject);
43 |
44 | css(() => ({
45 | height: 100
46 | }));
47 |
48 | css([
49 | { display: 'none' },
50 | [
51 | { position: false },
52 | { width: 100 }
53 | ]
54 | ]);
55 |
56 | css(
57 | { display: 'none' },
58 | [
59 | { position: false },
60 | { width: 100 }
61 | ]
62 | );
63 |
64 | css(null);
65 |
66 | fontFace`
67 | font-family: 'Foo';
68 | `;
69 |
70 | injectGlobal`
71 | #foo {
72 | font-face: 'Foo';
73 | }
74 | `;
75 |
76 | const cxResult: string = cx(() => () => [
77 | () => [className, false && className2, 'modal'],
78 | () => [() => [className, () => ({ [className2]: true }), 'profile']]
79 | ]);
80 |
81 | hydrate(['css-123', 'css-456']);
82 |
83 | /*
84 | * Can use css prop, transpiled by babel plugin
85 | */
86 |
87 |
;
88 |
;
89 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/src/macro.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { replaceCssWithCallExpression } from './index'
3 | import { buildMacroRuntimeNode, addRuntimeImports } from './babel-utils'
4 | import { createMacro } from 'babel-macros'
5 |
6 | module.exports = createMacro(macro)
7 |
8 | function macro({ references, state, babel: { types: t } }) {
9 | Object.keys(references).forEach(referenceKey => {
10 | let isPure = true
11 | switch (referenceKey) {
12 | case 'injectGlobal': {
13 | isPure = false
14 | }
15 | // eslint-disable-next-line no-fallthrough
16 | case 'css':
17 | case 'keyframes': {
18 | references[referenceKey].reverse().forEach(reference => {
19 | const path = reference.parentPath
20 | const runtimeNode = buildMacroRuntimeNode(
21 | reference,
22 | state,
23 | referenceKey,
24 | t
25 | )
26 | if (t.isTaggedTemplateExpression(path)) {
27 | replaceCssWithCallExpression(
28 | path,
29 | runtimeNode,
30 | state,
31 | t,
32 | undefined,
33 | !isPure
34 | )
35 | } else {
36 | if (isPure) {
37 | path.addComment('leading', '#__PURE__')
38 | }
39 | reference.replaceWith(runtimeNode)
40 | }
41 | })
42 | break
43 | }
44 | default: {
45 | references[referenceKey].reverse().forEach(reference => {
46 | reference.replaceWith(
47 | buildMacroRuntimeNode(reference, state, referenceKey, t)
48 | )
49 | })
50 | }
51 | }
52 | })
53 | addRuntimeImports(state, t)
54 | }
55 |
--------------------------------------------------------------------------------
/packages/emotion/test/keyframes.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React from 'react'
3 | import renderer from 'react-test-renderer'
4 | import { keyframes, sheet, flush } from 'emotion'
5 | import styled from 'react-emotion'
6 |
7 | describe('keyframes', () => {
8 | afterEach(() => {
9 | flush()
10 | })
11 | test('renders', () => {
12 | const bounce = keyframes`
13 | from, 20%, 53%, 80%, to {
14 | animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
15 | transform: translate3d(0,0,0);
16 | }
17 |
18 | 40%, 43% {
19 | animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
20 | transform: translate3d(0, -30px, 0);
21 | }
22 |
23 | 70% {
24 | animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
25 | transform: translate3d(0, -15px, 0);
26 | }
27 |
28 | 90% {
29 | transform: translate3d(0,-4px,0);
30 | }
31 | `
32 |
33 | const H1 = styled.h1`
34 | animation: ${bounce} 2s linear infinite;
35 | `
36 |
37 | const tree = renderer.create(hello world ).toJSON()
38 |
39 | expect(tree).toMatchSnapshot()
40 | expect(sheet).toMatchSnapshot()
41 | })
42 | test('keyframes with interpolation', () => {
43 | const endingRotation = '360deg'
44 |
45 | const H1 = styled.h1`
46 | animation: ${keyframes`
47 | from {
48 | transform: rotate(0deg);
49 | }
50 |
51 | to {
52 | transform: rotate(${endingRotation});
53 | }
54 | `} 2s linear infinite;
55 | `
56 |
57 | const tree = renderer.create(hello world ).toJSON()
58 |
59 | expect(tree).toMatchSnapshot()
60 |
61 | expect(sheet).toMatchSnapshot()
62 | })
63 | })
64 |
--------------------------------------------------------------------------------
/packages/emotion/types/index.d.ts:
--------------------------------------------------------------------------------
1 | // TypeScript Version: 2.2
2 | export type Interpolation = string | number | boolean | null | undefined | _Interpolation1 | _Interpolation2 | (() => Interpolation);
3 |
4 | // HACK: See https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540
5 | export interface _Interpolation1 extends Record {}
6 | export interface _Interpolation2 extends Array {}
7 |
8 | export type CreateStyles = ((...values: Interpolation[]) => TRet)
9 | & ((strings: TemplateStringsArray, ...vars: Interpolation[]) => TRet);
10 |
11 | // TODO: Make this more precise than just Function
12 | // tslint:disable-next-line:ban-types
13 | export type StylisUse = (plugin: Function | Function[] | null) => StylisUse;
14 |
15 | export interface StyleSheet {
16 | inject(): void;
17 | speedy(bool: boolean): void;
18 | insert(rule: string, sourceMap: string): void;
19 | flush(): void;
20 | }
21 |
22 | export const sheet: StyleSheet;
23 |
24 | export const useStylisPlugin: StylisUse;
25 |
26 | export const inserted: Record;
27 |
28 | export const registered: Record;
29 |
30 | export function flush(): void;
31 |
32 | export const css: CreateStyles;
33 |
34 | export const injectGlobal: CreateStyles;
35 |
36 | export const keyframes: CreateStyles;
37 |
38 | export const fontFace: CreateStyles;
39 |
40 | export function getRegisteredStyles(registeredStyles: string[], classNames: string): string;
41 |
42 | export function cx(...interpolations: Interpolation[]): string;
43 |
44 | export function hydrate(ids: string[]): void;
45 |
46 | declare module 'react' {
47 | interface HTMLAttributes {
48 | css?: Interpolation;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/emotion/test/inject-global.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { injectGlobal, sheet, flush, css } from 'emotion'
3 |
4 | describe('injectGlobal', () => {
5 | afterEach(() => {
6 | flush()
7 | })
8 | test('basic', () => {
9 | injectGlobal`
10 | html {
11 | background: pink;
12 | }
13 | html.active {
14 | background: red;
15 | }
16 | `
17 | expect(sheet).toMatchSnapshot()
18 | })
19 | test('interpolated value', () => {
20 | const color = 'yellow'
21 | injectGlobal`
22 | body {
23 | color: ${color};
24 | margin: 0;
25 | padding: 0;
26 | }
27 | `
28 | expect(sheet).toMatchSnapshot()
29 | })
30 | test('nested interpolated media query', () => {
31 | injectGlobal`
32 | body {
33 | ${'@media (max-width: 600px)'} {
34 | display: flex;
35 | }
36 | }
37 | `
38 | expect(sheet).toMatchSnapshot()
39 | })
40 | test('random interpolation', () => {
41 | const cls = css`
42 | display: flex;
43 | `
44 | injectGlobal`
45 | body {
46 | ${cls};
47 | }
48 | `
49 | expect(sheet).toMatchSnapshot()
50 | })
51 | test('with @font-face', () => {
52 | injectGlobal`
53 | @font-face {
54 | font-family: 'Patrick Hand SC';
55 | font-style: normal;
56 | font-weight: 400;
57 | src: local('Patrick Hand SC'), local('PatrickHandSC-Regular'),
58 | url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2)
59 | format('woff2');
60 | unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf,
61 | U+2c60-2c7f, U+A720-A7FF;
62 | }
63 | `
64 | expect(sheet).toMatchSnapshot()
65 | })
66 | })
67 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/test/macro/keyframes.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React from 'react'
3 | import renderer from 'react-test-renderer'
4 | import { keyframes, sheet, flush } from 'emotion/macro'
5 | import styled from 'react-emotion/macro'
6 |
7 | describe('keyframes - macro', () => {
8 | afterEach(() => {
9 | flush()
10 | })
11 | test('renders', () => {
12 | const bounce = keyframes`
13 | from, 20%, 53%, 80%, to {
14 | animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
15 | transform: translate3d(0,0,0);
16 | }
17 |
18 | 40%, 43% {
19 | animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
20 | transform: translate3d(0, -30px, 0);
21 | }
22 |
23 | 70% {
24 | animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
25 | transform: translate3d(0, -15px, 0);
26 | }
27 |
28 | 90% {
29 | transform: translate3d(0,-4px,0);
30 | }
31 | `
32 |
33 | const H1 = styled.h1`
34 | animation: ${bounce} 2s linear infinite;
35 | `
36 |
37 | const tree = renderer.create(hello world ).toJSON()
38 |
39 | expect(tree).toMatchSnapshot()
40 | expect(sheet).toMatchSnapshot()
41 | })
42 | test('keyframes with interpolation', () => {
43 | const endingRotation = '360deg'
44 |
45 | const H1 = styled.h1`
46 | animation: ${keyframes`
47 | from {
48 | transform: rotate(0deg);
49 | }
50 |
51 | to {
52 | transform: rotate(${endingRotation});
53 | }
54 | `} 2s linear infinite;
55 | `
56 |
57 | const tree = renderer.create(hello world ).toJSON()
58 |
59 | expect(tree).toMatchSnapshot()
60 |
61 | expect(sheet).toMatchSnapshot()
62 | })
63 | })
64 |
--------------------------------------------------------------------------------
/packages/emotion/test/extract/extract.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import renderer from 'react-test-renderer'
3 | import { basename } from 'path'
4 | import { injectGlobal, css } from 'emotion'
5 | import styled from 'react-emotion'
6 |
7 | describe('styled', () => {
8 | test('no dynamic', () => {
9 | const H1 = styled.h1`
10 | font-size: 12px;
11 | `
12 |
13 | const tree = renderer.create(hello world ).toJSON()
14 |
15 | expect(tree).toMatchSnapshot()
16 | })
17 |
18 | test('basic render nested', () => {
19 | const H1 = styled.h1`
20 | font-size: 20px;
21 | & span {
22 | color: blue;
23 |
24 | &:hover {
25 | color: green;
26 |
27 | &:after {
28 | content: 'after';
29 | }
30 | }
31 | }
32 | `
33 | const tree = renderer.create(hello world ).toJSON()
34 |
35 | expect(tree).toMatchSnapshot()
36 | })
37 |
38 | test('className prop on styled', () => {
39 | const H1 = styled.h1`
40 | font-size: 20px;
41 | `
42 | const tree = renderer
43 | .create(hello world )
44 | .toJSON()
45 |
46 | expect(tree).toMatchSnapshot()
47 | })
48 |
49 | test('injectGlobal', () => {
50 | injectGlobal`
51 | html {
52 | background: pink;
53 | }
54 | `
55 | })
56 | test('css', () => {
57 | expect(css`
58 | font-family: sans-serif;
59 | color: yellow;
60 | background-color: purple;
61 | `)
62 | })
63 | test('writes the correct css', () => {
64 | const filenameArr = basename(__filename).split('.')
65 | filenameArr.pop()
66 | filenameArr.push('emotion', 'css')
67 | const cssFilename = filenameArr.join('.')
68 | expect(global.mockedCssImports[cssFilename]).toMatchSnapshot()
69 | })
70 | })
71 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/test/keyframes.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { createInlineTests, createExtractTests } from './util'
3 |
4 | const cases = {
5 | 'keyframes basic': {
6 | code: `
7 | const rotate360 = keyframes\`
8 | from {
9 | transform: rotate(0deg);
10 | }
11 | to {
12 | transform: rotate(360deg);
13 | }
14 | \`;`
15 | },
16 |
17 | 'keyframes with interpolation': {
18 | code: `
19 | const rotate360 = keyframes\`
20 | from {
21 | transform: rotate(0deg);
22 | }
23 | to {
24 | transform: rotate(\${endingRotation});
25 | }
26 | \`;`,
27 | extract: false
28 | },
29 |
30 | 'static change import': {
31 | code: `
32 | const rotate360 = frames\`
33 | from {
34 | transform: rotate(0deg);
35 | }
36 | to {
37 | transform: rotate(360deg);
38 | }
39 | \`;
40 | const rotate3601 = keyframes\`
41 | from {
42 | transform: rotate(0deg);
43 | }
44 | to {
45 | transform: rotate(360deg);
46 | }
47 | \`;`,
48 |
49 | opts: { importedNames: { keyframes: 'frames' } }
50 | },
51 |
52 | 'dynamic change import': {
53 | code: `
54 | import { keyframes as frames } from 'emotion'
55 | const rotate360 = frames\`
56 | from {
57 | transform: rotate(0deg);
58 | }
59 | to {
60 | transform: rotate(360deg);
61 | }
62 | \`;
63 | const rotate3601 = keyframes\`
64 | from {
65 | transform: rotate(0deg);
66 | }
67 | to {
68 | transform: rotate(360deg);
69 | }
70 | \`;`
71 | }
72 | }
73 |
74 | createInlineTests('keyframes', cases)
75 |
76 | createExtractTests('keyframes extract', cases)
77 |
--------------------------------------------------------------------------------
/docs/css.md:
--------------------------------------------------------------------------------
1 | ## CSS
2 |
3 | `css` accepts styles as a template literal, object, or array of objects and returns a class name. It is the foundation of emotion.
4 |
5 | ```jsx harmony
6 | import { css } from 'emotion'
7 | import { hiDPI, lighten, modularScale } from 'polished'
8 |
9 | // Write your css as normal.
10 | const flex = css`
11 | display: flex;
12 | `
13 | const justifyCenter = css`
14 | ${flex};
15 | justifyContent: center;
16 | `
17 |
18 | // As an object
19 | const cssA = {
20 | // Easily use [polished](https://polished.js.org/)
21 | color: lighten(0.2, '#000'),
22 | "font-size": modularScale(1),
23 | [hiDPI(1.5)]: {
24 | "font-size": modularScale(1.25)
25 | }
26 | }
27 |
28 | const H1 = styled('h1')`
29 | ${cssA};
30 | font-size: ${modularScale(4)};
31 | `
32 |
33 | const H2 = styled(H1)`font-size:32px;`
34 |
35 |
36 | Centered Content
37 |
38 |
39 | ```
40 |
41 | ## CSS Prop
42 | ###### [requires babel plugin](babel.md)
43 | A shortcut for calling the css function and appending the result to the className prop. Done at compile time.
44 | _Note: CSS props are not compatible with babel's `"transform-react-inline-elements"` plugin. If you include it in your `.babelrc` no transformation will take place and your styles will silently fail._
45 |
46 | ```jsx
47 | function SomeComponent (props) {
48 | // Create styles as if you're calling css and the class will be applied to the component
49 | return (
61 | This will be blue until hovered.
62 |
63 | This font size will be 20px
64 |
65 |
)
66 | }
67 |
68 | ```
69 |
--------------------------------------------------------------------------------
/packages/emotion/test/prod-mode/prod-mode.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React from 'react'
3 | import renderer from 'react-test-renderer'
4 | import { css, sheet, flush } from 'emotion'
5 |
6 | describe('production mode', () => {
7 | afterEach(() => flush())
8 | test('production mode basic', () => {
9 | const cls1 = css`
10 | color: yellow;
11 | & .some-class {
12 | display: flex;
13 | & .some-other-class {
14 | background-color: hotpink;
15 | }
16 | @media (max-width: 600px) {
17 | background-color: pink;
18 | }
19 | }
20 | `
21 | const tree = renderer
22 | .create(
23 |
28 | )
29 | .toJSON()
30 | expect(tree).toMatchSnapshot()
31 | })
32 |
33 | test('production mode nested styles', () => {
34 | const mq = [
35 | '@media(min-width: 420px)',
36 | '@media(min-width: 640px)',
37 | '@media(min-width: 960px)'
38 | ]
39 |
40 | css({
41 | color: 'blue',
42 | '&:hover': {
43 | '& .name': {
44 | color: 'amethyst',
45 | '&:focus': {
46 | color: 'burlywood',
47 | [mq[0]]: {
48 | color: 'rebeccapurple'
49 | }
50 | }
51 | },
52 | color: 'green'
53 | }
54 | })
55 | expect(sheet).toMatchSnapshot()
56 | })
57 |
58 | test('production mode nested media queries', () => {
59 | css`
60 | @media (max-width: 600px) {
61 | h1 {
62 | font-size: 1.4rem;
63 | }
64 | }
65 |
66 | @media (max-width: 400px), (max-height: 420px) {
67 | h1 {
68 | font-size: 1.1rem;
69 | }
70 | }
71 | `
72 |
73 | expect(sheet).toMatchSnapshot()
74 | })
75 | })
76 |
--------------------------------------------------------------------------------
/packages/benchmarks/run-headless.js:
--------------------------------------------------------------------------------
1 | const puppeteer = require('puppeteer')
2 | const Table = require('cli-table')
3 | const path = require('path')
4 |
5 | const sendResult = require('./send-results')
6 |
7 | const {
8 | TRAVIS_BRANCH = 'test',
9 | TRAVIS_COMMIT = 'test',
10 | TRAVIS_COMMIT_MESSAGE = 'test',
11 | TRAVIS_PULL_REQUEST = 'test'
12 | } = process.env
13 |
14 | // need to add a timeout if it never completes
15 | // needs error handling
16 |
17 | async function run() {
18 | const browser = await puppeteer.launch()
19 | const page = await browser.newPage()
20 | await page.goto(`file://${path.resolve(__dirname, './dist/index.html')}`)
21 |
22 | const results = []
23 | let done = false
24 | page.on('console', async result => {
25 | if (result && result.name) {
26 | console.log(`${result.name}: Mean ${result.mean}ms`)
27 | results.push(result)
28 | if (done) {
29 | try {
30 | await sendResult({
31 | branch: TRAVIS_BRANCH,
32 | commit: TRAVIS_COMMIT,
33 | commitMessage: TRAVIS_COMMIT_MESSAGE,
34 | pr: TRAVIS_PULL_REQUEST,
35 | results: results.map(r => ({
36 | name: r.name,
37 | duration: parseInt(r.mean, 10),
38 | type: r.name.toLowerCase().includes('deep')
39 | ? 'DEEP'
40 | : r.name.toLowerCase().includes('wide') ? 'WIDE' : 'TRIANGLE'
41 | }))
42 | })
43 | } catch (e) {
44 | console.log('graphql failed')
45 | console.log(e.message)
46 | }
47 |
48 | const table = new Table()
49 | table.push(['Benchmark', 'Mean (ms)'])
50 | table.push(...results.map(res => [res.name, res.mean]))
51 | console.log(table.toString())
52 | await browser.close()
53 | }
54 | }
55 | if (result === 'done') {
56 | done = true
57 | }
58 | })
59 | }
60 |
61 | run()
62 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/src/macro-styled.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import {
3 | buildStyledCallExpression,
4 | buildStyledObjectCallExpression
5 | } from './index'
6 | import { buildMacroRuntimeNode, omit } from './babel-utils'
7 | import emotionMacro from './macro'
8 | import { createMacro } from 'babel-macros'
9 |
10 | module.exports = createMacro(macro)
11 |
12 | function macro(options) {
13 | const { references, state, babel: { types: t } } = options
14 | let referencesWithoutDefault = references
15 | if (references.default) {
16 | referencesWithoutDefault = omit(references, key => key !== 'default')
17 | references.default.reverse().forEach(styledReference => {
18 | const path = styledReference.parentPath.parentPath
19 | const runtimeNode = buildMacroRuntimeNode(
20 | styledReference,
21 | state,
22 | 'default',
23 | t
24 | )
25 | if (t.isTemplateLiteral(path.node.quasi)) {
26 | if (t.isMemberExpression(path.node.tag)) {
27 | path.replaceWith(
28 | buildStyledCallExpression(
29 | runtimeNode,
30 | t.stringLiteral(path.node.tag.property.name),
31 | path,
32 | state,
33 | t
34 | )
35 | )
36 | } else if (t.isCallExpression(path.node.tag)) {
37 | path.replaceWith(
38 | buildStyledCallExpression(
39 | runtimeNode,
40 | path.node.tag.arguments[0],
41 | path,
42 | state,
43 | t
44 | )
45 | )
46 | }
47 | } else if (
48 | t.isCallExpression(path) &&
49 | (t.isCallExpression(path.node.callee) ||
50 | t.isIdentifier(path.node.callee.object))
51 | ) {
52 | path.replaceWith(
53 | buildStyledObjectCallExpression(path, state, runtimeNode, t)
54 | )
55 | }
56 | })
57 | }
58 | emotionMacro({ ...options, references: referencesWithoutDefault })
59 | }
60 |
--------------------------------------------------------------------------------
/packages/create-emotion-server/src/stream.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { Emotion } from 'create-emotion'
3 | import through from 'through'
4 | import tokenize from 'html-tokenize'
5 | import pipe from 'multipipe'
6 |
7 | const createRenderStylesToNodeStream = (
8 | emotion: Emotion,
9 | nonceString: string
10 | ) => () => {
11 | let insed = {}
12 | const tokenStream = tokenize()
13 |
14 | const inlineStream = through(
15 | function write(thing) {
16 | let [type, data] = thing
17 | if (type === 'open') {
18 | let css = ''
19 | let ids = {}
20 |
21 | let match
22 | let fragment = data.toString()
23 | let regex = new RegExp(`${emotion.caches.key}-([a-zA-Z0-9-]+)`, 'gm')
24 | while ((match = regex.exec(fragment)) !== null) {
25 | if (match !== null && insed[match[1]] === undefined) {
26 | ids[match[1]] = true
27 | }
28 | }
29 | Object.keys(emotion.caches.inserted).forEach(id => {
30 | if (
31 | emotion.caches.inserted[id] !== true &&
32 | insed[id] === undefined &&
33 | (ids[id] === true ||
34 | (emotion.caches.registered[`${emotion.caches.key}-${id}`] ===
35 | undefined &&
36 | (ids[id] = true)))
37 | ) {
38 | insed[id] = true
39 | // $FlowFixMe flow thinks emotion.caches.inserted[id] can be true even though it's checked earlier
40 | css += emotion.caches.inserted[id]
41 | }
42 | })
43 |
44 | if (css !== '') {
45 | this.queue(
46 | ``
49 | )
50 | }
51 | }
52 | this.queue(data)
53 | },
54 | function end() {
55 | this.queue(null)
56 | }
57 | )
58 |
59 | return pipe(tokenStream, inlineStream)
60 | }
61 |
62 | export default createRenderStylesToNodeStream
63 |
--------------------------------------------------------------------------------
/docs/babel.md:
--------------------------------------------------------------------------------
1 | ## Usage with Babel
2 |
3 | The Babel Plugin is highly recommended, but not required in version 8 and above. All of the options that can be provided to `babel-plugin-emotion` are documented in [`babel-plugin-emotion`'s README](https://github.com/emotion-js/emotion/tree/master/packages/babel-plugin-emotion)
4 |
5 | See the [installation instructions](install.md).
6 |
7 | ### Features which are enabled with the babel plugin
8 |
9 | ### styled.element Syntax
10 | `styled('div')` will work without the plugin
11 |
12 | ### Minification
13 | Any leading/trailing space between properties in your `css` and `styled` blocks is removed. This can reduce the size of your final bundle.
14 |
15 | ### Dead Code Elimination
16 | Uglifyjs will use the injected `/*#__PURE__*/` flag comments to mark your `css` and `styled` blocks as candidates for dead code elimination.
17 |
18 | ### Static Extraction
19 | Generated CSS that is eligible for extraction can be moved to an external css file.
20 |
21 | ### Source Maps
22 | When enabled, navigate directly to the style declaration in your javascript file.
23 |
24 | ### css as Prop
25 | Convenient helper for calling `css` and appending the generated className during compile time.
26 |
27 | ## Babel Macros
28 |
29 | Instead of using emotion's babel plugin, you can use emotion with [`babel-macros`](https://github.com/kentcdodds/babel-macros). Add `babel-macros` to your babel config and import whatever you want from emotion but add `/macro` to the end. Currently every API except for the css prop is supported by the macro.
30 |
31 | ```jsx
32 | import styled from 'react-emotion/macro'
33 | import { css, keyframes, fontFace, injectGlobal, flush, hydrate } from 'emotion/macro'
34 | ```
35 |
36 | [create-react-app issue discussing macros](https://github.com/facebookincubator/create-react-app/issues/2730).
37 |
38 | ### Components as selectors
39 | The ability to refer to another component to apply override styles depending on nesting context. Learn more in the [react-emotion docs](./styled.md#targeting-another-emotion-component).
40 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import resolve from 'rollup-plugin-node-resolve'
2 | import uglify from 'rollup-plugin-uglify'
3 | import replace from 'rollup-plugin-replace'
4 | import babel from 'rollup-plugin-babel'
5 | import alias from 'rollup-plugin-alias'
6 | import cjs from 'rollup-plugin-commonjs'
7 | import path from 'path'
8 | import { rollup as lernaAliases } from 'lerna-alias'
9 |
10 | const pkg = require(path.resolve(process.cwd(), './package.json'))
11 |
12 | const basePlugins = [
13 | cjs({ exclude: [path.join(__dirname, 'packages', '*/src/**/*')] }),
14 | resolve(),
15 | babel({
16 | presets: [
17 | [
18 | '@babel/env',
19 | {
20 | loose: true,
21 | modules: false,
22 | exclude: ['transform-typeof-symbol']
23 | }
24 | ],
25 | '@babel/stage-0',
26 | '@babel/react',
27 | '@babel/flow'
28 | ],
29 | plugins: ['codegen', 'closure-elimination'],
30 | babelrc: false
31 | })
32 | ]
33 |
34 | const baseConfig = {
35 | input: './src/index.js',
36 | exports: 'named',
37 | sourcemap: true
38 | }
39 |
40 | const baseExternal = ['react', 'prop-types', 'preact']
41 |
42 | const mainConfig = Object.assign({}, baseConfig, {
43 | external: baseExternal.concat([
44 | 'emotion',
45 | 'emotion-utils',
46 | 'hoist-non-react-statics',
47 | 'stylis-rule-sheet'
48 | ]),
49 | plugins: basePlugins,
50 | output: [
51 | { file: pkg.main, format: 'cjs' },
52 | { file: pkg.module, format: 'es' }
53 | ]
54 | })
55 |
56 | const umdConfig = Object.assign({}, baseConfig, {
57 | plugins: [alias(lernaAliases())].concat(basePlugins).concat(
58 | replace({
59 | 'process.env.NODE_ENV': JSON.stringify('production')
60 | }),
61 | uglify()
62 | ),
63 | output: [
64 | {
65 | file: './dist/emotion.umd.min.js',
66 | format: 'umd',
67 | name: pkg.name
68 | }
69 | ],
70 | globals: { react: 'React', 'prop-types': 'PropTypes', preact: 'preact' },
71 | external: baseExternal
72 | })
73 |
74 | export default [mainConfig, umdConfig]
75 |
--------------------------------------------------------------------------------
/packages/create-emotion/src/utils.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { memoize, unitless } from 'emotion-utils'
3 |
4 | const hyphenateRegex = /[A-Z]|^ms/g
5 |
6 | export const processStyleName: (
7 | styleName: string
8 | ) => string = memoize((styleName: string) =>
9 | styleName.replace(hyphenateRegex, '-$&').toLowerCase()
10 | )
11 |
12 | export const processStyleValue = (key: string, value: string): string => {
13 | if (value == null || typeof value === 'boolean') {
14 | return ''
15 | }
16 |
17 | if (
18 | unitless[key] !== 1 &&
19 | key.charCodeAt(1) !== 45 && // custom properties
20 | !isNaN(value) &&
21 | value !== 0
22 | ) {
23 | return value + 'px'
24 | }
25 | return value
26 | }
27 |
28 | export type ClassNameArg =
29 | | string
30 | | boolean
31 | | (() => ClassNameArg)
32 | | { [key: string]: boolean }
33 | | Array
34 |
35 | export const classnames = (args: Array): string => {
36 | let len = args.length
37 | let i = 0
38 | let cls = ''
39 | for (; i < len; i++) {
40 | let arg = args[i]
41 |
42 | if (arg == null) continue
43 | let next = (cls && cls + ' ') || cls
44 |
45 | switch (typeof arg) {
46 | case 'boolean':
47 | break
48 | case 'function':
49 | cls = next + classnames([arg()])
50 | break
51 | case 'object': {
52 | if (Array.isArray(arg)) {
53 | cls = next + classnames(arg)
54 | } else {
55 | for (const k in arg) {
56 | if (arg[k]) {
57 | cls && (cls += ' ')
58 | cls += k
59 | }
60 | }
61 | }
62 | break
63 | }
64 | default: {
65 | cls = next + arg
66 | }
67 | }
68 | }
69 | return cls
70 | }
71 |
72 | export const isBrowser = typeof window !== 'undefined'
73 |
74 | export type PrefixOption =
75 | | boolean
76 | | ((key: string, value: string, context: 1 | 2 | 3) => boolean)
77 |
78 | export type StylisOptions = {
79 | keyframe?: boolean,
80 | compress?: boolean,
81 | global?: boolean,
82 | cascade?: boolean,
83 | semicolon?: boolean,
84 | preserve?: boolean,
85 | prefix?: PrefixOption
86 | }
87 |
--------------------------------------------------------------------------------
/packages/emotion-theming/test/test-helpers.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React, { Component, PureComponent } from 'react'
3 |
4 | import { channel } from 'emotion-theming'
5 |
6 | export const getContextTypes = C => C.contextTypes
7 | export const getChannel = C => Object.keys(getContextTypes(C))[0]
8 |
9 | export const mountOptions = broadcast => ({
10 | childContextTypes: {
11 | [channel]: PropTypes.object.isRequired
12 | },
13 | context: {
14 | [channel]: {
15 | subscribe: broadcast.subscribe,
16 | unsubscribe: broadcast.unsubscribe
17 | }
18 | }
19 | })
20 |
21 | export function getInterceptor(initialState) {
22 | let state = initialState
23 | return newState => {
24 | if (newState) {
25 | state = newState
26 | }
27 | return state
28 | }
29 | }
30 |
31 | export const StatelessComp = props =>
32 |
33 | StatelessComp.displayName = 'StatelessComp'
34 |
35 | export class Pure extends PureComponent {
36 | static propTypes = {
37 | children: PropTypes.node.isRequired
38 | }
39 | render() {
40 | return {this.props.children}
41 | }
42 | }
43 |
44 | export class PropTrap extends Component {
45 | static propTypes = {
46 | intercept: PropTypes.func.isRequired,
47 | theme: PropTypes.object.isRequired
48 | }
49 | constructor(props) {
50 | super(props)
51 | this.props.intercept(props.theme)
52 | }
53 | componentWillReceiveProps(nextProps) {
54 | if (nextProps) {
55 | this.props.intercept(nextProps.theme)
56 | }
57 | }
58 | // eslint-disable-next-line
59 | render() {
60 | return
61 | }
62 | }
63 |
64 | export class ContextTrap extends Component {
65 | static propTypes = {
66 | intercept: PropTypes.func.isRequired
67 | }
68 | static contextTypes = {
69 | [channel]: PropTypes.object.isRequired
70 | }
71 | componentWillMount() {
72 | if (this.context[channel]) {
73 | this.unsubscribe = this.context[channel].subscribe(this.props.intercept)
74 | }
75 | }
76 | // eslint-disable-next-line
77 | render() {
78 | return
79 | }
80 | }
81 |
82 | export const Trap = {
83 | Prop: PropTrap,
84 | Context: ContextTrap
85 | }
86 |
--------------------------------------------------------------------------------
/docs/ssr.md:
--------------------------------------------------------------------------------
1 | ## Server-Side Rendering
2 |
3 | Server-Side Rendering in emotion currently does not work with `extractStatic`. It's similar to [glamor's api](https://github.com/threepointone/glamor/blob/master/docs/server.md). For an example of emotion and next.js checkout the [with-emotion example in the next.js repo](https://github.com/zeit/next.js/tree/master/examples/with-emotion).
4 |
5 | ### renderStylesToString
6 | This returns a string of html that inlines the critical css required right before it's used.
7 |
8 | ```jsx
9 | import { renderToString } from 'react-dom/server'
10 | import { renderStylesToString } from 'emotion-server'
11 | import App from './App'
12 |
13 | const html = renderStylesToString(renderToString( ))
14 | ```
15 |
16 |
17 | ### renderStylesToNodeStream
18 | This returns [Node Stream Writable](https://nodejs.org/api/stream.html#stream_class_stream_writable) that can be used to insert critical css right before it's required. This can be used with [React's streaming API](https://reactjs.org/docs/react-dom-server.html#rendertonodestream).
19 |
20 | ```jsx
21 | import { renderToNodeStream } from 'react-dom/server'
22 | import { renderStylesToNodeStream } from 'emotion-server'
23 | import App from './App'
24 |
25 | const stream = renderToNodeStream( ).pipe(renderStylesToNodeStream())
26 | ```
27 |
28 |
29 | ### extractCritical
30 | This returns an object with the properties `html`, `ids` and `css`. It removes unused rules that were created with emotion(it still includes rules that were inserted with `injectGlobal`).
31 |
32 | ```jsx
33 | import { renderToString } from 'react-dom/server'
34 | import { extractCritical } from 'emotion-server'
35 | import App from './App'
36 |
37 |
38 | const { html, ids, css } = extractCritical(renderToString( ))
39 |
40 | ```
41 |
42 | #### hydrate
43 | `hydrate` should be called on the client with the `ids` that `extractCritical` returns. If you don't call it then emotion will reinsert all the rules. `hydrate` is **only** required for `extractCritical`, **not** for `renderStylesToString` or `renderStylesToNodeStream`, hydration occurs automatically with `renderStylesToString` and `renderStylesToNodeStream`.
44 |
45 | ```jsx
46 | import { hydrate } from 'emotion'
47 |
48 | hydrate(ids)
49 |
50 | ```
51 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/src/ast-object.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { Types, Expression, StringLiteral } from 'babel-flow-types'
3 | const interleave = (
4 | strings: Array,
5 | interpolations: Array
6 | ) =>
7 | interpolations.reduce(
8 | (array, interp, i) => array.concat([interp], strings[i + 1]),
9 | [strings[0]]
10 | )
11 |
12 | export default class ASTObject {
13 | expressions: Array
14 | t: any
15 | src: string
16 |
17 | constructor(src: string, expressions: Array, t: Types) {
18 | this.src = src
19 | this.expressions = expressions || []
20 | this.t = t
21 | }
22 | getDynamicMatches(str: string) {
23 | const re = /xxx(\d+)xxx/gm
24 | let match
25 | const matches = []
26 | while ((match = re.exec(str)) !== null) {
27 | matches.push({
28 | value: match[0],
29 | p1: parseInt(match[1], 10),
30 | index: match.index
31 | })
32 | }
33 |
34 | return matches
35 | }
36 |
37 | replacePlaceholdersWithExpressions(matches: any[], str: string) {
38 | const { expressions, t } = this
39 | if (expressions.length === 0) {
40 | if (str === '') {
41 | return []
42 | }
43 | return [t.stringLiteral(str)]
44 | }
45 | const strings = []
46 | const finalExpressions = []
47 | let cursor = 0
48 |
49 | matches.forEach(({ value, p1, index }, i) => {
50 | const preMatch = str.substring(cursor, index)
51 | cursor = cursor + preMatch.length + value.length
52 | if (preMatch) {
53 | strings.push(t.stringLiteral(preMatch))
54 | } else if (i === 0) {
55 | strings.push(t.stringLiteral(''))
56 | }
57 |
58 | finalExpressions.push(expressions[p1])
59 | if (i === matches.length - 1) {
60 | strings.push(t.stringLiteral(str.substring(index + value.length)))
61 | }
62 | })
63 |
64 | return interleave(strings, finalExpressions).filter(
65 | // $FlowFixMe
66 | (node: StringLiteral) => {
67 | return node.value !== ''
68 | }
69 | )
70 | }
71 | toExpressions() {
72 | return this.replacePlaceholdersWithExpressions(
73 | this.getDynamicMatches(this.src),
74 | this.src
75 | )
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/packages/benchmarks/tests/renderSierpinskiTriangle.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import SierpinskiTriangle from '../src/components/SierpinskiTriangle'
4 | import { fmt, standardDeviation, mean, median } from '../benchmark'
5 |
6 | const node = document.querySelector('.root')
7 |
8 | let runs = 20
9 |
10 | class Speedometer extends React.Component {
11 | state = { renderCount: -1 }
12 |
13 | async componentDidMount() {
14 | const durations = []
15 | while (runs--) {
16 | const then = window.performance.now()
17 | await new Promise(resolve => {
18 | this.raf = window.requestAnimationFrame(() => {
19 | this.setState({ renderCount: this.state.renderCount + 1 }, () => {
20 | const now = window.performance.now()
21 | durations.push(now - then)
22 | resolve()
23 | })
24 | })
25 | })
26 | }
27 | runs = 20
28 | const stdDev = standardDeviation(durations)
29 | const formattedMean = fmt(mean(durations))
30 | const formattedMedian = fmt(median(durations))
31 | const formattedStdDev = fmt(stdDev)
32 |
33 | console.log({
34 | name: this.props.name,
35 | description: this.props.description,
36 | mean: formattedMean,
37 | median: formattedMedian,
38 | stdDev: formattedStdDev,
39 | durations
40 | })
41 | this.props.onComplete()
42 | }
43 | componentWillUnmount() {
44 | window.cancelAnimationFrame(this.raf)
45 | }
46 | render() {
47 | return (
48 |
49 |
56 |
57 | )
58 | }
59 | }
60 |
61 | const renderSierpinskiTriangle = (name, { Dot, Wrapper }) => () => {
62 | return new Promise(resolve => {
63 | ReactDOM.render(
64 | {
68 | ReactDOM.unmountComponentAtNode(node)
69 | resolve()
70 | }}
71 | name={`Triangle [${name}]`}
72 | description="push dynamic styles"
73 | />,
74 | node
75 | )
76 | })
77 | }
78 |
79 | export default renderSierpinskiTriangle
80 |
--------------------------------------------------------------------------------
/packages/create-emotion-server/src/inline.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { Emotion } from 'create-emotion'
3 |
4 | function toTag(
5 | emotion: Emotion,
6 | ids: Array,
7 | thing: { keys: Array },
8 | nonceString: string
9 | ) {
10 | let idhash = ids.reduce((o, x) => {
11 | o[x] = true
12 | return o
13 | }, {})
14 | let styles = ''
15 | let idHydration = ''
16 | thing.keys = thing.keys.filter(id => {
17 | if (idhash[id] !== undefined && emotion.caches.inserted[id] !== true) {
18 | styles += emotion.caches.inserted[id]
19 | idHydration += ` ${id}`
20 | }
21 | return true
22 | })
23 | return ``
26 | }
27 |
28 | const createRenderStylesToString = (emotion: Emotion, nonceString: string) => (
29 | html: string
30 | ): string => {
31 | let regex = new RegExp(`<|${emotion.caches.key}-([a-zA-Z0-9-]+)`, 'gm')
32 |
33 | let match
34 | let lastBackIndex = 0
35 | let idBuffer = []
36 | let result = ''
37 | let insed = {}
38 | let keys = Object.keys(emotion.caches.inserted)
39 | let globalStyles = ''
40 | let globalIds = ''
41 | keys = keys.filter(id => {
42 | if (
43 | emotion.caches.registered[`${emotion.caches.key}-${id}`] === undefined &&
44 | emotion.caches.inserted[id] !== true
45 | ) {
46 | globalStyles += emotion.caches.inserted[id]
47 | globalIds += ` ${id}`
48 | return false
49 | }
50 | return true
51 | })
52 | if (globalStyles !== '') {
53 | result += ``
56 | }
57 | const thing = { keys }
58 | while ((match = regex.exec(html)) !== null) {
59 | if (match[0] === '<') {
60 | idBuffer = idBuffer.filter(x => !insed[x])
61 | if (idBuffer.length > 0) {
62 | result += toTag(emotion, idBuffer, thing, nonceString)
63 | }
64 | result += html.substring(lastBackIndex, match.index)
65 | lastBackIndex = match.index
66 | idBuffer.forEach(x => {
67 | insed[x] = true
68 | })
69 | idBuffer = []
70 | } else {
71 | idBuffer.push(match[1])
72 | }
73 | }
74 | result += html.substring(lastBackIndex, html.length)
75 | return result
76 | }
77 |
78 | export default createRenderStylesToString
79 |
--------------------------------------------------------------------------------
/packages/site/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const HtmlWebpackPlugin = require('html-webpack-plugin')
3 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
4 | const UglifyPlugin = require('uglifyjs-webpack-plugin')
5 | const webpack = require('webpack')
6 |
7 | module.exports = env => {
8 | const PROD = env === 'production'
9 |
10 | var loaders = [
11 | {
12 | test: /\.jsx?$/,
13 | include: [
14 | /src/,
15 | /autoprefixer/,
16 | /chalk/,
17 | /ansi-styles/,
18 | /postcss-nested/,
19 | /caniuse-lite/
20 | ],
21 | loader: 'babel-loader'
22 | },
23 | {
24 | test: /\.css$/,
25 | use: PROD
26 | ? ExtractTextPlugin.extract({
27 | fallback: 'style-loader',
28 | use: {
29 | loader: 'css-loader',
30 | options: {
31 | sourceMap: true,
32 | modules: true
33 | }
34 | }
35 | })
36 | : ['style-loader', { loader: 'css-loader' }]
37 | },
38 | {
39 | test: /\.(jpg|png|svg)$/,
40 | loader: 'url-loader',
41 | options: {
42 | limit: 25000
43 | }
44 | },
45 | {
46 | loader: 'raw-loader',
47 | test: /\.(example|md)$/
48 | }
49 | ]
50 |
51 | return {
52 | devtool: 'source-map',
53 | entry: path.resolve('src', 'main.js'),
54 | output: {
55 | path: path.resolve('build'),
56 | filename: 'main.js',
57 | publicPath: '/'
58 | },
59 | resolve: {
60 | extensions: ['.ts', '.tsx', '.js', '.jsx']
61 | },
62 | plugins: [
63 | new HtmlWebpackPlugin({
64 | template: path.resolve('src', 'index.tpl.html'),
65 | favicon: './favicon.ico',
66 | filename: 'index.html',
67 | inject: false
68 | })
69 | ].concat(
70 | PROD
71 | ? [
72 | new ExtractTextPlugin('styles.css'),
73 | new webpack.DefinePlugin({
74 | 'process.env.NODE_ENV': JSON.stringify('production')
75 | }),
76 | new UglifyPlugin({
77 | extractComments: true,
78 | parallel: true,
79 | sourceMap: true
80 | })
81 | ]
82 | : []
83 | ),
84 | module: {
85 | loaders: loaders
86 | },
87 | node: {
88 | fs: 'empty'
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/packages/benchmarks/index.js:
--------------------------------------------------------------------------------
1 | import cssModules from './src/css-modules'
2 | import emotion from './src/emotion'
3 | import emotionCSS from './src/emotion-css'
4 | import emotionObj from './src/emotion-obj'
5 | import glamor from './src/glamor'
6 | import glamorous from './src/glamorous'
7 | import styledComponents from './src/styled-components'
8 |
9 | import renderDeepTree from './tests/renderDeepTree'
10 | import renderWideTree from './tests/renderWideTree'
11 | import renderSierpinskiTriangle from './tests/renderSierpinskiTriangle'
12 |
13 | const allTests = {
14 | emotion: [
15 | () => renderSierpinskiTriangle('emotion', emotion),
16 | () => renderDeepTree('emotion', emotion),
17 | () => renderWideTree('emotion', emotion)
18 | ],
19 | emotionCSS: [
20 | () => renderDeepTree('emotionCSS', emotionCSS),
21 | () => renderWideTree('emotionCSS', emotionCSS)
22 | ],
23 | emotionObj: [
24 | () => renderDeepTree('emotionObj', emotionObj),
25 | () => renderWideTree('emotionObj', emotionObj)
26 | ],
27 | glamor: [
28 | () => renderSierpinskiTriangle('glamor', glamor),
29 | () => renderDeepTree('glamor', glamor),
30 | () => renderWideTree('glamor', glamor)
31 | ],
32 | glamorous: [
33 | () => renderDeepTree('glamorous', glamorous),
34 | () => renderWideTree('glamorous', glamorous)
35 | ],
36 | 'styled-components': [
37 | () => renderDeepTree('styled-components', styledComponents),
38 | () => renderWideTree('styled-components', styledComponents)
39 | ],
40 | 'css-modules': [
41 | () => renderDeepTree('css-modules', cssModules),
42 | () => renderWideTree('css-modules', cssModules)
43 | ]
44 | }
45 |
46 | const tests = []
47 |
48 | if (window.location.hash) {
49 | window.location.hash
50 | .slice(1)
51 | .split(',')
52 | .forEach(test => {
53 | if (Array.isArray(allTests[test])) {
54 | tests.push(...allTests[test])
55 | } else {
56 | throw new Error(`Benchmark for ${test} not found`)
57 | }
58 | })
59 | } else {
60 | tests.push(...allTests.emotion)
61 | tests.push(...allTests.emotionObj)
62 | tests.push(...allTests.emotionCSS)
63 | tests.push(...allTests['css-modules'])
64 | tests.push(...allTests.glamorous)
65 | tests.push(...allTests.glamor)
66 | tests.push(...allTests['styled-components'])
67 | }
68 |
69 | tests.push(() => () => Promise.resolve(console.log('done')))
70 |
71 | tests.reduce((promise, test) => promise.then(test()), Promise.resolve())
72 |
--------------------------------------------------------------------------------
/packages/emotion/test/__snapshots__/selectivity.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`css complex nested media queries 1`] = `
4 | "@media (max-width:600px) {
5 | .css-acogag h1 {
6 | font-size: 1.4rem;
7 | }
8 | }
9 |
10 | @media (max-width:400px),(max-height:420px) {
11 | .css-acogag h1 {
12 | font-size: 1.1rem;
13 | }
14 | }"
15 | `;
16 |
17 | exports[`css complex nested styles 1`] = `
18 | ".css-s7aswl {
19 | color: blue;
20 | }
21 |
22 | .css-s7aswl:hover {
23 | color: green;
24 | }
25 |
26 | .css-s7aswl:hover .name {
27 | color: amethyst;
28 | }
29 |
30 | .css-s7aswl:hover .name:focus {
31 | color: burlywood;
32 | }
33 |
34 | @media (min-width:420px) {
35 | .css-s7aswl:hover .name:focus {
36 | color: rebeccapurple;
37 | }
38 | }"
39 | `;
40 |
41 | exports[`css handles media query merges 1`] = `
42 | ".css-yno01n {
43 | color: darkslateblue;
44 | color: red;
45 | color: purple;
46 | }
47 |
48 | @media (min-width:420px) {
49 | .css-yno01n {
50 | color: amethyst;
51 | }
52 | }
53 |
54 | @media (min-width:640px) {
55 | .css-yno01n {
56 | color: rebeccapurple;
57 | }
58 | }
59 |
60 | @media (min-width:960px) {
61 | .css-yno01n {
62 | color: burlywood;
63 | }
64 | }
65 |
66 | @media (min-width:640px) {
67 | .css-yno01n {
68 | color: blue;
69 | }
70 | }
71 |
72 | @media (min-width:640px) {
73 | .css-yno01n {
74 | color: aquamarine;
75 | }
76 | }"
77 | `;
78 |
79 | exports[`css media queries with multiple nested selectors 1`] = `
80 | ".css-17riori {
81 | color: blue;
82 | }
83 |
84 | @media (max-width:400px) {
85 | .css-17riori {
86 | color: green;
87 | }
88 |
89 | .css-17riori h1 {
90 | color: red;
91 | }
92 |
93 | .css-17riori span {
94 | color: red;
95 | }
96 | }"
97 | `;
98 |
99 | exports[`css media query with nested selector with nested selector on root 1`] = `
100 | ".css-1ssiu3j span {
101 | color: blue;
102 | }
103 |
104 | @media (max-width:400px) {
105 | .css-1ssiu3j {
106 | color: green;
107 | }
108 |
109 | .css-1ssiu3j span {
110 | color: red;
111 | }
112 | }"
113 | `;
114 |
115 | exports[`css media query with nested selector without declarations on root 1`] = `
116 | "@media (max-width:400px) {
117 | .css-18muius {
118 | color: green;
119 | }
120 |
121 | .css-18muius span {
122 | color: red;
123 | }
124 | }"
125 | `;
126 |
--------------------------------------------------------------------------------
/packages/emotion/test/auto-label/auto-label.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React from 'react'
3 | import renderer from 'react-test-renderer'
4 | import { css, flush, sheet } from 'emotion'
5 |
6 | describe('meta', () => {
7 | afterEach(() => flush())
8 | test('css generated class name should have the correct id', () => {
9 | const cls1 = css`
10 | color: blue;
11 | `
12 | const cls2 = css`
13 | & .${cls1} {
14 | color: red;
15 | }
16 | `
17 | const tree = renderer
18 | .create(
19 |
22 | )
23 | .toJSON()
24 | expect(tree).toMatchSnapshot()
25 | expect(sheet).toMatchSnapshot()
26 | })
27 |
28 | test('css prop correctly adds the id', () => {
29 | const SFC = () => {
30 | return Hello
31 | }
32 |
33 | class ClsComp extends React.Component<*> {
34 | render() {
35 | return Hello
36 | }
37 | }
38 |
39 | const hoc = W =>
40 | class extends React.Component<*> {
41 | render() {
42 | return (
43 |
44 |
45 |
46 | )
47 | }
48 | }
49 |
50 | const Wrapped = hoc(ClsComp)
51 |
52 | const sfcTree = renderer.create(Hello ).toJSON()
53 | expect(sfcTree).toMatchSnapshot()
54 |
55 | const clsCompTree = renderer.create(Hello ).toJSON()
56 | expect(clsCompTree).toMatchSnapshot()
57 |
58 | const hocTree = renderer.create(Hello ).toJSON()
59 | expect(hocTree).toMatchSnapshot()
60 |
61 | expect(sheet).toMatchSnapshot()
62 | })
63 | test('multiple classes with the same styles', () => {
64 | const cls1 = css`
65 | display: flex;
66 | `
67 | const cls2 = css`
68 | display: flex;
69 | `
70 | const tree = renderer
71 | .create(
72 |
76 | )
77 | .toJSON()
78 | expect(tree).toMatchSnapshot()
79 | })
80 | test('manually use label property', () => {
81 | const cls1 = css`
82 | color: blue;
83 | label: wow;
84 | `
85 | const tree = renderer.create(
).toJSON()
86 | expect(tree).toMatchSnapshot()
87 | expect(sheet).toMatchSnapshot()
88 | })
89 | })
90 |
--------------------------------------------------------------------------------
/packages/jest-emotion/README.md:
--------------------------------------------------------------------------------
1 | # jest-emotion
2 |
3 | > Jest testing utilities for emotions
4 |
5 | # Installation
6 |
7 | ```bash
8 | npm install --save-dev jest-emotion
9 | ```
10 |
11 | # Snapshot Serializer
12 |
13 | The easiest way to test React components with emotion is with the snapshot serializer. (the example below is with react-test-renderer but jest-emotion also works with enzyme)
14 |
15 | ```jsx
16 | import React from 'react'
17 | import renderer from 'react-test-renderer'
18 | import { createSerializer } from 'jest-emotion'
19 | import * as emotion from 'emotion'
20 | import styled from 'react-emotion'
21 |
22 | expect.addSnapshotSerializer(createSerializer(emotion))
23 |
24 | test('renders with correct styles', () => {
25 | const H1 = styled.h1`
26 | float: left;
27 | `
28 |
29 | const tree = renderer.create(hello world ).toJSON()
30 |
31 | expect(tree).toMatchSnapshot()
32 | })
33 | ```
34 |
35 | Refer to the [testing doc](https://github.com/emotion-js/emotion/blob/master/docs/testing.md) for more information about snapshot testing with emotion.
36 |
37 | ## Options
38 |
39 | # `classNameReplacer`
40 |
41 | jest-emotion's snapshot serializer replaces the hashes in class names with an index so that things like whitespace changes won't break snapshots. It optionally accepts a custom class name replacer, it defaults to the below.
42 |
43 | ```jsx
44 | function classNameReplacer(className, index) {
45 | return `emotion-${index}`
46 | }
47 | ```
48 | ```jsx
49 | import * as emotion from 'emotion'
50 | import { createSerializer } from 'jest-emotion'
51 |
52 | expect.addSnapshotSerializer(
53 | createSerializer(emotion, {
54 | classNameReplacer(className, index) {
55 | return `my-new-class-name-${index}`
56 | }
57 | })
58 | )
59 | ```
60 | # getStyles
61 |
62 | jest-emotion also allows you to get all the css that emotion has inserted. This is meant to be an escape hatch if you don't use React or you want to build your own utilities for testing with emotion.
63 |
64 | ```jsx
65 | import * as emotion from 'emotion'
66 | import { css } from 'emotion'
67 | import { getStyles } from 'jest-emotion'
68 |
69 | test('correct styles are inserted', () => {
70 | const cls = css`
71 | display: flex;
72 | `
73 |
74 | expect(getStyles(emotion)).toMatchSnapshot()
75 | })
76 | ```
77 |
78 | ## Thanks
79 |
80 | Thanks to [Kent C. Dodds](https://twitter.com/kentcdodds) who wrote [jest-glamor-react](https://github.com/kentcdodds/jest-glamor-react) which this library is largely based on.
--------------------------------------------------------------------------------
/packages/react-emotion/test/component-selectors/component-selector.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled, { flush, sheet } from 'react-emotion'
3 | import renderer from 'react-test-renderer'
4 |
5 | afterEach(() => flush())
6 |
7 | test('component as selector', () => {
8 | const fontSize = '20px'
9 | const H1 = styled.h1`
10 | font-size: ${fontSize};
11 | `
12 |
13 | const Thing = styled.div`
14 | display: flex;
15 | ${H1} {
16 | color: green;
17 | }
18 | `
19 |
20 | const tree = renderer
21 | .create(
22 |
23 | hello This will be green world
24 |
25 | )
26 | .toJSON()
27 |
28 | expect(tree).toMatchSnapshot()
29 | expect(sheet).toMatchSnapshot()
30 | })
31 |
32 | test('component as selector function interpolation', () => {
33 | const H1 = styled.h1`
34 | font-size: ${props => props.fontSize}px;
35 | `
36 |
37 | const Thing = styled.div`
38 | display: flex;
39 | ${H1} {
40 | color: green;
41 | }
42 | `
43 |
44 | const tree = renderer
45 | .create(
46 |
47 | hello This will be green world
48 |
49 | )
50 | .toJSON()
51 |
52 | expect(tree).toMatchSnapshot()
53 | expect(sheet).toMatchSnapshot()
54 | })
55 |
56 | test('component as selector (object syntax)', () => {
57 | const fontSize = '20px'
58 | const H1 = styled('h1')({ fontSize })
59 |
60 | const Thing = styled('div')({
61 | display: 'flex',
62 | [H1]: {
63 | color: 'green'
64 | }
65 | })
66 |
67 | const tree = renderer
68 | .create(
69 |
70 | hello This will be green world
71 |
72 | )
73 | .toJSON()
74 |
75 | expect(tree).toMatchSnapshot()
76 | expect(sheet).toMatchSnapshot()
77 | })
78 |
79 | test('component as selector function interpolation (object syntax)', () => {
80 | const H1 = styled('h1')({}, props => ({
81 | fontSize: `${props.fontSize}px`
82 | }))
83 |
84 | const Thing = styled('div')({
85 | display: 'flex',
86 | [H1]: {
87 | color: 'green'
88 | }
89 | })
90 |
91 | const tree = renderer
92 | .create(
93 |
94 | hello This will be green world
95 |
96 | )
97 | .toJSON()
98 |
99 | expect(tree).toMatchSnapshot()
100 | expect(sheet).toMatchSnapshot()
101 | })
102 |
--------------------------------------------------------------------------------
/packages/benchmarks/benchmark.js:
--------------------------------------------------------------------------------
1 | import * as marky from 'marky'
2 |
3 | export const fmt = time => Math.round(time * 100) / 100
4 |
5 | const measure = (name, fn) => {
6 | marky.mark(name)
7 | fn()
8 | const performanceMeasure = marky.stop(name)
9 | return performanceMeasure.duration
10 | }
11 |
12 | export const mean = values => {
13 | const sum = values.reduce((sum, value) => sum + value, 0)
14 | return sum / values.length
15 | }
16 |
17 | export const median = values => {
18 | if (!Array.isArray(values)) {
19 | return 0
20 | }
21 | if (values.length === 1) {
22 | return values[0]
23 | }
24 |
25 | const numbers = [...values].sort((a, b) => a - b)
26 | return (numbers[(numbers.length - 1) >> 1] + numbers[numbers.length >> 1]) / 2
27 | }
28 |
29 | export const standardDeviation = values => {
30 | const avg = mean(values)
31 |
32 | const squareDiffs = values.map(value => {
33 | const diff = value - avg
34 | return diff * diff
35 | })
36 |
37 | const meanSquareDiff = mean(squareDiffs)
38 | return Math.sqrt(meanSquareDiff)
39 | }
40 |
41 | const benchmark = ({ name, description, setup, teardown, task, runs }) => {
42 | return new Promise(resolve => {
43 | const durations = []
44 | let i = 0
45 |
46 | setup()
47 | const first = measure('first', task)
48 | teardown()
49 |
50 | const done = () => {
51 | const stdDev = standardDeviation(durations)
52 | const formattedFirst = fmt(first)
53 | const formattedMean = fmt(mean(durations))
54 | const formattedMedian = fmt(median(durations))
55 | const formattedStdDev = fmt(stdDev)
56 |
57 | console.log({
58 | name,
59 | description,
60 | first: formattedFirst,
61 | mean: formattedMean,
62 | median: formattedMedian,
63 | stdDev: formattedStdDev,
64 | durations
65 | })
66 | resolve()
67 | }
68 |
69 | const a = () => {
70 | setup()
71 | window.requestAnimationFrame(b)
72 | }
73 |
74 | const b = () => {
75 | const duration = measure('mean', task)
76 | durations.push(duration)
77 | window.requestAnimationFrame(c)
78 | }
79 |
80 | const c = () => {
81 | teardown()
82 | window.requestAnimationFrame(d)
83 | }
84 |
85 | const d = () => {
86 | i += 1
87 | if (i < runs) {
88 | window.requestAnimationFrame(a)
89 | } else {
90 | window.requestAnimationFrame(done)
91 | }
92 | }
93 |
94 | window.requestAnimationFrame(a)
95 | })
96 | }
97 |
98 | export default benchmark
99 |
--------------------------------------------------------------------------------
/packages/emotion/test/auto-label/__snapshots__/auto-label.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`meta css generated class name should have the correct id 1`] = `
4 | .emotion-0 {
5 | color: blue;
6 | }
7 |
8 | .emotion-1 .emotion-0 {
9 | color: red;
10 | }
11 |
12 |
19 | `;
20 |
21 | exports[`meta css generated class name should have the correct id 2`] = `
22 | ".css-1staf2a-cls1 {
23 | color: blue;
24 | }
25 |
26 | .css-14o1mnu-cls2 .css-1staf2a-cls1 {
27 | color: red;
28 | }"
29 | `;
30 |
31 | exports[`meta css prop correctly adds the id 1`] = `
32 | .emotion-0 {
33 | display: -webkit-box;
34 | display: -webkit-flex;
35 | display: -ms-flexbox;
36 | display: flex;
37 | }
38 |
39 |
42 | Hello
43 |
44 | `;
45 |
46 | exports[`meta css prop correctly adds the id 2`] = `
47 | .emotion-0 {
48 | display: grid;
49 | }
50 |
51 |
54 | Hello
55 |
56 | `;
57 |
58 | exports[`meta css prop correctly adds the id 3`] = `
59 | .emotion-0 {
60 | display: grid;
61 | }
62 |
63 | .emotion-1 {
64 | display: block;
65 | }
66 |
67 |
70 |
73 | Hello
74 |
75 |
76 | `;
77 |
78 | exports[`meta css prop correctly adds the id 4`] = `
79 | ".css-1bl9k6-SFC {
80 | display: -webkit-box;
81 | display: -webkit-flex;
82 | display: -ms-flexbox;
83 | display: flex;
84 | }
85 |
86 | .css-ptjj1k-ClsComp {
87 | display: grid;
88 | }
89 |
90 | .css-c6rdfv-hoc {
91 | display: block;
92 | }"
93 | `;
94 |
95 | exports[`meta manually use label property 1`] = `
96 | .emotion-0 {
97 | color: blue;
98 | }
99 |
100 |
103 | `;
104 |
105 | exports[`meta manually use label property 2`] = `
106 | ".css-1sy7nqn-wow-cls1 {
107 | color: blue;
108 | }"
109 | `;
110 |
111 | exports[`meta multiple classes with the same styles 1`] = `
112 | .emotion-0 {
113 | display: -webkit-box;
114 | display: -webkit-flex;
115 | display: -ms-flexbox;
116 | display: flex;
117 | }
118 |
119 | .emotion-1 {
120 | display: -webkit-box;
121 | display: -webkit-flex;
122 | display: -ms-flexbox;
123 | display: flex;
124 | }
125 |
126 |
134 | `;
135 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/test/fs.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { transform } from 'babel-core'
3 | import fs from 'fs'
4 | import touch from 'touch'
5 | import plugin from 'babel-plugin-emotion'
6 |
7 | jest.mock('fs').mock('touch')
8 | fs.statSync.mockReturnValue({ isFile: () => false })
9 |
10 | const basic = `
11 | css\`
12 | margin: 12px 48px;
13 | color: #ffffff;
14 | display: flex;
15 | flex: 1 0 auto;
16 | color: blue;
17 | name: class;
18 | \``
19 |
20 | let output
21 |
22 | const filenameArr = __filename.split('.')
23 | filenameArr.pop()
24 | filenameArr.push('emotion', 'css')
25 | const cssFilename = filenameArr.join('.')
26 |
27 | describe('babel plugin fs', () => {
28 | test('creates and writes to the css file when it does not exist', () => {
29 | fs.existsSync.mockReturnValueOnce(false)
30 | const { code } = transform(basic, {
31 | plugins: [[plugin, { extractStatic: true }]],
32 | filename: __filename,
33 | babelrc: false
34 | })
35 | expect(fs.existsSync).toBeCalledWith(cssFilename)
36 | expect(touch.sync).toBeCalledWith(cssFilename)
37 | expect(fs.writeFileSync).toHaveBeenCalled()
38 | expect(fs.writeFileSync.mock.calls[0][0]).toBe(cssFilename)
39 | expect(fs.writeFileSync.mock.calls[0][1]).toMatchSnapshot()
40 | output = fs.writeFileSync.mock.calls[0][1]
41 | expect(code).toMatchSnapshot()
42 | })
43 | test('writes to the css file when it does exist ', () => {
44 | fs.existsSync.mockReturnValueOnce(true)
45 | fs.readFileSync.mockReturnValueOnce('')
46 | const { code } = transform(basic, {
47 | plugins: [[plugin, { extractStatic: true }]],
48 | filename: __filename,
49 | babelrc: false
50 | })
51 | expect(fs.existsSync).toBeCalledWith(cssFilename)
52 | expect(touch.sync).toHaveBeenCalledTimes(1)
53 | expect(fs.writeFileSync).toHaveBeenCalledTimes(2)
54 | expect(fs.writeFileSync.mock.calls[1][0]).toBe(cssFilename)
55 | expect(fs.writeFileSync.mock.calls[1][1]).toMatchSnapshot()
56 | expect(code).toMatchSnapshot()
57 | })
58 | test('does not write to the css file when it is the same as is already written', () => {
59 | fs.existsSync.mockReturnValueOnce(true)
60 | fs.readFileSync.mockReturnValueOnce(output)
61 | const { code } = transform(basic, {
62 | plugins: [[plugin, { extractStatic: true }]],
63 | filename: __filename,
64 | babelrc: false
65 | })
66 | expect(fs.existsSync).toBeCalledWith(cssFilename)
67 | expect(touch.sync).toHaveBeenCalledTimes(1)
68 | expect(fs.writeFileSync).toHaveBeenCalledTimes(2)
69 | expect(code).toMatchSnapshot()
70 | })
71 | })
72 |
--------------------------------------------------------------------------------
/packages/emotion-server/test/inline.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * @jest-environment node
4 | */
5 | import React from 'react'
6 | import { renderToString } from 'react-dom/server'
7 | import {
8 | getComponents,
9 | getInjectedRules,
10 | createBigComponent,
11 | getCssFromChunks,
12 | setHtml
13 | } from './util'
14 | import { JSDOM } from 'jsdom'
15 |
16 | let emotion
17 | let emotionServer
18 | let reactEmotion
19 |
20 | describe('renderStylesToString', () => {
21 | beforeEach(() => {
22 | global.__SECRET_EMOTION__ = undefined
23 | jest.resetModules()
24 | emotion = require('emotion')
25 | emotionServer = require('emotion-server')
26 | reactEmotion = require('react-emotion')
27 | })
28 | test('renders styles with ids', () => {
29 | const { Page1, Page2 } = getComponents(emotion, reactEmotion)
30 | expect(
31 | emotionServer.renderStylesToString(renderToString( ))
32 | ).toMatchSnapshot()
33 | expect(
34 | emotionServer.renderStylesToString(renderToString( ))
35 | ).toMatchSnapshot()
36 | })
37 | test('renders large recursive component', () => {
38 | const BigComponent = createBigComponent(emotion)
39 | expect(
40 | emotionServer.renderStylesToString(
41 | renderToString( )
42 | )
43 | ).toMatchSnapshot()
44 | })
45 | })
46 | describe('hydration', () => {
47 | afterAll(() => {
48 | global.document = undefined
49 | global.window = undefined
50 | })
51 | beforeEach(() => {
52 | global.__SECRET_EMOTION__ = undefined
53 | jest.resetModules()
54 | emotion = require('emotion')
55 | emotionServer = require('emotion-server')
56 | reactEmotion = require('react-emotion')
57 | })
58 | test('only inserts rules that are not in the critical css', () => {
59 | const { Page1 } = getComponents(emotion, reactEmotion)
60 | const html = emotionServer.renderStylesToString(renderToString( ))
61 | expect(html).toMatchSnapshot()
62 | const { window } = new JSDOM(html)
63 | global.document = window.document
64 | global.window = window
65 | global.__SECRET_EMOTION__ = undefined
66 | setHtml(html, document)
67 | jest.resetModules()
68 | emotion = require('emotion')
69 | emotionServer = require('emotion-server')
70 | reactEmotion = require('react-emotion')
71 |
72 | expect(emotion.caches.registered).toEqual({})
73 |
74 | const { Page1: NewPage1 } = getComponents(emotion, reactEmotion)
75 | renderToString( )
76 | expect(getInjectedRules(emotion)).toMatchSnapshot()
77 | expect(getCssFromChunks(emotion, document)).toMatchSnapshot()
78 | })
79 | })
80 |
--------------------------------------------------------------------------------
/packages/emotion-server/test/stream.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * @jest-environment node
4 | */
5 | import React from 'react'
6 | import { renderToString } from 'react-dom/server'
7 | import {
8 | getComponents,
9 | getInjectedRules,
10 | createBigComponent,
11 | getCssFromChunks,
12 | setHtml,
13 | renderToStringWithStream
14 | } from './util'
15 | import { JSDOM } from 'jsdom'
16 |
17 | let emotion
18 | let emotionServer
19 | let reactEmotion
20 |
21 | describe('renderStylesToNodeStream', () => {
22 | beforeEach(() => {
23 | global.__SECRET_EMOTION__ = undefined
24 | jest.resetModules()
25 | emotion = require('emotion')
26 | emotionServer = require('emotion-server')
27 | reactEmotion = require('react-emotion')
28 | })
29 | test('renders styles with ids', async () => {
30 | const { Page1, Page2 } = getComponents(emotion, reactEmotion)
31 | expect(
32 | await renderToStringWithStream( , emotionServer)
33 | ).toMatchSnapshot()
34 | expect(
35 | await renderToStringWithStream( , emotionServer)
36 | ).toMatchSnapshot()
37 | })
38 | test('renders large recursive component', async () => {
39 | const BigComponent = createBigComponent(emotion)
40 | expect(
41 | await renderToStringWithStream(
42 | ,
43 | emotionServer
44 | )
45 | ).toMatchSnapshot()
46 | })
47 | })
48 | describe('hydration', () => {
49 | afterAll(() => {
50 | global.document = undefined
51 | global.window = undefined
52 | })
53 | beforeEach(() => {
54 | jest.resetModules()
55 | global.__SECRET_EMOTION__ = undefined
56 | emotion = require('emotion')
57 | emotionServer = require('emotion-server')
58 | reactEmotion = require('react-emotion')
59 | })
60 | test('only inserts rules that are not in the critical css', async () => {
61 | const { Page1 } = getComponents(emotion, reactEmotion)
62 | const html = await renderToStringWithStream( , emotionServer)
63 | expect(html).toMatchSnapshot()
64 | const { window } = new JSDOM(html)
65 | global.document = window.document
66 | global.window = window
67 | global.__SECRET_EMOTION__ = undefined
68 | setHtml(html, document)
69 | jest.resetModules()
70 | emotion = require('emotion')
71 | emotionServer = require('emotion-server')
72 | reactEmotion = require('react-emotion')
73 | expect(emotion.caches.registered).toEqual({})
74 |
75 | const { Page1: NewPage1 } = getComponents(emotion, reactEmotion)
76 | renderToString( )
77 | expect(getInjectedRules(emotion)).toMatchSnapshot()
78 | expect(getCssFromChunks(emotion, document)).toMatchSnapshot()
79 | })
80 | })
81 |
--------------------------------------------------------------------------------
/packages/create-emotion/test/inline.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * @jest-environment node
4 | */
5 | import React from 'react'
6 | import { renderToString } from 'react-dom/server'
7 | import {
8 | getComponents,
9 | getInjectedRules,
10 | createBigComponent,
11 | getCssFromChunks,
12 | setHtml
13 | } from '../../emotion-server/test/util'
14 | import { JSDOM } from 'jsdom'
15 |
16 | let emotion
17 | let emotionServer
18 | let reactEmotion
19 |
20 | describe('renderStylesToString', () => {
21 | beforeEach(() => {
22 | global.__SECRET_EMOTION__ = undefined
23 | jest.resetModules()
24 | emotion = require('./emotion-instance')
25 | emotionServer = require('./emotion-instance')
26 | reactEmotion = require('./emotion-instance')
27 | })
28 | test('renders styles with ids', () => {
29 | const { Page1, Page2 } = getComponents(emotion, reactEmotion)
30 | expect(
31 | emotionServer.renderStylesToString(renderToString( ))
32 | ).toMatchSnapshot()
33 | expect(
34 | emotionServer.renderStylesToString(renderToString( ))
35 | ).toMatchSnapshot()
36 | })
37 | test('renders large recursive component', () => {
38 | const BigComponent = createBigComponent(emotion)
39 | expect(
40 | emotionServer.renderStylesToString(
41 | renderToString( )
42 | )
43 | ).toMatchSnapshot()
44 | })
45 | })
46 | describe('hydration', () => {
47 | afterAll(() => {
48 | global.document = undefined
49 | global.window = undefined
50 | })
51 | beforeEach(() => {
52 | global.__SECRET_EMOTION__ = undefined
53 | jest.resetModules()
54 | emotion = require('./emotion-instance')
55 | emotionServer = require('./emotion-instance')
56 | reactEmotion = require('./emotion-instance')
57 | })
58 | test('only inserts rules that are not in the critical css', () => {
59 | const { Page1 } = getComponents(emotion, reactEmotion)
60 | const html = emotionServer.renderStylesToString(renderToString( ))
61 | expect(html).toMatchSnapshot()
62 | const { window } = new JSDOM(html)
63 | global.document = window.document
64 | global.window = window
65 | global.__SECRET_EMOTION__ = undefined
66 | setHtml(html, document)
67 | jest.resetModules()
68 | emotion = require('./emotion-instance')
69 | emotionServer = require('./emotion-instance')
70 | reactEmotion = require('./emotion-instance')
71 |
72 | expect(emotion.caches.registered).toEqual({})
73 |
74 | const { Page1: NewPage1 } = getComponents(emotion, reactEmotion)
75 | renderToString( )
76 | expect(getInjectedRules(emotion)).toMatchSnapshot()
77 | expect(getCssFromChunks(emotion, document)).toMatchSnapshot()
78 | })
79 | })
80 |
--------------------------------------------------------------------------------
/packages/emotion/test/__snapshots__/css-prop.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`css prop react basic 1`] = `
4 | .emotion-0 {
5 | color: red;
6 | font-size: 1px;
7 | }
8 |
9 |
12 | hello world
13 |
14 | `;
15 |
16 | exports[`css prop react kitchen sink 1`] = `
17 | .emotion-4 {
18 | display: -webkit-box;
19 | display: -webkit-flex;
20 | display: -ms-flexbox;
21 | display: flex;
22 | font-weight: bold;
23 | display: -webkit-box;
24 | display: -webkit-flex;
25 | display: -ms-flexbox;
26 | display: flex;
27 | -webkit-box-pack: center;
28 | -webkit-justify-content: center;
29 | -ms-flex-pack: center;
30 | justify-content: center;
31 | -webkit-align-items: center;
32 | -webkit-box-align: center;
33 | -ms-flex-align: center;
34 | align-items: center;
35 | }
36 |
37 | .emotion-0 {
38 | font-size: 6;
39 | color: red;
40 | }
41 |
42 | .emotion-1 {
43 | color: blue;
44 | }
45 |
46 | .emotion-2 {
47 | display: -webkit-inline-box;
48 | display: -webkit-inline-flex;
49 | display: -ms-inline-flexbox;
50 | display: inline-flex;
51 | }
52 |
53 | .emotion-3 {
54 | color: red;
55 | border-radius: 5;
56 | }
57 |
58 | .emotion-3:hover {
59 | font-weight: bold;
60 | color: gray;
61 | }
62 |
63 |
66 |
69 | BOOM
70 |
71 |
74 | Hello
75 |
76 |
79 | World
80 |
81 |
84 | hello world
85 |
86 |
87 | `;
88 |
89 | exports[`css prop react merging regular classes 1`] = `
90 | .emotion-0 {
91 | display: block;
92 | }
93 |
94 |
97 | `;
98 |
99 | exports[`css prop react objects 1`] = `
100 | .emotion-0 {
101 | color: red;
102 | font-size: 1px;
103 | }
104 |
105 |
108 | hello world
109 |
110 | `;
111 |
112 | exports[`css prop react specificity with composition 1`] = `
113 | .emotion-0 {
114 | display: block;
115 | display: -webkit-box;
116 | display: -webkit-flex;
117 | display: -ms-flexbox;
118 | display: flex;
119 | }
120 |
121 |
124 | `;
125 |
126 | exports[`css prop react string expression 1`] = `
127 | .emotion-0 {
128 | color: red;
129 | background: blue;
130 | font-size: 48px;
131 | }
132 |
133 |
136 | hello world
137 |
138 | `;
139 |
--------------------------------------------------------------------------------
/packages/create-emotion/test/stream.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * @jest-environment node
4 | */
5 | import React from 'react'
6 | import { renderToString } from 'react-dom/server'
7 | import {
8 | getComponents,
9 | getInjectedRules,
10 | createBigComponent,
11 | getCssFromChunks,
12 | setHtml,
13 | renderToStringWithStream
14 | } from '../../emotion-server/test/util'
15 | import { JSDOM } from 'jsdom'
16 |
17 | let emotion
18 | let emotionServer
19 | let reactEmotion
20 |
21 | describe('renderStylesToNodeStream', () => {
22 | beforeEach(() => {
23 | global.__SECRET_EMOTION__ = undefined
24 | jest.resetModules()
25 | emotion = require('./emotion-instance')
26 | emotionServer = require('./emotion-instance')
27 | reactEmotion = require('./emotion-instance')
28 | })
29 | test('renders styles with ids', async () => {
30 | const { Page1, Page2 } = getComponents(emotion, reactEmotion)
31 | expect(
32 | await renderToStringWithStream( , emotionServer)
33 | ).toMatchSnapshot()
34 | expect(
35 | await renderToStringWithStream( , emotionServer)
36 | ).toMatchSnapshot()
37 | })
38 | test('renders large recursive component', async () => {
39 | const BigComponent = createBigComponent(emotion)
40 | expect(
41 | await renderToStringWithStream(
42 | ,
43 | emotionServer
44 | )
45 | ).toMatchSnapshot()
46 | })
47 | })
48 | describe('hydration', () => {
49 | afterAll(() => {
50 | global.document = undefined
51 | global.window = undefined
52 | })
53 | beforeEach(() => {
54 | jest.resetModules()
55 | global.__SECRET_EMOTION__ = undefined
56 | emotion = require('./emotion-instance')
57 | emotionServer = require('./emotion-instance')
58 | reactEmotion = require('./emotion-instance')
59 | })
60 | test('only inserts rules that are not in the critical css', async () => {
61 | const { Page1 } = getComponents(emotion, reactEmotion)
62 | const html = await renderToStringWithStream( , emotionServer)
63 | expect(html).toMatchSnapshot()
64 | const { window } = new JSDOM(html)
65 | global.document = window.document
66 | global.window = window
67 | global.__SECRET_EMOTION__ = undefined
68 | setHtml(html, document)
69 | jest.resetModules()
70 | emotion = require('./emotion-instance')
71 | emotionServer = require('./emotion-instance')
72 | reactEmotion = require('./emotion-instance')
73 | expect(emotion.caches.registered).toEqual({})
74 |
75 | const { Page1: NewPage1 } = getComponents(emotion, reactEmotion)
76 | renderToString( )
77 | expect(getInjectedRules(emotion)).toMatchSnapshot()
78 | expect(getCssFromChunks(emotion, document)).toMatchSnapshot()
79 | })
80 | })
81 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/test/source-map.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { createInlineTests } from './util'
3 |
4 | const cases = {
5 | 'css source map': {
6 | code: `
7 | css\`
8 | margin: 12px 48px;
9 | color: #ffffff;
10 | display: flex;
11 | flex: 1 0 auto;
12 | color: blue;
13 | @media(min-width: 420px) {
14 | line-height: 40px;
15 | }
16 | width: \${widthVar};
17 | \``,
18 |
19 | filename: 'css.source-map.test.js'
20 | },
21 |
22 | 'styled object styles source map': {
23 | code: `
24 | styled('div')({
25 | color: 'blue',
26 | '&:hover': {
27 | '& .name': {
28 | color: 'amethyst',
29 | '&:focus': {
30 | color: 'burlywood',
31 | [mq[0]]: {
32 | color: 'rebeccapurple'
33 | }
34 | }
35 | },
36 | color: 'green'
37 | }
38 | })
39 | `,
40 |
41 | filename: 'css-nested.source-map.test.js'
42 | },
43 |
44 | 'styled source map': {
45 | code: `const Avatar = styled('img')\`
46 | width: 96px;
47 | height: 96px;
48 |
49 | border-radius: $\{props =>
50 | props.theme.borderRadius};
51 |
52 | border: 1px solid $\{props =>
53 | props.theme.borderColor};
54 | \``,
55 |
56 | filename: 'styled.source-map.test.js'
57 | },
58 |
59 | 'css prop': {
60 | code: `
61 |
80 | `,
81 |
82 | filename: 'site.source-map.test.js'
83 | },
84 |
85 | 'css prop with objects': {
86 | code: `
87 |
92 | `,
93 |
94 | filename: 'site.source-map.test.js'
95 | },
96 |
97 | 'css prop with merge': {
98 | code: `
99 |
105 | `,
106 |
107 | filename: 'site.source-map.test.js'
108 | },
109 | 'css object': {
110 | code: `css({color: 'hotpink'})`
111 | }
112 | }
113 |
114 | for (const thing in cases) {
115 | cases[thing].opts = { sourceMap: true }
116 | }
117 |
118 | createInlineTests('source map', cases)
119 |
--------------------------------------------------------------------------------
/packages/emotion-theming/test/__snapshots__/index.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`emotion integration test 1`] = `
4 | .emotion-0 {
5 | color: red;
6 | background-color: green;
7 | border: 1px solid blue;
8 | }
9 |
10 |
18 |
19 |
22 |
23 |
24 | `;
25 |
26 | exports[`updating theme after a listener unsubscribed 1`] = `
27 |
35 |
42 |
43 |
44 |
51 |
52 |
53 |
60 |
61 |
62 |
63 |
64 | `;
65 |
66 | exports[`updating theme after a listener unsubscribed 2`] = `
67 |
75 |
82 |
83 |
84 |
91 |
92 |
93 |
94 |
95 | `;
96 |
97 | exports[`updating theme after a listener unsubscribed 3`] = `
98 |
106 |
113 |
114 |
115 |
122 |
123 |
124 |
125 |
126 | `;
127 |
--------------------------------------------------------------------------------
/packages/babel-plugin-emotion/test/inject-global.test.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { createInlineTests, createExtractTests } from './util'
3 |
4 | const cases = {
5 | 'injectGlobal basic': {
6 | code: `
7 | injectGlobal\`
8 | body {
9 | margin: 0;
10 | padding: 0;
11 | & > div {
12 | display: flex;
13 | }
14 | }
15 | html {
16 | background: green;
17 | }
18 | \`;`
19 | },
20 | 'injectGlobal with interpolation': {
21 | code: `
22 | injectGlobal\`
23 | body {
24 | margin: 0;
25 | padding: 0;
26 | display: \${display};
27 | & > div {
28 | display: none;
29 | }
30 | }
31 | html {
32 | background: green;
33 | }
34 | \`;`,
35 | extract: false
36 | },
37 | 'static change import': {
38 | code: `
39 | inject\`
40 | body {
41 | margin: 0;
42 | padding: 0;
43 | & > div {
44 | display: flex;
45 | }
46 | }
47 | html {
48 | background: green;
49 | }
50 | \`;
51 | injectGlobal\`
52 | body {
53 | margin: 0;
54 | padding: 0;
55 | & > div {
56 | display: flex;
57 | }
58 | }
59 | html {
60 | background: green;
61 | }
62 | \`;`,
63 |
64 | opts: { importedNames: { injectGlobal: 'inject' } }
65 | },
66 | 'dynamic change import': {
67 | code: `
68 | import { injectGlobal as inject } from 'emotion'
69 | inject\`
70 | body {
71 | margin: 0;
72 | padding: 0;
73 | & > div {
74 | display: flex;
75 | }
76 | }
77 | html {
78 | background: green;
79 | }
80 | \`;
81 | injectGlobal\`
82 | body {
83 | margin: 0;
84 | padding: 0;
85 | & > div {
86 | display: flex;
87 | }
88 | }
89 | html {
90 | background: green;
91 | }
92 | \`;`
93 | },
94 | 'with @font-face': {
95 | code: `injectGlobal\`
96 | @font-face {
97 | font-family: 'Patrick Hand SC';
98 | font-style: normal;
99 | font-weight: 400;
100 | src: local('Patrick Hand SC'), local('PatrickHandSC-Regular'),
101 | url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2)
102 | format('woff2');
103 | unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf,
104 | U+2c60-2c7f, U+A720-A7FF;
105 | }
106 | \``
107 | }
108 | }
109 |
110 | createInlineTests('injectGlobal', cases)
111 |
112 | createExtractTests('injectGlobal extract', cases)
113 |
--------------------------------------------------------------------------------