├── .gitignore ├── .npmignore ├── .travis.yml ├── .bin ├── clean ├── build ├── push └── test ├── package.json ├── README.md ├── index.js └── index-test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | src 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "iojs" 4 | -------------------------------------------------------------------------------- /.bin/clean: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | find . -name '*.js' -type f -not -path './node_modules/*' -not -path '.bin/*' -exec rm {} \; -print 4 | -------------------------------------------------------------------------------- /.bin/build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | IGNORE='node_modules/**,__tests__/**'; 4 | 5 | ./node_modules/.bin/babel --extensions .es6 --ignore $IGNORE --presets es2015 --out-dir . . 6 | -------------------------------------------------------------------------------- /.bin/push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | read -p "New version (enter nothing for patch): " VERSION; 4 | VERSION=${VERSION:-patch}; 5 | 6 | git pull && 7 | git push && 8 | npm test && 9 | npm version $VERSION && 10 | npm publish && 11 | git push --follow-tags 12 | -------------------------------------------------------------------------------- /.bin/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | where=${1:-.} 4 | tests=$1 5 | 6 | if [ -d $where ]; then 7 | tests=`find $where -type f -name '*-test.*' -not -path './node_modules/*'` 8 | fi 9 | 10 | ln -s `pwd` node_modules/babel-plugin-react-autoprefix 11 | 12 | ./node_modules/.bin/babel-node $2 $tests 13 | 14 | rm node_modules/babel-plugin-react-autoprefix 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-react-autoprefix", 3 | "description": "Adds vendor prefixes to inline styles in React elements", 4 | "repository": "UXtemple/babel-plugin-react-prefix-styles", 5 | "license": "MIT", 6 | "main": "index.js", 7 | "devDependencies": { 8 | "babel-core": "^6.10.4", 9 | "babel-plugin-transform-object-rest-spread": "^6.8.0", 10 | "babel-preset-react": "^6.11.1", 11 | "tape": "^4.0.1" 12 | }, 13 | "scripts": { 14 | "push": ".bin/push", 15 | "test": "node index-test.js" 16 | }, 17 | "keywords": [ 18 | "babel-plugin", 19 | "react", 20 | "auto", 21 | "prefix", 22 | "vendor", 23 | "style", 24 | "inline", 25 | "styles" 26 | ], 27 | "dependencies": { 28 | "autoprefix": "^1.0.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # babel-plugin-react-autoprefix 2 | 3 | Adds vendor prefixes to inline styles in React elements through 4 | [autoprefix](https://github.com/uxtemple/autoprefix). 5 | 6 | [](https://travis-ci.org/UXtemple/babel-plugin-react-autoprefix) 7 | 8 | ## Installation 9 | 10 | ```sh 11 | $ npm install babel-plugin-react-autoprefix 12 | ``` 13 | 14 | ## Usage 15 | 16 | ### Via `.babelrc` (Recommended) 17 | 18 | **.babelrc** 19 | 20 | ```js 21 | { 22 | "plugins": ["react-autoprefix"] 23 | } 24 | ``` 25 | 26 | ### Via CLI 27 | 28 | ```sh 29 | $ babel --plugins react-autoprefix script.js 30 | ``` 31 | 32 | ### Via Node API 33 | 34 | ```javascript 35 | require("babel-core").transform("code", { 36 | plugins: ["react-autoprefix"] 37 | }); 38 | ``` 39 | 40 | ## Changing the matching pattern 41 | 42 | The plugin will match `style` props by default. If you want to match other props, like 43 | `styleSomething`, you can use the plugin's `matcher` option, e.g. in `.babelrc`: 44 | 45 | ```js 46 | { 47 | "plugins": ["react-autoprefix", { matcher: /^style.*$/ }] 48 | } 49 | ``` 50 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const autoprefix = require('autoprefix') 2 | 3 | const cache = {} 4 | function mAutoprefix(name, value) { 5 | const key = `${name}${value}`; 6 | return cache[key] || (cache[key] = autoprefix({ [name]: value })); 7 | } 8 | 9 | function prefix(t, path, node) { 10 | let binding 11 | switch (node.type) { 12 | // test: variable outside 13 | case 'Identifier': 14 | binding = path.scope.getBinding(node.name) 15 | 16 | if (binding && binding.path.node.type === 'VariableDeclarator') { 17 | // we've already prefixed this object 18 | if (binding.path.data.autoprefixed) return 19 | // track that we've autoprefixed this so we don't do it multiple times 20 | binding.path.data.autoprefixed = true 21 | 22 | switch (binding.path.node.init.type) { 23 | // test: object spread 24 | // test: more advanced object spread 25 | case 'CallExpression': 26 | for (const arg of binding.path.node.init.arguments) { 27 | // binding.path.node.init.arguments.forEarch(arg => { 28 | switch (arg.type) { 29 | case 'Identifier': 30 | prefix(t, path, path.scope.getBinding(arg.name).identifier) 31 | break 32 | 33 | case 'ObjectExpression': 34 | prefix(t, path, arg) 35 | break 36 | 37 | default: 38 | break 39 | } 40 | } 41 | break 42 | 43 | case 'ObjectExpression': 44 | prefix(t, path, binding.path.node.init) 45 | break 46 | 47 | default: 48 | break 49 | } 50 | } 51 | break 52 | 53 | // test: variable outside, key in object 54 | case 'MemberExpression': 55 | binding = path.scope.getBinding(node.object.name) 56 | if (binding && binding.path.node.type === 'VariableDeclarator' && binding.path.node.init.properties) { 57 | // we've already prefixed this object 58 | if (binding.path.data.autoprefixed) return 59 | // track that we've autoprefixed this so we don't do it multiple times 60 | binding.path.data.autoprefixed = true 61 | 62 | const subProperty = binding.path.node.init.properties.find(p => p.key.name === node.property.name) 63 | prefix(t, path, subProperty.value) 64 | } 65 | break 66 | 67 | // test: inline style 68 | case 'ObjectExpression': 69 | const nextProperties = [] 70 | 71 | node.properties.forEach(prop => { 72 | // test: spread inline 73 | if (prop.type === 'SpreadProperty') { 74 | prefix(t, path, prop.argument) 75 | nextProperties.push(prop) 76 | } else { 77 | // we don't process computed properties 78 | if (prop.computed || !t.isLiteral(prop.value)) { 79 | nextProperties.push(prop) 80 | return 81 | } 82 | 83 | // // ensure that the value is a string 84 | // if (!t.isLiteral(prop.value)) return 85 | 86 | // register property as one we'll try and autoprefix 87 | const object = mAutoprefix(prop.key.name, prop.value.value) 88 | 89 | for (const key in object) { 90 | // make sure the prefixed value produces valid CSS at all times. 91 | const value = Array.isArray(object[key]) ? object[key].join(`;${key}:`) : object[key] 92 | 93 | nextProperties.push( 94 | t.objectProperty( 95 | t.stringLiteral(key), 96 | t.valueToNode(value) 97 | ) 98 | ) 99 | } 100 | } 101 | }) 102 | 103 | node.properties = nextProperties 104 | break 105 | 106 | default: break 107 | } 108 | } 109 | 110 | module.exports = ({ types: t }) => ({ 111 | visitor: { 112 | JSXAttribute(path, state) { 113 | const prop = path.node.name.name 114 | if (state.opts.matcher) { 115 | if (!state.opts.matcher.test(prop)) return 116 | } else if (prop !== 'style') { 117 | return 118 | } 119 | 120 | // verify this is an object as it's the only type we take 121 | if (!t.isJSXExpressionContainer(path.node.value)) return 122 | 123 | // we've already prefixed this object 124 | if (path.data.autoprefixed) return 125 | // track that we've autoprefixed this so we don't do it multiple times 126 | path.data.autoprefixed = true 127 | 128 | prefix(t, path, path.node.value.expression) 129 | } 130 | } 131 | }) 132 | -------------------------------------------------------------------------------- /index-test.js: -------------------------------------------------------------------------------- 1 | const { transform } = require('babel-core') 2 | const autoprefix = require('./index') 3 | const objectRestSpread = require('babel-plugin-transform-object-rest-spread') 4 | const test = require('tape') 5 | 6 | const FIXTURES = [{ 7 | name: 'anything', 8 | src: '
', 9 | out: `React.createElement("div",{style:"anything"});` 10 | }, { 11 | name: 'inline style', 12 | src: '', 13 | out: `React.createElement("div",{style:{"WebkitBoxAlign":"center","msFlexAlign":"center","alignItems":"center","display":"-webkit-box;display:-ms-flexbox;display:flex","width":100}});` 14 | }, { 15 | name: 'variable outside', 16 | src: 'const from={alignItems: "center", display: "flex", width: 100};', 17 | out: 'const from={"WebkitBoxAlign":"center","msFlexAlign":"center","alignItems":"center","display":"-webkit-box;display:-ms-flexbox;display:flex","width":100};React.createElement("div",{style:from});' 18 | }, { 19 | name: 'variable outside, key in object', 20 | src: `const style={stuff: {alignItems: "center", display: "flex", width: 100}};`, 21 | out: 'const style={stuff:{"WebkitBoxAlign":"center","msFlexAlign":"center","alignItems":"center","display":"-webkit-box;display:-ms-flexbox;display:flex","width":100}};React.createElement("div",{style:style.stuff});' 22 | // }, { 23 | // name: 'object shorthand', 24 | // src: `const display = "flex"; const from={alignItems: "center", display, width: 100};`, 25 | // out: '...' 26 | }, { 27 | name: 'object spread', 28 | src: `const from={alignItems: "center", width: 100};const finalStyle={...from, display: "flex"};`, 29 | out: 'var _extends=Object.assign||function(target){for(var i=1;i