├── .eslintignore
├── .gitattributes
├── .npmrc
├── src
├── __tests__
│ ├── __fixtures__
│ │ ├── .babelrc
│ │ ├── import.js
│ │ ├── require.js
│ │ ├── wrapped-component.js
│ │ ├── styles.js
│ │ ├── glam-not-orous.js
│ │ ├── references.js
│ │ ├── imported-styles.js
│ │ ├── member-expression-reference.js
│ │ ├── arrow-ternary.js
│ │ ├── .eslintrc
│ │ └── untouched.js
│ ├── __snapshots__
│ │ ├── babel-plugin.js.snap
│ │ └── index.js.snap
│ ├── babel-plugin.js
│ └── index.js
├── index.js
├── get-literalizers.js
└── babel-plugin.js
├── .gitignore
├── other
├── EXAMPLES.md
├── ROADMAP.md
└── CODE_OF_CONDUCT.md
├── .babelrc
├── CHANGELOG.md
├── .travis.yml
├── .all-contributorsrc
├── .github
├── PULL_REQUEST_TEMPLATE.md
└── ISSUE_TEMPLATE.md
├── LICENSE
├── package-scripts.js
├── package.json
├── CONTRIBUTING.md
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | dist
4 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.js text eol=lf
3 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | registry=http://registry.npmjs.org/
2 | save-exact=true
3 |
--------------------------------------------------------------------------------
/src/__tests__/__fixtures__/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "babelrc": false
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | dist
4 | .opt-in
5 | .opt-out
6 |
7 |
--------------------------------------------------------------------------------
/src/__tests__/__fixtures__/import.js:
--------------------------------------------------------------------------------
1 | import fred from 'glamorous'
2 | fred.div({fontSize: 10})
3 |
--------------------------------------------------------------------------------
/src/__tests__/__fixtures__/require.js:
--------------------------------------------------------------------------------
1 | const ethel = require('glamorous')
2 | ethel.span({fontSize: 25})
3 |
--------------------------------------------------------------------------------
/src/__tests__/__fixtures__/wrapped-component.js:
--------------------------------------------------------------------------------
1 | import g from 'glamorous'
2 |
3 | g('div')({margin: 2})
4 |
--------------------------------------------------------------------------------
/other/EXAMPLES.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | There aren't any examples yet! Want to add one? See `CONTRIBUTING.md`
4 |
--------------------------------------------------------------------------------
/src/__tests__/__fixtures__/styles.js:
--------------------------------------------------------------------------------
1 | export const colors = {
2 | primary: 'red',
3 | secondary: 'blue',
4 | }
5 |
--------------------------------------------------------------------------------
/src/__tests__/__fixtures__/glam-not-orous.js:
--------------------------------------------------------------------------------
1 | import glamNotOrous from 'glam-not-orous'
2 | glamNotOrous.div({blah: 'blah'})
3 |
--------------------------------------------------------------------------------
/src/__tests__/__fixtures__/references.js:
--------------------------------------------------------------------------------
1 | import glamorous from 'glamorous'
2 |
3 | const styles = {color: 'palevioletred'}
4 |
5 | glamorous.div(styles)
6 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "targets": {
5 | "node": 4.5
6 | }
7 | }],
8 | "stage-2"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/src/__tests__/__fixtures__/imported-styles.js:
--------------------------------------------------------------------------------
1 | import glamorous from 'glamorous'
2 | import {colors as col} from './styles'
3 |
4 | glamorous.p({color: col.primary})
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | The changelog is automatically updated using [semantic-release](https://github.com/semantic-release/semantic-release).
4 | You can see it on the [releases page](../../releases).
5 |
--------------------------------------------------------------------------------
/src/__tests__/__fixtures__/member-expression-reference.js:
--------------------------------------------------------------------------------
1 | import glamorous from 'glamorous'
2 |
3 | const colors = {
4 | primary: 'red',
5 | secondary: 'blue',
6 | }
7 |
8 | glamorous.p({color: colors.primary})
9 |
--------------------------------------------------------------------------------
/src/__tests__/__fixtures__/arrow-ternary.js:
--------------------------------------------------------------------------------
1 | import glamorous from 'glamorous'
2 |
3 | glamorous.div(
4 | ({big}) =>
5 | (big
6 | ? {
7 | fontSize: 20,
8 | }
9 | : {
10 | fontSize: 12,
11 | })
12 | )
13 |
--------------------------------------------------------------------------------
/other/ROADMAP.md:
--------------------------------------------------------------------------------
1 | # Project Roadmap
2 |
3 | This is where we'll define a few things about the library's goals.
4 |
5 | We haven't filled this out yet though. Care to help? See `CONTRIBUTING.md`
6 |
7 | ## Want to do
8 |
9 | ## Might do
10 |
11 | ## Wont do
12 |
--------------------------------------------------------------------------------
/src/__tests__/__fixtures__/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": [
4 | "kentcdodds/possible-errors",
5 | "kentcdodds/es6/possible-errors",
6 | "kentcdodds/prettier"
7 | ],
8 | "rules": {
9 | "semi": [2, "never"] // for ever and ever
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/babel-plugin.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`converts static objects to css class names 1`] = `
4 | "import glamorous from 'glamorous'
5 | glamorous.div(
6 | \\"css-1i7gko5\\",
7 | \\"css-oh2yv\\",
8 | )"
9 | `;
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | cache:
4 | directories:
5 | - node_modules
6 | notifications:
7 | email: false
8 | node_js:
9 | - '6'
10 | script:
11 | - yarn start validate
12 | after_success:
13 | - yarn start report-coverage
14 | - yarn start release
15 | branches:
16 | only:
17 | - master
18 |
--------------------------------------------------------------------------------
/src/__tests__/__fixtures__/untouched.js:
--------------------------------------------------------------------------------
1 | import fakeGlamorous from 'something-else'
2 | import realGlamorous from 'glamorous'
3 | const requiredGlamorous = require('glamorous')
4 |
5 | fakeGlamorous.div({fontSize: 20})
6 |
7 | realGlamorous.div()
8 |
9 | realGlamorous.div
10 |
11 | realGlamorous.span(globalThing)
12 |
13 | requiredGlamorous('h2')
14 |
--------------------------------------------------------------------------------
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "css-in-js-precompiler",
3 | "projectOwner": "kentcdodds",
4 | "files": [
5 | "README.md"
6 | ],
7 | "imageSize": 100,
8 | "commit": false,
9 | "contributors": [
10 | {
11 | "login": "kentcdodds",
12 | "name": "Kent C. Dodds",
13 | "avatar_url": "https://avatars.githubusercontent.com/u/1500684?v=3",
14 | "profile": "https://kentcdodds.com",
15 | "contributions": [
16 | "code",
17 | "doc",
18 | "infra",
19 | "test"
20 | ]
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/src/__tests__/babel-plugin.js:
--------------------------------------------------------------------------------
1 | import stripIndent from 'strip-indent'
2 | import * as recast from 'recast'
3 | import * as babel from 'babel-core'
4 | import plugin from '../babel-plugin'
5 |
6 | test('converts static objects to css class names', () => {
7 | const source = `
8 | import glamorous from 'glamorous'
9 | glamorous.div(
10 | {
11 | fontSize: 20,
12 | fontWeight: 'normal',
13 | },
14 | {
15 | margin: 20,
16 | ':hover': {
17 | margin: 10,
18 | },
19 | },
20 | )
21 | `
22 | const code = transpile(source)
23 | expect(code).toMatchSnapshot()
24 | })
25 |
26 | function transpile(source) {
27 | const {code} = babel.transform(stripIndent(source).trim(), {
28 | parserOpts: {parser: recast.parse},
29 | generatorOpts: {generator: recast.print, lineTerminator: '\n'},
30 | babelrc: false,
31 | plugins: [plugin],
32 | })
33 | return code
34 | }
35 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | **What**:
19 |
20 |
21 | **Why**:
22 |
23 |
24 | **How**:
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2016 Kent C. Dodds
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | SOFTWARE.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
13 |
14 | - `css-in-js-precompiler` version:
15 | - `node` version:
16 | - `npm` (or `yarn`) version:
17 |
18 | Relevant code or config
19 |
20 | ```javascript
21 |
22 | ```
23 |
24 | What you did:
25 |
26 |
27 |
28 | What happened:
29 |
30 |
31 |
32 | Reproduction repository:
33 |
34 |
38 |
39 | Problem description:
40 |
41 |
42 |
43 | Suggested solution:
44 |
--------------------------------------------------------------------------------
/package-scripts.js:
--------------------------------------------------------------------------------
1 | const npsUtils = require('nps-utils')
2 |
3 | const series = npsUtils.series
4 | const concurrent = npsUtils.concurrent
5 | const rimraf = npsUtils.rimraf
6 |
7 | module.exports = {
8 | scripts: {
9 | contributors: {
10 | add: {
11 | description: 'When new people contribute to the project, run this',
12 | script: 'all-contributors add',
13 | },
14 | generate: {
15 | description: 'Update the badge and contributors table',
16 | script: 'all-contributors generate',
17 | },
18 | },
19 | commit: {
20 | description: 'This uses commitizen to help us generate well formatted commit messages',
21 | script: 'git-cz',
22 | },
23 | test: {
24 | default: 'jest --coverage',
25 | watch: 'jest --watch',
26 | },
27 | build: {
28 | description: 'delete the dist directory and run babel to build the files',
29 | script: series(
30 | rimraf('dist'),
31 | 'babel --copy-files --out-dir dist --ignore __tests__ src'
32 | ),
33 | },
34 | lint: {
35 | description: 'lint the entire project',
36 | script: 'eslint .',
37 | },
38 | reportCoverage: {
39 | description: 'Report coverage stats to codecov. This should be run after the `test` script',
40 | script: 'codecov',
41 | },
42 | release: {
43 | description: 'We automate releases with semantic-release. This should only be run on travis',
44 | script: 'echo "we are not auto-releasing quite yet..."',
45 | // script: series('semantic-release pre', 'npm publish', 'semantic-release post'),
46 | },
47 | validate: {
48 | description: 'This runs several scripts to make sure things look good before committing or on clean install',
49 | script: concurrent.nps('lint', 'build', 'test'),
50 | },
51 | },
52 | options: {
53 | silent: false,
54 | },
55 | }
56 |
57 | // this is not transpiled
58 | /*
59 | eslint
60 | max-len: 0,
61 | comma-dangle: [
62 | 2,
63 | {
64 | arrays: 'always-multiline',
65 | objects: 'always-multiline',
66 | functions: 'never'
67 | }
68 | ]
69 | */
70 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs'
2 | import * as babel from 'babel-core'
3 | import * as glamor from 'glamor'
4 | import {renderStatic} from 'glamor/server'
5 | import plugin from './babel-plugin'
6 |
7 | const defaultBabelOptions = {
8 | babelrc: false,
9 | sourceMaps: true,
10 | plugins: [],
11 | parserOpts: {
12 | plugins: [
13 | // include all the things because why not?
14 | // 'estree', // except this one because why...?
15 | 'jsx',
16 | 'flow',
17 | 'classConstructorCall',
18 | 'doExpressions',
19 | 'trailingFunctionCommas',
20 | 'objectRestSpread',
21 | 'decorators',
22 | 'classProperties',
23 | 'exportExtensions',
24 | 'exponentiationOperator',
25 | 'asyncGenerators',
26 | 'functionBind',
27 | 'functionSent',
28 | 'dynamicImport',
29 | 'asyncFunctions',
30 | ],
31 | },
32 | }
33 |
34 | module.exports = precompile
35 |
36 | function precompile({sources = []}) {
37 | let transformed = []
38 | const {css, ids} = renderStatic(() => {
39 | transformed = sources
40 | .map(({filename, code = fs.readFileSync(filename, 'utf8'), ...rest}) => ({
41 | filename,
42 | code,
43 | ...rest,
44 | }))
45 | .filter(({code}) => hasGlamorous(code))
46 | .map(({filename, code, babelOptions = {}}) => {
47 | if (!hasGlamorous(code)) {
48 | return {
49 | source: code,
50 | code,
51 | filename,
52 | }
53 | }
54 | const babelOptionsToUse = {
55 | filename,
56 | ...defaultBabelOptions,
57 | ...babelOptions,
58 | }
59 | babelOptionsToUse.plugins.unshift(plugin)
60 | return {
61 | source: code,
62 | filename,
63 | ...babel.transform(code, babelOptionsToUse),
64 | }
65 | })
66 | return '
fake html to make glamor happy
'
67 | })
68 | glamor.flush() // make sure multiple runs don't mess things up
69 | return {transformed, css, ids}
70 | }
71 |
72 | // TODO: when we support more than just glamorous, we'll want to
73 | // expand this or even remove it entirely, but this ahead-of-time
74 | // filtering is really handy for performance.
75 | function hasGlamorous(code) {
76 | return code.indexOf('glamorous') !== -1
77 | }
78 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "css-in-js-precompiler",
3 | "version": "0.0.0-semantically-released",
4 | "description": "Precompiles CSS-in-JS objects to CSS strings",
5 | "main": "dist/index.js",
6 | "scripts": {
7 | "start": "nps",
8 | "test": "nps test",
9 | "commitmsg": "opt --in commit-msg --exec \"validate-commit-msg\"",
10 | "precommit": "lint-staged && opt --in pre-commit --exec \"npm start validate\""
11 | },
12 | "files": [
13 | "dist"
14 | ],
15 | "keywords": [],
16 | "author": "Kent C. Dodds (http://kentcdodds.com/)",
17 | "license": "MIT",
18 | "dependencies": {
19 | "babel-core": "^6.24.1",
20 | "glamor": "^2.20.24"
21 | },
22 | "devDependencies": {
23 | "all-contributors-cli": "^4.0.1",
24 | "babel-cli": "^6.23.0",
25 | "babel-jest": "^19.0.0",
26 | "babel-preset-env": "^1.2.0",
27 | "babel-preset-stage-2": "^6.22.0",
28 | "babel-register": "^6.23.0",
29 | "codecov": "^2.1.0",
30 | "commitizen": "^2.9.6",
31 | "css": "^2.2.1",
32 | "cz-conventional-changelog": "^2.0.0",
33 | "eslint": "^3.17.0",
34 | "eslint-config-kentcdodds": "^12.0.0",
35 | "husky": "^0.13.2",
36 | "jest-cli": "^19.0.2",
37 | "lint-staged": "^3.3.1",
38 | "nps": "^5.0.3",
39 | "nps-utils": "^1.1.2",
40 | "opt-cli": "^1.5.1",
41 | "prettier-eslint-cli": "^3.1.2",
42 | "recast": "^0.12.3",
43 | "semantic-release": "^6.3.6",
44 | "strip-indent": "^2.0.0",
45 | "validate-commit-msg": "^2.11.1"
46 | },
47 | "eslintConfig": {
48 | "extends": [
49 | "kentcdodds",
50 | "kentcdodds/jest",
51 | "kentcdodds/prettier"
52 | ]
53 | },
54 | "lint-staged": {
55 | "*.js": [
56 | "prettier-eslint --write",
57 | "git add"
58 | ]
59 | },
60 | "jest": {
61 | "testEnvironment": "node",
62 | "testPathIgnorePatterns": [
63 | "/node_modules/",
64 | "/__fixtures__/"
65 | ],
66 | "transformIgnorePatterns": [
67 | "/node_modules/",
68 | "/__fixtures__/"
69 | ],
70 | "coverageThreshold": {
71 | "global": {
72 | "branches": 80,
73 | "functions": 80,
74 | "lines": 80,
75 | "statements": 80
76 | }
77 | }
78 | },
79 | "config": {
80 | "commitizen": {
81 | "path": "node_modules/cz-conventional-changelog"
82 | }
83 | },
84 | "repository": {
85 | "type": "git",
86 | "url": "https://github.com/kentcdodds/css-in-js-precompiler.git"
87 | },
88 | "bugs": {
89 | "url": "https://github.com/kentcdodds/css-in-js-precompiler/issues"
90 | },
91 | "homepage": "https://github.com/kentcdodds/css-in-js-precompiler#readme"
92 | }
93 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thanks for being willing to contribute!
4 |
5 | **Working on your first Pull Request?** You can learn how from this *free* series
6 | [How to Contribute to an Open Source Project on GitHub][egghead]
7 |
8 | ## Project setup
9 |
10 | 1. Fork and clone the repo
11 | 2. `$ npm install` to install dependencies
12 | 3. `$ npm start validate` to validate you've got it working
13 | 4. Create a branch for your PR
14 |
15 | This project uses [`nps`][nps] and you can run `npm start` to see what scripts are available.
16 |
17 | ## Add yourself as a contributor
18 |
19 | This project follows the [all contributors][all-contributors] specification. To add yourself to the table of
20 | contributors on the README.md, please use the automated script as part of your PR:
21 |
22 | ```console
23 | npm start "addContributor "
24 | ```
25 |
26 | Follow the prompt. If you've already added yourself to the list and are making a new type of contribution, you can run
27 | it again and select the added contribution type.
28 |
29 | ## Committing and Pushing changes
30 |
31 | This project uses [`semantic-release`][semantic-release] to do automatic releases and generate a changelog based on the
32 | commit history. So we follow [a convention][convention] for commit messages. Please follow this convention for your
33 | commit messages.
34 |
35 | You can use `commitizen` to help you to follow [the convention][convention]
36 |
37 | Once you are ready to commit the changes, please use the below commands
38 |
39 | 1. `git add `
40 | 2. `$ npm start commit`
41 |
42 | ... and follow the instruction of the interactive prompt.
43 |
44 | ### opt into git hooks
45 |
46 | There are git hooks set up with this project that are automatically installed when you install dependencies. They're
47 | really handy, but are turned off by default (so as to not hinder new contributors). You can opt into these by creating
48 | a file called `.opt-in` at the root of the project and putting this inside:
49 |
50 | ```
51 | commit-msg
52 | pre-commit
53 | ```
54 |
55 | ## Help needed
56 |
57 | Please checkout the [ROADMAP.md][ROADMAP] and raise an issue to discuss
58 | any of the items in the want to do or might do list.
59 |
60 | Also, please watch the repo and respond to questions/bug reports/feature requests! Thanks!
61 |
62 | [egghead]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github
63 | [semantic-release]: https://npmjs.com/package/semantic-release
64 | [convention]: https://github.com/conventional-changelog/conventional-changelog-angular/blob/ed32559941719a130bb0327f886d6a32a8cbc2ba/convention.md
65 | [all-contributors]: https://github.com/kentcdodds/all-contributors
66 | [ROADMAP]: ./other/ROADMAP.md
67 | [nps]: https://npmjs.com/package/nps
68 |
--------------------------------------------------------------------------------
/other/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at kent+coc@doddsfamily.us. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/index.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`1. follows imports 1`] = `
4 | "
5 | import fred from 'glamorous'
6 | fred.div({fontSize: 10})
7 |
8 | 👇
9 |
10 | import fred from 'glamorous'
11 | fred.div(\\"css-1ea0ufb\\")
12 |
13 | .css-1ea0ufb,
14 | [data-css-1ea0ufb] {
15 | font-size: 10px;
16 | }
17 | "
18 | `;
19 |
20 | exports[`2. follows requires 1`] = `
21 | "
22 | const ethel = require('glamorous')
23 | ethel.span({fontSize: 25})
24 |
25 | 👇
26 |
27 | const ethel = require('glamorous')
28 | ethel.span(\\"css-1rbjb59\\")
29 |
30 | .css-1rbjb59,
31 | [data-css-1rbjb59] {
32 | font-size: 25px;
33 | }
34 | "
35 | `;
36 |
37 | exports[`3. supports statics in arrow functions 1`] = `
38 | "
39 | import glamorous from 'glamorous'
40 |
41 | glamorous.div(
42 | ({big}) =>
43 | (big
44 | ? {
45 | fontSize: 20,
46 | }
47 | : {
48 | fontSize: 12,
49 | })
50 | )
51 |
52 | 👇
53 |
54 | import glamorous from 'glamorous'
55 |
56 | glamorous.div(
57 | ({big}) =>
58 | (big
59 | ? \\"css-vfrxuj\\"
60 | : \\"css-1dmiui7\\")
61 | )
62 |
63 | .css-vfrxuj,
64 | [data-css-vfrxuj] {
65 | font-size: 20px;
66 | }
67 |
68 | .css-1dmiui7,
69 | [data-css-1dmiui7] {
70 | font-size: 12px;
71 | }
72 | "
73 | `;
74 |
75 | exports[`4. follows references 1`] = `
76 | "
77 | import glamorous from 'glamorous'
78 |
79 | const styles = {color: 'palevioletred'}
80 |
81 | glamorous.div(styles)
82 |
83 | 👇
84 |
85 | import glamorous from 'glamorous'
86 |
87 | const styles = \\"css-zij22n\\"
88 |
89 | glamorous.div(styles)
90 |
91 | .css-zij22n,
92 | [data-css-zij22n] {
93 | color: palevioletred;
94 | }
95 | "
96 | `;
97 |
98 | exports[`5. when creating custom glamorous components 1`] = `
99 | "
100 | import g from 'glamorous'
101 |
102 | g('div')({margin: 2})
103 |
104 | 👇
105 |
106 | import g from 'glamorous'
107 |
108 | g('div')(\\"css-18570p2\\")
109 |
110 | .css-18570p2,
111 | [data-css-18570p2] {
112 | margin: 2px;
113 | }
114 | "
115 | `;
116 |
117 | exports[`6. styles using member expressions 1`] = `
118 | "
119 | import glamorous from 'glamorous'
120 |
121 | const colors = {
122 | primary: 'red',
123 | secondary: 'blue',
124 | }
125 |
126 | glamorous.p({color: colors.primary})
127 |
128 | 👇
129 |
130 | import glamorous from 'glamorous'
131 |
132 | const colors = {
133 | primary: 'red',
134 | secondary: 'blue',
135 | }
136 |
137 | glamorous.p(\\"css-1ezp9xe\\")
138 |
139 | .css-1ezp9xe,
140 | [data-css-1ezp9xe] {
141 | color: red;
142 | }
143 | "
144 | `;
145 |
146 | exports[`7. styles using variables across files 1`] = `
147 | "
148 | import glamorous from 'glamorous'
149 | import {colors as col} from './styles'
150 |
151 | glamorous.p({color: col.primary})
152 |
153 | 👇
154 |
155 | import glamorous from 'glamorous'
156 | import {colors as col} from './styles'
157 |
158 | glamorous.p(\\"css-1ezp9xe\\")
159 |
160 | .css-1ezp9xe,
161 | [data-css-1ezp9xe] {
162 | color: red;
163 | }
164 | "
165 | `;
166 |
167 | exports[`css from 4 concatenated files 1`] = `
168 | "
169 | justCode:
170 | import glamorous from 'glamorous';glamorous.div({margin: 0})
171 | import glamorous from 'glamorous';glamorous.article({margin: 1})
172 |
173 | justFiles:
174 | /src/__tests__/__fixtures__/import.js
175 | /src/__tests__/__fixtures__/require.js
176 |
177 | 👇
178 |
179 | .css-1wiofhw,
180 | [data-css-1wiofhw] {
181 | margin: 0;
182 | }
183 |
184 | .css-1jr7337,
185 | [data-css-1jr7337] {
186 | margin: 1px;
187 | }
188 |
189 | .css-1ea0ufb,
190 | [data-css-1ea0ufb] {
191 | font-size: 10px;
192 | }
193 |
194 | .css-1rbjb59,
195 | [data-css-1rbjb59] {
196 | font-size: 25px;
197 | }
198 | "
199 | `;
200 |
--------------------------------------------------------------------------------
/src/get-literalizers.js:
--------------------------------------------------------------------------------
1 | import nodePath from 'path'
2 | import * as babel from 'babel-core'
3 |
4 | const {types: t} = babel
5 |
6 | export default getLiteralizers
7 |
8 | function getLiteralizers({file}) {
9 | return toLiteral
10 |
11 | function objectToLiteral(path) {
12 | const props = path.get('properties').map(propPath => {
13 | const propNodeClone = t.clone(propPath.node)
14 | const valPath = propPath.get('value')
15 | propNodeClone.shorthand = false
16 | propNodeClone.value = toLiteral(valPath)
17 | return propNodeClone
18 | })
19 | return t.objectExpression(props)
20 | }
21 |
22 | function arrayToLiteral(path) {
23 | const els = path.get('elements').map(toLiteral)
24 | return t.arrayExpression(els)
25 | }
26 |
27 | function identifierToLiteral(path) {
28 | const binding = path.scope.getBinding(path.node.name)
29 | if (binding.path.type.startsWith('Import')) {
30 | return importToLiteral(binding.path)
31 | }
32 | const initPath = binding.path.get('init')
33 | return toLiteral(initPath)
34 | }
35 |
36 | function importToLiteral(path) {
37 | const importedFile = nodePath.resolve(
38 | nodePath.dirname(file.opts.filename),
39 | // TODO: support any extension and import style
40 | `${path.parent.source.value}.js`,
41 | )
42 | let literalValue
43 | babel.transformFileSync(importedFile, {
44 | plugins: [
45 | () => {
46 | let thingToLiteralize
47 | return {
48 | visitor: {
49 | // TODO: support other exports
50 | ExportNamedDeclaration(namedDeclarationPath) {
51 | thingToLiteralize = namedDeclarationPath
52 | .get('declaration.declarations')[0]
53 | .get('init')
54 | },
55 | Program: {
56 | exit() {
57 | literalValue = toLiteral(thingToLiteralize)
58 | },
59 | },
60 | },
61 | }
62 | },
63 | ],
64 | })
65 | return literalValue
66 | }
67 |
68 | function memberExpressionToLiteral(path) {
69 | const pathProperty = path.get('property')
70 | let literalProperty
71 | const pathPropertyValue = getPathPropertyValue()
72 | const literalObject = toLiteral(path.get('object'))
73 | if (t.isObjectExpression(literalObject)) {
74 | const literalObjectProperty = literalObject.properties.find(prop => {
75 | return prop.key.name === pathPropertyValue
76 | })
77 | if (literalObjectProperty) {
78 | literalProperty = literalObjectProperty.value
79 | }
80 | } else if (t.isArrayExpression(literalObject)) {
81 | literalProperty = literalObject.elements[pathPropertyValue]
82 | } else {
83 | throw new Error(
84 | // eslint-disable-next-line max-len
85 | `${literalObject.type} is not yet supported in memberExpressionToLiteral`,
86 | )
87 | }
88 | return t.clone(literalProperty || t.identifier('undefined'))
89 |
90 | function getPathPropertyValue() {
91 | if (pathProperty.isIdentifier() && path.node.computed) {
92 | return getLiteralPropertyValue(identifierToLiteral(pathProperty))
93 | } else {
94 | return getLiteralPropertyValue(pathProperty.node)
95 | }
96 | }
97 |
98 | function getLiteralPropertyValue(node) {
99 | if (t.isLiteral(node)) {
100 | return node.value
101 | } else if (t.isIdentifier(node)) {
102 | return node.name
103 | } else {
104 | throw new Error(
105 | // eslint-disable-next-line max-len
106 | `${node.type} is not yet supported in getLiteralPropertyValue of memberExpressionToLiteral`,
107 | )
108 | }
109 | }
110 | }
111 |
112 | function toLiteral(path) {
113 | const toLiterals = [
114 | [t.isLiteral, p => t.clone(p.node)],
115 | [t.isMemberExpression, memberExpressionToLiteral],
116 | [t.isIdentifier, identifierToLiteral],
117 | [t.isArrayExpression, arrayToLiteral],
118 | [t.isObjectExpression, objectToLiteral],
119 | ]
120 | return toLiterals.reduce((literalVal, [test, toLiteralFn]) => {
121 | if (literalVal) {
122 | return literalVal
123 | }
124 | if (test(path)) {
125 | return toLiteralFn(path)
126 | }
127 | return null
128 | }, null)
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/__tests__/index.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs'
2 | import path from 'path'
3 | import * as babel from 'babel-core'
4 | import cssParser from 'css'
5 | import stripIndent from 'strip-indent'
6 | import * as recast from 'recast'
7 |
8 | const precompile = require('../')
9 |
10 | const babelOptions = {
11 | parserOpts: {parser: recast.parse},
12 | generatorOpts: {generator: recast.print, lineTerminator: '\n'},
13 | }
14 |
15 | const tests = [
16 | {
17 | title: 'follows imports',
18 | fixtureName: 'import.js',
19 | },
20 | {
21 | title: 'follows requires',
22 | fixtureName: 'require.js',
23 | },
24 | {
25 | title: 'supports statics in arrow functions',
26 | fixtureName: 'arrow-ternary.js',
27 | },
28 | {
29 | title: 'follows references',
30 | fixtureName: 'references.js',
31 | },
32 | {
33 | title: 'when creating custom glamorous components',
34 | fixtureName: 'wrapped-component.js',
35 | },
36 | {
37 | title: 'styles using member expressions',
38 | fixtureName: 'member-expression-reference.js',
39 | },
40 | {
41 | title: 'styles using variables across files',
42 | fixtureName: 'imported-styles.js',
43 | },
44 | ]
45 |
46 | tests.forEach(({title, fixtureName, modifier}, index) => {
47 | if (modifier) {
48 | test[modifier](title, testFn)
49 | } else {
50 | test(title, testFn)
51 | }
52 | function testFn() {
53 | const filename = fixturePath(fixtureName)
54 | const sourceCode = fs.readFileSync(filename, 'utf8')
55 | const {transformed, css} = precompile({
56 | sources: [{filename, code: sourceCode, babelOptions}],
57 | })
58 |
59 | const [{code}] = transformed
60 | const output = trimAndNewline(
61 | [
62 | '\n',
63 | sourceCode.trim(),
64 | `\n\n 👇\n\n`,
65 | code.trim(),
66 | '\n\n',
67 | formatCSS(css),
68 | '\n',
69 | ].join(''),
70 | )
71 |
72 | expect(output).toMatchSnapshot(`${index + 1}. ${title}`)
73 | }
74 | })
75 |
76 | test('does not change code that should not be changed', () => {
77 | const sourceFile = path.join(__dirname, '__fixtures__/untouched.js')
78 | const source = fs.readFileSync(sourceFile, 'utf8')
79 | const {transformed, css} = precompile({
80 | sources: [{filename: sourceFile, babelOptions}],
81 | })
82 |
83 | const [{code}] = transformed
84 | expect(code.trim()).toEqual(source.trim())
85 | expect(css).toEqual('')
86 | })
87 |
88 | test('forwards along a bunch of stuff from babel', () => {
89 | const results = precompile({
90 | sources: [
91 | {
92 | code: stripIndent(
93 | `
94 | import glamorous from 'glamorous'
95 | glamorous.div(
96 | {
97 | fontSize: 20,
98 | fontWeight: 'normal',
99 | },
100 | {
101 | margin: 20,
102 | ':hover': {
103 | margin: 10,
104 | },
105 | },
106 | )
107 | someOtherCall({fontSize: 30})
108 | `,
109 | ).trim(),
110 | },
111 | ],
112 | })
113 | expect(Object.keys(results.transformed[0])).toEqual([
114 | // just extra things that might be useful
115 | 'source',
116 | 'filename',
117 | // babel stuff
118 | 'metadata',
119 | 'options',
120 | 'ignored',
121 | 'code',
122 | 'ast',
123 | 'map',
124 | ])
125 | expect(Object.keys(results)).toEqual(['transformed', 'css', 'ids'])
126 | })
127 |
128 | test('can accept sources with just code or just filename', () => {
129 | const justFilename = [
130 | {filename: fixturePath('import.js')},
131 | {filename: fixturePath('require.js')},
132 | ]
133 | const justCode = [
134 | {code: `import glamorous from 'glamorous';glamorous.div({margin: 0})`},
135 | {code: `import glamorous from 'glamorous';glamorous.article({margin: 1})`},
136 | ]
137 | const {transformed, css} = precompile({
138 | sources: [...justCode, ...justFilename],
139 | })
140 | expect(transformed).toHaveLength(4)
141 | const output = trimAndNewline(
142 | [
143 | 'justCode:\n ',
144 | justCode.map(({code}) => code).join('\n '),
145 | '\n\njustFiles:\n ',
146 | justFilename.map(({filename}) => relativizePaths(filename)).join('\n '),
147 | `\n\n 👇\n\n`,
148 | formatCSS(css),
149 | ].join(''),
150 | )
151 | expect(output).toMatchSnapshot(`css from 4 concatenated files`)
152 | })
153 |
154 | test('does not parse source which does not use `glamorous`', () => {
155 | const sources = [{code: 'import glamNotOrous from "glam-not-orous"'}]
156 | const sourceFiles = [fixturePath('glam-not-orous.js')]
157 | const transformSpy = jest.spyOn(babel, 'transform')
158 | precompile({sources, sourceFiles})
159 | expect(transformSpy).not.toHaveBeenCalled()
160 | transformSpy.mockRestore()
161 | })
162 |
163 | //////////////////// utils ////////////////////
164 |
165 | function fixturePath(name) {
166 | return path.join(__dirname, '__fixtures__', name)
167 | }
168 |
169 | function formatCSS(css) {
170 | return cssParser.stringify(cssParser.parse(css)).trim()
171 | }
172 |
173 | /*
174 | * This takes the results object and removes environment-specific
175 | * elements from the path.
176 | */
177 | function relativizePaths(filepath) {
178 | return filepath
179 | .replace(':/', ':\\')
180 | .replace(path.resolve(__dirname, '../../'), '')
181 | .replace(/\\/g, '/')
182 | }
183 |
184 | function trimAndNewline(string) {
185 | return `\n${string.trim()}\n`
186 | }
187 |
--------------------------------------------------------------------------------
/src/babel-plugin.js:
--------------------------------------------------------------------------------
1 | import * as glamor from 'glamor'
2 | import getLiteralizers from './get-literalizers'
3 |
4 | export default function(babel) {
5 | const {types: t} = babel
6 | const glamorousIdentifiers = new Set()
7 | return {
8 | name: 'glamorous-static',
9 | visitor: {
10 | ImportDeclaration(path) {
11 | const defaultSpecifierPath = path.get('specifiers')[0]
12 | if (
13 | path.node.source.value !== 'glamorous' ||
14 | !t.isImportDefaultSpecifier(defaultSpecifierPath)
15 | ) {
16 | return
17 | }
18 | const {node: {local: {name}}} = defaultSpecifierPath
19 | const {referencePaths} = path.scope.getBinding(name)
20 | referencePaths.forEach(reference => {
21 | glamorousIdentifiers.add(reference)
22 | })
23 | },
24 | VariableDeclarator(path) {
25 | const {node} = path
26 | if (!isRequireCall(node.init) || !t.isIdentifier(node.id)) {
27 | return
28 | }
29 | const {id: {name}} = node
30 | const binding = path.scope.getBinding(name)
31 | const {referencePaths} = binding
32 | referencePaths.forEach(reference => {
33 | glamorousIdentifiers.add(reference)
34 | })
35 | },
36 | Program: {
37 | exit(programPath, state) {
38 | const toLiteral = getLiteralizers(state)
39 | Array.from(glamorousIdentifiers).forEach(identifier => {
40 | const isGlamorousCall = looksLike(identifier, {
41 | parentPath: {
42 | type: type =>
43 | type === 'MemberExpression' || type === 'CallExpression',
44 | parentPath: {
45 | type: 'CallExpression',
46 | },
47 | },
48 | })
49 | if (!isGlamorousCall) {
50 | return
51 | }
52 | const callExpression = identifier.parentPath.parentPath
53 | const staticPaths = callExpression
54 | .get('arguments')
55 | .reduce(
56 | (paths, argPath) => paths.concat(getStaticPaths(argPath)),
57 | [],
58 | )
59 | .filter(Boolean)
60 | staticPaths.forEach(staticPath => {
61 | staticPath.replaceWith(t.stringLiteral(glamorize(staticPath)))
62 | })
63 |
64 | function glamorize(literalNodePath) {
65 | let obj
66 | const {code} = babel.transformFromAst(
67 | t.program([t.expressionStatement(toLiteral(literalNodePath))]),
68 | )
69 | // eslint-disable-next-line no-eval
70 | eval(`obj = ${code}`)
71 | const className = glamor.css(obj).toString()
72 | return className
73 | }
74 | })
75 |
76 | // babel utils
77 | function getStaticPaths(path) {
78 | const pathGetters = [
79 | getStaticObjectPaths,
80 | getStaticsInDynamic,
81 | getStaticReferences,
82 | ]
83 | return pathGetters.reduce((pathsAlreadyGotten, pathGetter) => {
84 | if (pathsAlreadyGotten) {
85 | return pathsAlreadyGotten
86 | }
87 | const paths = pathGetter(path)
88 | if (paths.length) {
89 | return paths
90 | }
91 | return null
92 | }, null)
93 | }
94 |
95 | function getStaticObjectPaths(path) {
96 | if (path.type !== 'ObjectExpression') {
97 | return []
98 | }
99 | return [path]
100 | }
101 |
102 | function getStaticsInDynamic(path) {
103 | const isSupportedFunction = looksLike(path, {
104 | node: {
105 | type: 'ArrowFunctionExpression',
106 | body: {
107 | type: 'ConditionalExpression',
108 | consequent: {type: 'ObjectExpression'},
109 | alternate: {type: 'ObjectExpression'},
110 | },
111 | },
112 | })
113 | if (!isSupportedFunction) {
114 | return []
115 | }
116 | const things = [
117 | ...getStaticPaths(path.get('body.consequent')),
118 | ...getStaticPaths(path.get('body.alternate')),
119 | ]
120 | return things
121 | }
122 |
123 | function getStaticReferences(path) {
124 | if (path.type !== 'Identifier') {
125 | return []
126 | }
127 | const binding = path.scope.getBinding(path.node.name)
128 | if (binding) {
129 | return getStaticObjectPaths(binding.path.get('init'))
130 | } else {
131 | return []
132 | }
133 | }
134 | },
135 | },
136 | },
137 | }
138 | }
139 |
140 | function isRequireCall(callExpression) {
141 | return looksLike(callExpression, {
142 | type: 'CallExpression',
143 | callee: {
144 | name: 'require',
145 | },
146 | arguments: args =>
147 | args.length === 1 && looksLike(args[0], {value: 'glamorous'}),
148 | })
149 | }
150 |
151 | // generic utils
152 | function looksLike(a, b) {
153 | return (
154 | a &&
155 | b &&
156 | Object.keys(b).every(bKey => {
157 | const bVal = b[bKey]
158 | const aVal = a[bKey]
159 | if (typeof bVal === 'function') {
160 | return bVal(aVal)
161 | }
162 | return isPrimitive(bVal) ? bVal === aVal : looksLike(aVal, bVal)
163 | })
164 | )
165 | }
166 |
167 | function isPrimitive(val) {
168 | // eslint-disable-next-line
169 | return val == null || /^[sbn]/.test(typeof val);
170 | }
171 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # css-in-js-precompiler
2 |
3 | Precompiles static CSS-in-JS objects to CSS strings
4 |
5 | **CURRENTLY A WORK IN PROGRESS**
6 |
7 |
27 |
28 | ## The problem
29 |
30 | You love the benefits of CSS-in-JS, but don't love some of the performance
31 | characteristics and trade-offs you have to make with regards to not using actual
32 | CSS files.
33 |
34 | ## This solution
35 |
36 | This module takes in your source code and gives you back the source code with
37 | the literal CSS-in-JS objects swapped for class names as well as the generated
38 | CSS. You can then use that to create a css file and deploy that.
39 |
40 | **It's still pretty early stages**
41 |
42 | ## Installation
43 |
44 |
53 |
54 | This is not yet published. But you can install it anyway with:
55 |
56 | ```
57 | npm install kentcdodds/css-in-js-precompiler
58 | cd node_modules/css-in-js-precompiler
59 | npm install
60 | npm start build
61 | ```
62 |
63 | And you should be able to use it now :)
64 |
65 | ## Usage
66 |
67 | This is still under development so the API and assumptions are going to change.
68 | But right now, we're naïvely assuming you're using `glamorous`. _But this
69 | solution will be modified to work with **any** CSS-in-JS library you're using_.
70 |
71 | ```javascript
72 | const precompileCSSInJS = require('css-in-js-precompiler')
73 | const options = {
74 | sources: [
75 | {
76 | code: `import glamorous from 'glamorous';\nglamorous.div({fontSize: 23})`,
77 | filename: '/some/path.js',
78 | babelOptions: {/* optional. Shallowly merges with the default babelOptions */}
79 | },
80 | ],
81 | }
82 |
83 | const result = precompileCSSInJS(options)
84 | result.transformed[0].code === `import glamorous from 'glamorous';\nglamorous.div("css-my79es");`
85 | result.transformed[0].map === ''
86 | result.css === '.css-my79es,[data-css-my79es]{font-size:23px;}'
87 | ```
88 |
89 | ### options
90 |
91 | #### `sources`
92 |
93 | This is an array of `SourceObjects` which will be used to determine what source
94 | to precompile and how. Here are the available properties on these objects:
95 |
96 | #### code
97 |
98 | This is the source code to actually precompile. If this is not provided, then
99 | the code will be derived from the `filename`.
100 |
101 | ##### filename
102 |
103 | This is a string path to the filename. If the `code` is not provided, this will
104 | be used to read the file. If this is not provided, then you will be unable to
105 | handle importing dynamic values from other files.
106 |
107 | #### babelOptions
108 |
109 | This is the same thing you would pass to `babel.transform` if you were calling
110 | it yourself. Read more [here](http://babeljs.io/docs/core-packages/#options).
111 | This will be shallowly merged with the default `babelOptions`. Currently
112 | (2017-05-03) the default babelOptions are:
113 |
114 | ```javascript
115 | {
116 | babelrc: false,
117 | sourceMaps: true,
118 | plugins: [/* our custom plugin to do this extraction */],
119 | parserOpts: {plugins: [/* all of them except estree */]},
120 | }
121 | ```
122 |
123 | This is shallowly merged, with the exception of `plugins`. You can specify any
124 | plugins you want and we'll make sure we always include our own plugin to do
125 | the precompiling. (You're welcome).
126 |
127 | ## Inspiration
128 |
129 | I started thinking about this around [here][inspiration-link].
130 |
131 | ## Other Solutions
132 |
133 | I'm not aware of any, if you are please [make a pull request][prs] and add it
134 | here!
135 |
136 | ## Contributors
137 |
138 | Thanks goes to these people ([emoji key][emojis]):
139 |
140 |
141 | | [
Kent C. Dodds](https://kentcdodds.com)
[💻](https://github.com/kentcdodds/css-in-js-precompiler/commits?author=kentcdodds) [📖](https://github.com/kentcdodds/css-in-js-precompiler/commits?author=kentcdodds) 🚇 [⚠️](https://github.com/kentcdodds/css-in-js-precompiler/commits?author=kentcdodds) |
142 | | :---: |
143 |
144 |
145 | This project follows the [all-contributors][all-contributors] specification.
146 | Contributions of any kind welcome!
147 |
148 | ## LICENSE
149 |
150 | MIT
151 |
152 | [npm]: https://www.npmjs.com/
153 | [node]: https://nodejs.org
154 | [build-badge]: https://img.shields.io/travis/kentcdodds/css-in-js-precompiler.svg?style=flat-square
155 | [build]: https://travis-ci.org/kentcdodds/css-in-js-precompiler
156 | [coverage-badge]: https://img.shields.io/codecov/c/github/kentcdodds/css-in-js-precompiler.svg?style=flat-square
157 | [coverage]: https://codecov.io/github/kentcdodds/css-in-js-precompiler
158 | [dependencyci-badge]: https://dependencyci.com/github/kentcdodds/css-in-js-precompiler/badge?style=flat-square
159 | [dependencyci]: https://dependencyci.com/github/kentcdodds/css-in-js-precompiler
160 | [version-badge]: https://img.shields.io/npm/v/css-in-js-precompiler.svg?style=flat-square
161 | [package]: https://www.npmjs.com/package/css-in-js-precompiler
162 | [downloads-badge]: https://img.shields.io/npm/dm/css-in-js-precompiler.svg?style=flat-square
163 | [npm-stat]: http://npm-stat.com/charts.html?package=css-in-js-precompiler&from=2016-04-01
164 | [license-badge]: https://img.shields.io/npm/l/css-in-js-precompiler.svg?style=flat-square
165 | [license]: https://github.com/kentcdodds/css-in-js-precompiler/blob/master/other/LICENSE
166 | [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
167 | [prs]: http://makeapullrequest.com
168 | [donate-badge]: https://img.shields.io/badge/$-support-green.svg?style=flat-square
169 | [donate]: http://kcd.im/donate
170 | [coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square
171 | [coc]: https://github.com/kentcdodds/css-in-js-precompiler/blob/master/other/CODE_OF_CONDUCT.md
172 | [roadmap-badge]: https://img.shields.io/badge/%F0%9F%93%94-roadmap-CD9523.svg?style=flat-square
173 | [roadmap]: https://github.com/kentcdodds/css-in-js-precompiler/blob/master/other/ROADMAP.md
174 | [examples-badge]: https://img.shields.io/badge/%F0%9F%92%A1-examples-8C8E93.svg?style=flat-square
175 | [examples]: https://github.com/kentcdodds/css-in-js-precompiler/blob/master/other/EXAMPLES.md
176 | [github-watch-badge]: https://img.shields.io/github/watchers/kentcdodds/css-in-js-precompiler.svg?style=social
177 | [github-watch]: https://github.com/kentcdodds/css-in-js-precompiler/watchers
178 | [github-star-badge]: https://img.shields.io/github/stars/kentcdodds/css-in-js-precompiler.svg?style=social
179 | [github-star]: https://github.com/kentcdodds/css-in-js-precompiler/stargazers
180 | [twitter]: https://twitter.com/intent/tweet?text=Check%20out%20css-in-js-precompiler!%20https://github.com/kentcdodds/css-in-js-precompiler%20%F0%9F%91%8D
181 | [twitter-badge]: https://img.shields.io/twitter/url/https/github.com/kentcdodds/css-in-js-precompiler.svg?style=social
182 | [emojis]: https://github.com/kentcdodds/all-contributors#emoji-key
183 | [all-contributors]: https://github.com/kentcdodds/all-contributors
184 | [inspiration-link]: https://github.com/paypal/glamorous/issues/43#issuecomment-294153104
185 |
--------------------------------------------------------------------------------