Skip to content

Commit 609c251

Browse files
committed
feat: initial commit
0 parents  commit 609c251

14 files changed

+311
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
lib/
3+
examples/webpack/bundle
4+
npm-debug.log

.npmignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
src/
2+
npm-debug.log
3+
.nyc_output/
4+
coverage/

Makefile

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
.DELETE_ON_ERROR:
2+
3+
BIN = ./node_modules/.bin
4+
TESTS = $(shell find src -path '*/__tests__/*-test.js')
5+
FIXTURES = $(shell find src -path '*/__tests__/*-fixture/*.js')
6+
SRC = $(filter-out $(TESTS) $(FIXTURES), $(shell find src -name '*.js'))
7+
LIB = $(SRC:src/%=lib/%)
8+
MOCHA_OPTS = -R dot --require babel-core/register
9+
10+
build::
11+
@$(MAKE) -j 8 $(LIB)
12+
13+
lint::
14+
@$(BIN)/eslint src
15+
16+
check::
17+
@$(BIN)/flow --show-all-errors src
18+
19+
test::
20+
@$(BIN)/mocha $(MOCHA_OPTS) $(TESTS)
21+
22+
ci::
23+
@$(BIN)/mocha $(MOCHA_OPTS) --watch --watch-extensions json,md $(TESTS)
24+
25+
version-major version-minor version-patch:: lint test
26+
@npm version $(@:version-%=%)
27+
28+
push::
29+
@git push --tags origin HEAD:master
30+
31+
clean::
32+
@rm -rf lib
33+
34+
lib/%.js: src/%.js
35+
@echo "Building $<"
36+
@mkdir -p $(@D)
37+
@$(BIN)/babel $(BABEL_OPTIONS) -o $@ $<

README.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# React CSS components
2+
3+
React CSS components is a CSS based module format to define styled React DOM
4+
components.
5+
6+
Example `styles.react.css`:
7+
8+
Label {
9+
color: red;
10+
}
11+
12+
Then:
13+
14+
import {Label} from './styles.react.css'
15+
16+
<Label /> // => <div className="<autogenerated classname>">...</div>

examples/webpack/.babelrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["prometheusresearch"]
3+
}

examples/webpack/index.html

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<div id="main">
2+
<script src="bundle/bundle.js"></script>

examples/webpack/index.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import {Header, Content, Root} from './styles.react.css';
4+
5+
ReactDOM.render(
6+
<Root>
7+
<Header>Header</Header>
8+
<Content>Content</Content>
9+
</Root>,
10+
document.getElementById('main')
11+
);

examples/webpack/package.json

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "react-css-components-example-webpack",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"scripts": {
6+
"build": "webpack",
7+
"watch": "webpack -w"
8+
},
9+
"dependencies": {
10+
"babel-loader": "^6.2.4",
11+
"css-loader": "^0.23.1",
12+
"react": "^15.0.0",
13+
"react-css-components": "../../",
14+
"react-dom": "^15.0.0",
15+
"style-loader": "^0.13.1",
16+
"webpack": "^1.12.14"
17+
}
18+
}

examples/webpack/styles.react.css

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Root {
2+
padding: 10px;
3+
font-family: Menlo;
4+
}
5+
6+
Header {
7+
font-size: 200%;
8+
font-weight: bold;
9+
color: black;
10+
}
11+
12+
Content {
13+
color: #444;
14+
}

examples/webpack/webpack.config.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
var path = require('path');
2+
3+
module.exports = {
4+
entry: './index.js',
5+
output: {
6+
path: 'bundle',
7+
filename: 'bundle.js',
8+
},
9+
devtool: 'cheap-eval-source-map',
10+
resolve: {
11+
fallback: path.join(__dirname, 'node_modules'),
12+
},
13+
resolveLoader: {
14+
fallback: path.join(__dirname, 'node_modules'),
15+
},
16+
module: {
17+
loaders: [
18+
{
19+
test: /\.react.css$/,
20+
loader: 'babel-loader!react-css-components/webpack',
21+
},
22+
{
23+
test: /\.js$/,
24+
exclude: /node_modules/,
25+
loader: 'babel-loader',
26+
}
27+
]
28+
}
29+
};

