├── .gitignore ├── example ├── more.css └── style.css ├── .travis.yml ├── package.json ├── LICENSE ├── README.md ├── bin └── cli.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | example/style.js 3 | -------------------------------------------------------------------------------- /example/more.css: -------------------------------------------------------------------------------- 1 | .foobar { 2 | background: black; 3 | } 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 0.10 5 | - 0.12 6 | 7 | # Use container-based Travis infrastructure. 8 | # See: http://docs.travis-ci.com/user/workers/container-based-infrastructure/ 9 | sudo: false 10 | 11 | branches: 12 | only: 13 | - master 14 | 15 | script: 16 | # Build environment. 17 | - node --version 18 | - npm --version 19 | # Simple execution tests. 20 | - bin/cli.js < example/style.css > example/style.js 21 | - cat example/style.css | bin/cli.js > example/style.js 22 | - cat example/style.css | bin/cli.js example/style.js 23 | - bin/cli.js --input example/style.css > example/style.js 24 | - bin/cli.js --input example/style.css example/style.js 25 | - bin/cli.js --input example/style.css --input example/more.css example/style.js 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "css-to-radium", 3 | "version": "1.0.2", 4 | "description": "Radium migration CLI, converts CSS to Radium-compatible JS objects", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/FormidableLabs/css-to-radium.git" 9 | }, 10 | "keywords": [ 11 | "radium", 12 | "react", 13 | "css" 14 | ], 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/FormidableLabs/css-to-radium/issues" 18 | }, 19 | "homepage": "https://github.com/FormidableLabs/css-to-radium", 20 | "dependencies": { 21 | "camelcase": "^1.0.2", 22 | "kat": "^0.3.0", 23 | "lodash": "^3.3.1", 24 | "minimist": "^1.1.0", 25 | "postcss": "^4.0.6", 26 | "stringify-object": "^1.0.0", 27 | "through2": "^0.6.3" 28 | }, 29 | "bin": { 30 | "css-to-radium": "bin/cli.js" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /example/style.css: -------------------------------------------------------------------------------- 1 | .btn { 2 | background: #ccc; 3 | padding: 10px 20px; 4 | font-size: 16px; 5 | 6 | -webkit-transition: none; 7 | -moz-transition: none; 8 | -ms-transition: none; 9 | -o-transition: none; 10 | transition: none; 11 | 12 | flex: 0; 13 | 14 | line-height: 2.4; 15 | } 16 | 17 | .btn-primary { 18 | background: orange; 19 | color: #fff; 20 | -webkit-font-smoothing: antialiased; 21 | } 22 | 23 | .btn { 24 | margin: 12px; 25 | } 26 | 27 | .btn { 28 | color: red; 29 | } 30 | 31 | .btn { 32 | background: blue; 33 | } 34 | 35 | @media only screen and (min-width: 1200px) { 36 | body { 37 | background: green; 38 | } 39 | 40 | .btn { 41 | padding: 24; 42 | } 43 | } 44 | 45 | @media (max-width: 600px) { 46 | body { 47 | background: red; 48 | } 49 | } 50 | 51 | @media only screen and (min-width: 1200px) { 52 | .foobar { 53 | color: orange; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Formidable Labs 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSS to Radium 2 | 3 | A CLI utility to convert the contents of a CSS file to a Radium-compatible JS object. 4 | 5 | ## Installation 6 | 7 | ``` 8 | $ npm install css-to-radium 9 | ``` 10 | 11 | ## Usage 12 | 13 | By default, `css-to-radium` works with `stdin` and `stdout`: 14 | 15 | ``` 16 | $ css-to-radium < example/style.css > example/style.js 17 | ``` 18 | 19 | You can also target a source file directly with the `--input` flag and return a converted file by specifying the target path after any options: 20 | 21 | ``` 22 | $ css-to-radium --input style.css style.js 23 | ``` 24 | 25 | Or you can mix and match the two: 26 | 27 | ``` 28 | $ css-to-radium --input example/style.css > example/style.js 29 | ``` 30 | 31 | It turns a file like this: 32 | 33 | ```css 34 | /* style.css */ 35 | .btn { 36 | background: #ccc; 37 | padding: 10px; 38 | font-size: 16px; 39 | } 40 | 41 | .btn-primary { 42 | background: orange; 43 | color: #fff; 44 | } 45 | ``` 46 | 47 | into one like this: 48 | 49 | ```js 50 | // style.js 51 | 52 | module.exports = { 53 | ".btn": { 54 | background: "#ccc", 55 | padding: 10, 56 | fontSize: 16 57 | }, 58 | ".btn-primary": { 59 | background: "orange", 60 | color: "white" 61 | } 62 | } 63 | ``` 64 | 65 | ## Options 66 | 67 | ### input 68 | 69 | `--input [inputSrc]` 70 | 71 | A source file to use as input. To process multiple files at once, include this 72 | flag multiple times: 73 | 74 | ``` 75 | $ css-to-radium --input style.css --input more.css 76 | ``` 77 | 78 | ### quote 79 | 80 | `--quote [type]` 81 | 82 | Type of quote used in generated JS. Defaults to `single`. 83 | 84 | ``` 85 | css-to-radium style.css style.js --quote double 86 | ``` 87 | 88 | ### indentSize 89 | 90 | `--indentSize [int]` 91 | 92 | Number of space characters to use for indents in generated JS. If this flag is not included, defaults to a single tab character (`\t`). 93 | 94 | ``` 95 | css-to-radium style.css style.js --indentSize=2 96 | ``` 97 | 98 | ## Webpack 99 | 100 | Using Webpack? Check out [radium-loader](https://github.com/dminkovsky/radium-loader). 101 | -------------------------------------------------------------------------------- /bin/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs'); 4 | var through2 = require('through2'); 5 | var minimist = require('minimist'); 6 | var stringifyObject = require('stringify-object'); 7 | var Kat = require('kat'); 8 | 9 | var convertCss = require('../index.js'); 10 | 11 | // ---------------------------------------------------------------------------- 12 | // Arguments 13 | // ---------------------------------------------------------------------------- 14 | var cliArgs = minimist(process.argv.slice(2)); 15 | 16 | var inputs = cliArgs.input; 17 | 18 | if (inputs) { 19 | inputs = Array.isArray(inputs) ? inputs : [ inputs ]; 20 | } 21 | 22 | // Index 0 (file path) else stdin output arguments in remainder. 23 | if (cliArgs._.length > 1) { 24 | throw new Error('Must have 0 or 1 output paths specified'); 25 | } 26 | 27 | var output = cliArgs._[0] || null; // Remaining argument. 28 | 29 | // ---------------------------------------------------------------------------- 30 | // Helpers 31 | // ---------------------------------------------------------------------------- 32 | var stringifyResult = function (data) { 33 | return stringifyObject(data, { 34 | singleQuotes: cliArgs.quote !== 'double', 35 | indent: cliArgs.indentSize ? (new Array(cliArgs.indentSize)).join(' ') : '\t' 36 | }); 37 | }; 38 | 39 | // ---------------------------------------------------------------------------- 40 | // Streams 41 | // ---------------------------------------------------------------------------- 42 | // Converter stream. 43 | var _buffer = []; 44 | var convertStream = through2(function (chunk, enc, callback) { 45 | _buffer.push(chunk.toString('utf8')); 46 | callback(); 47 | }, function (callback) { 48 | var data = _buffer.join(''); 49 | var cssObj = convertCss(data); 50 | var converted = new Buffer(stringifyResult(cssObj)); 51 | this.push(converted); 52 | callback(); 53 | }); 54 | 55 | // Encoding. 56 | process.stdin.setEncoding('utf8'); 57 | 58 | // Input: Files or stdin. 59 | var inputStream = process.stdin; 60 | 61 | if (inputs) { 62 | inputStream = new Kat(); 63 | inputs.forEach(function (input) { 64 | inputStream.add(input); 65 | }); 66 | } 67 | 68 | // Output: File or stdout. 69 | var outputStream = output ? 70 | fs.createWriteStream(output) : 71 | process.stdout; 72 | 73 | // Pipe it! 74 | inputStream 75 | .pipe(convertStream) 76 | .pipe(outputStream); 77 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var postcss = require('postcss'); 3 | var camelCase = require('camelcase'); 4 | 5 | var sanitizeSelector = function (selector) { 6 | return selector.replace(/\n/gi, ' '); 7 | }; 8 | 9 | var convertValue = function (value) { 10 | var result = value; 11 | var resultNumber = Number(result); 12 | 13 | // Handle single pixel values (font-size: 16px) 14 | if (result.indexOf(' ') === -1 && result.indexOf('px') !== -1) { 15 | result = parseInt(result.replace('px', ''), 10); 16 | // Handle numeric values 17 | } else if (_.isNaN(resultNumber) === false) { 18 | result = resultNumber; 19 | } 20 | 21 | return result; 22 | }; 23 | 24 | var convertProp = function (prop) { 25 | var result = camelCase(prop); 26 | 27 | // Handle vendor prefixes 28 | if (prop.indexOf('-webkit') === 0) { 29 | result = result.replace('webkit', 'Webkit'); 30 | } else if (prop.indexOf('-moz') === 0) { 31 | result = result.replace('moz', 'Moz'); 32 | } else if (prop.indexOf('-o') === 0) { 33 | result = result.replace('o', 'O'); 34 | } 35 | 36 | return camelCase(result); 37 | }; 38 | 39 | var convertDecl = function (decl) { 40 | return { 41 | property: convertProp(decl.prop), 42 | value: convertValue(decl.value) 43 | }; 44 | }; 45 | 46 | var convertRule = function (rule) { 47 | var returnObj = {}; 48 | var selector = sanitizeSelector(rule.selector); 49 | 50 | returnObj[selector] = _.transform(rule.nodes, function (convertedDecls, decl) { 51 | if (decl.type === 'decl') { 52 | var convertedDecl = convertDecl(decl); 53 | 54 | convertedDecls[convertedDecl.property] = convertedDecl.value; 55 | } 56 | }, {}) 57 | 58 | return returnObj; 59 | }; 60 | 61 | var convertMedia = function (media) { 62 | var returnObj = { mediaQueries: {} }; 63 | returnObj.mediaQueries[media.params] = {}; 64 | 65 | _.forEach(media.nodes, function (node) { 66 | if (node.type !== 'rule') { 67 | return; 68 | } 69 | 70 | _.merge(returnObj.mediaQueries[media.params], convertRule(node)); 71 | }); 72 | 73 | return returnObj; 74 | }; 75 | 76 | var convertCss = function (sourceCss) { 77 | var source = postcss.parse(sourceCss).nodes; 78 | 79 | return _.transform(source, function (convertedObj, node) { 80 | node.type === 'rule' && _.merge(convertedObj, convertRule(node)); 81 | node.name === 'media' && _.merge(convertedObj, convertMedia(node)); 82 | }, {}); 83 | }; 84 | 85 | module.exports = convertCss; 86 | --------------------------------------------------------------------------------