package.json

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "react-css-components",
3+
"version": "0.1.0",
4+
"description": "Define styled React components using CSS based module format",
5+
"main": "lib/index.js",
6+
"babel": {
7+
"presets": [
8+
"prometheusresearch"
9+
]
10+
},
11+
"eslintConfig": {
12+
"extends": "prometheusresearch"
13+
},
14+
"scripts": {
15+
"test": "make test"
16+
},
17+
"repository": {
18+
"type": "git",
19+
"url": "git+https://github.com/andreypopp/react-css-components.git"
20+
},
21+
"keywords": [
22+
"react",
23+
"css",
24+
"postcss-plugin",
25+
"webpack"
26+
],
27+
"author": "Andrey Popp <8mayday@gmail.com>",
28+
"license": "MIT",
29+
"bugs": {
30+
"url": "https://github.com/andreypopp/react-css-components/issues"
31+
},
32+
"homepage": "https://github.com/andreypopp/react-css-components#readme",
33+
"dependencies": {
34+
"babel-types": "^6.7.2",
35+
"loader-utils": "^0.2.14",
36+
"postcss": "^5.0.19"
37+
},
38+
"devDependencies": {
39+
"babel-cli": "^6.7.5",
40+
"babel-core": "^6.7.6",
41+
"babel-preset-prometheusresearch": "^0.1.0",
42+
"eslint": "^2.7.0",
43+
"eslint-config-prometheusresearch": "^0.2.0",
44+
"mocha": "^2.4.5"
45+
}
46+
}

src/__tests__/index-test.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {render} from '../';
2+
3+
describe('react-css-components', function() {
4+
5+
it('renders react compoents', function() {
6+
let {js, css} = render(`
7+
8+
Label {
9+
color: red;
10+
}
11+
`, {requestCSS: 'react-css-components?css!styles.react.css'});
12+
console.log('--- js');
13+
console.log(js);
14+
console.log('--- css');
15+
console.log(css);
16+
});
17+
18+
19+
it('renders react components with pseudoclasses', function() {
20+
let {js, css} = render(`
21+
22+
Label {
23+
color: red;
24+
:hover {
25+
color: white;
26+
}
27+
}
28+
`, {requestCSS: 'react-css-components?css!styles.react.css'});
29+
console.log('--- js');
30+
console.log(js);
31+
console.log('--- css');
32+
console.log(css);
33+
});
34+
});

src/index.js

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* @copyright 2016-present, React CSS Components team
3+
* @flow
4+
*/
5+
6+
import * as types from 'babel-types';
7+
import * as postcss from 'postcss';
8+
import * as LoaderUtils from 'loader-utils';
9+
import generate from 'babel-generator';
10+
11+
const LOADER = require.resolve('../webpack');
12+
13+
export type RenderConfig = {
14+
requestCSS: string;
15+
};
16+
17+
export function loader(source: string): string {
18+
this.cacheable();
19+
let query = LoaderUtils.parseQuery(this.query);
20+
if (query.css) {
21+
let result = renderToCSS(source);
22+
return result;
23+
} else {
24+
let requestCSS = `!!style-loader!css-loader?module!${LOADER}?css!${this.resource}`;
25+
let result = renderToJS(source, {requestCSS});
26+
return result;
27+
}
28+
}
29+
30+
export function render(source: string, config: RenderConfig = {}): {js: string; css: string}{
31+
let js = renderToJS(source, {requestCSS: config.requestCSS});
32+
let css = renderToCSS(source);
33+
return {js, css};
34+
}
35+
36+
function renderToCSS(source: string, config: RenderConfig): string {
37+
let root = postcss.parse(source);
38+
root.walkRules(node => {
39+
let cssNode = node.clone();
40+
cssNode.selector = `:local(.${node.selector})`;
41+
node.replaceWith(cssNode);
42+
});
43+
return root.toString();
44+
}
45+
46+
function renderToJS(source: string, config: RenderConfig): string {
47+
let root = postcss.parse(source);
48+
let statements = [];
49+
root.walkRules(node => {
50+
statements.push(exportComponent(node.selector, 'div', node.selector));
51+
});
52+
statements.unshift(
53+
types.importDeclaration(
54+
[types.importDefaultSpecifier(types.identifier('styles'))],
55+
types.stringLiteral(config.requestCSS)
56+
)
57+
);
58+
statements.unshift(
59+
types.importDeclaration(
60+
[types.importDefaultSpecifier(types.identifier('React'))],
61+
types.stringLiteral('react')
62+
)
63+
);
64+
return generate(types.program(statements)).code;
65+
}
66+
67+
function exportComponent(name: string, component: string, className: string) {
68+
let propsNode = types.objectExpression([
69+
types.spreadProperty(types.identifier('props')),
70+
types.objectProperty(
71+
types.identifier('className'),
72+
types.memberExpression(types.identifier('styles'), types.identifier(name))
73+
)
74+
]);
75+
let elementNode = types.callExpression(
76+
types.memberExpression(
77+
types.identifier('React'),
78+
types.identifier('createElement')),
79+
[types.stringLiteral(component), propsNode]
80+
);
81+
let componentNode = types.functionDeclaration(
82+
types.identifier(name),
83+
[types.identifier('props')],
84+
types.blockStatement([types.returnStatement(elementNode)])
85+
);
86+
return types.exportNamedDeclaration(componentNode, [], null);
87+
}

webpack.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* @copyright 2016-present, React CSS Components team
3+
* @flow
4+
*/
5+
6+
module.exports = require('./lib/index').loader;

0 commit comments

Comments
 (0)