├── .babelrc ├── .gitignore ├── README.md ├── index.js ├── lib ├── flex.js └── flexbox-props.js ├── package.json ├── src ├── flex.jsx └── flexbox-props.js ├── test ├── chessboard.jsx ├── index.html ├── main.js ├── ref.html └── test.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "optional": [ 3 | "es7.decorators", 4 | "es7.classProperties", 5 | "es7.objectRestSpread", 6 | "runtime" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This makes use of parent context, currently only found on react >= 0.14 in [react master](https://github.com/facebook/react). 2 | 3 | To install from react master branch: 4 | ``` 5 | git clone https://github.com/facebook/react.git 6 | cd react 7 | npm run build 8 | cd npm-react 9 | npm link 10 | ``` 11 | 12 | Then in your project folder: 13 | ``` 14 | npm link react 15 | ``` 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/flex.js'); 2 | -------------------------------------------------------------------------------- /lib/flex.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _inherits = require('babel-runtime/helpers/inherits')['default']; 4 | 5 | var _get = require('babel-runtime/helpers/get')['default']; 6 | 7 | var _createClass = require('babel-runtime/helpers/create-class')['default']; 8 | 9 | var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default']; 10 | 11 | var _extends = require('babel-runtime/helpers/extends')['default']; 12 | 13 | var _Object$assign = require('babel-runtime/core-js/object/assign')['default']; 14 | 15 | var _Object$keys = require('babel-runtime/core-js/object/keys')['default']; 16 | 17 | var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default']; 18 | 19 | Object.defineProperty(exports, '__esModule', { 20 | value: true 21 | }); 22 | 23 | var _react = require('react'); 24 | 25 | var _react2 = _interopRequireDefault(_react); 26 | 27 | var _wolfy87Eventemitter = require('wolfy87-eventemitter'); 28 | 29 | var _wolfy87Eventemitter2 = _interopRequireDefault(_wolfy87Eventemitter); 30 | 31 | var _cssLayout = require('css-layout'); 32 | 33 | var _cssLayout2 = _interopRequireDefault(_cssLayout); 34 | 35 | var _flexboxProps = require('./flexbox-props'); 36 | 37 | var _flexboxProps2 = _interopRequireDefault(_flexboxProps); 38 | 39 | var Component = _react2['default'].Component; 40 | 41 | function _setStyle(style, styles) { 42 | if (style === undefined) style = {}; 43 | var path = arguments[2] === undefined ? [] : arguments[2]; 44 | 45 | if (styles.style === undefined) { 46 | styles.style = style; 47 | } else { 48 | var childStyle = { style: style, children: [] }; 49 | styles.children.push(childStyle); 50 | path.push(styles.children.length - 1); 51 | 52 | styles = childStyle; 53 | } 54 | 55 | return { 56 | path: path.slice(), 57 | setStyle: function setStyle(childStyle) { 58 | return _setStyle(childStyle, styles, path.slice()); 59 | } 60 | }; 61 | } 62 | 63 | var FlexContext = (function (_Component) { 64 | function FlexContext(props, context) { 65 | var _this = this; 66 | 67 | _classCallCheck(this, FlexContext); 68 | 69 | _get(Object.getPrototypeOf(FlexContext.prototype), 'constructor', this).call(this, props); 70 | 71 | this.deregister = function (cb) { 72 | _this.layoutNotifier.removeListener('layout-update', cb); 73 | }; 74 | 75 | this.waitForLayoutCalculation = function (cb) { 76 | _this.layoutNotifier.once('layout-update', cb); 77 | }; 78 | 79 | this.layoutNotifier = new _wolfy87Eventemitter2['default'](); 80 | 81 | this.stylesRoot = { children: [] }; 82 | this.styleTools = {}; 83 | } 84 | 85 | _inherits(FlexContext, _Component); 86 | 87 | _createClass(FlexContext, [{ 88 | key: 'getChildContext', 89 | value: function getChildContext() { 90 | return { 91 | styleTools: this.styleTools, 92 | waitForLayoutCalculation: this.waitForLayoutCalculation, 93 | deregister: this.deregister 94 | }; 95 | } 96 | }, { 97 | key: 'render', 98 | value: function render() { 99 | return _react2['default'].createElement( 100 | 'g', 101 | null, 102 | this.props.children 103 | ); 104 | } 105 | }, { 106 | key: 'startNewStyleTree', 107 | value: function startNewStyleTree() { 108 | this.stylesRoot = { children: [] }; 109 | 110 | var _setStyle2 = _setStyle(undefined, this.stylesRoot); 111 | 112 | var layoutFunc = _setStyle2.setStyle; 113 | 114 | this.styleTools.setStyle = layoutFunc; 115 | } 116 | }, { 117 | key: 'computeLayoutAndBroadcastResults', 118 | value: function computeLayoutAndBroadcastResults() { 119 | var flexLayout = (0, _cssLayout2['default'])(this.stylesRoot); 120 | this.layoutNotifier.emit('layout-update', flexLayout); 121 | } 122 | }, { 123 | key: 'componentWillMount', 124 | value: function componentWillMount() { 125 | this.startNewStyleTree(); 126 | } 127 | }, { 128 | key: 'componentDidMount', 129 | value: function componentDidMount() { 130 | this.computeLayoutAndBroadcastResults(); 131 | } 132 | }, { 133 | key: 'componentWillUpdate', 134 | value: function componentWillUpdate() { 135 | this.startNewStyleTree(); 136 | } 137 | }, { 138 | key: 'componentDidUpdate', 139 | value: function componentDidUpdate() { 140 | this.computeLayoutAndBroadcastResults(); 141 | } 142 | }], [{ 143 | key: 'childContextTypes', 144 | value: { 145 | styleTools: _react2['default'].PropTypes.object.isRequired, 146 | waitForLayoutCalculation: _react2['default'].PropTypes.func.isRequired, 147 | deregister: _react2['default'].PropTypes.func.isRequired 148 | }, 149 | enumerable: true 150 | }]); 151 | 152 | return FlexContext; 153 | })(Component); 154 | 155 | exports.FlexContext = FlexContext; 156 | var FlexBox = function FlexBox(Composed) { 157 | var componentStyles = arguments[1] === undefined ? {} : arguments[1]; 158 | return (function (_Component2) { 159 | var _class = function _class(props, context) { 160 | var _this2 = this; 161 | 162 | _classCallCheck(this, _class); 163 | 164 | _get(Object.getPrototypeOf(_class.prototype), 'constructor', this).call(this, props); 165 | 166 | this.handleLayoutCalculation = function (layout) { 167 | _this2.setState({ layout: _this2.getMyLayout(layout) }); 168 | }; 169 | 170 | var style = _Object$assign(componentStyles, props.style); 171 | 172 | var _partitionStyles = partitionStyles(style); 173 | 174 | var svgStyles = _partitionStyles.svgStyles; 175 | var flexStyles = _partitionStyles.flexStyles; 176 | 177 | this.flexStyles = flexStyles; 178 | this.styleTools = {}; 179 | 180 | this.state = { 181 | layout: { top: 0, left: 0, width: 0, height: 0 }, 182 | styles: svgStyles 183 | }; 184 | }; 185 | 186 | _inherits(_class, _Component2); 187 | 188 | _createClass(_class, [{ 189 | key: 'getMyLayout', 190 | value: function getMyLayout(layout) { 191 | this.pathToNode.forEach(function (childIndex) { 192 | layout = layout.children[childIndex]; 193 | }); 194 | 195 | return layout; 196 | } 197 | }, { 198 | key: 'componentWillMount', 199 | value: function componentWillMount() { 200 | var _context$styleTools$setStyle = this.context.styleTools.setStyle(this.flexStyles); 201 | 202 | var setStyleFunc = _context$styleTools$setStyle.setStyle; 203 | var path = _context$styleTools$setStyle.path; 204 | 205 | this.styleTools.setStyle = setStyleFunc; 206 | this.pathToNode = path; 207 | 208 | this.context.waitForLayoutCalculation(this.handleLayoutCalculation); 209 | } 210 | }, { 211 | key: 'componentWillUnmount', 212 | value: function componentWillUnmount() { 213 | this.context.deregister(this.handleLayoutCalculation); 214 | } 215 | }, { 216 | key: 'componentWillReceiveProps', 217 | value: function componentWillReceiveProps() { 218 | var _context$styleTools$setStyle2 = this.context.styleTools.setStyle(this.flexStyles); 219 | 220 | var setStyleFunc = _context$styleTools$setStyle2.setStyle; 221 | var path = _context$styleTools$setStyle2.path; 222 | 223 | this.styleTools.setStyle = setStyleFunc; 224 | this.pathToNode = path; 225 | 226 | this.context.waitForLayoutCalculation(this.handleLayoutCalculation); 227 | } 228 | }, { 229 | key: 'getChildContext', 230 | value: function getChildContext() { 231 | return { 232 | styleTools: this.styleTools 233 | }; 234 | } 235 | }, { 236 | key: 'render', 237 | value: function render() { 238 | var transformation = 'translate(' + this.state.layout.left + ',' + this.state.layout.top + ')'; 239 | return _react2['default'].createElement( 240 | 'g', 241 | { transform: transformation }, 242 | _react2['default'].createElement(Composed, _extends({ layout: this.state.layout, style: this.state.styles }, this.props)) 243 | ); 244 | } 245 | }], [{ 246 | key: 'displayName', 247 | value: 'FlexBox', 248 | enumerable: true 249 | }, { 250 | key: 'contextTypes', 251 | value: { 252 | styleTools: _react2['default'].PropTypes.object.isRequired, 253 | waitForLayoutCalculation: _react2['default'].PropTypes.func.isRequired, 254 | deregister: _react2['default'].PropTypes.func.isRequired 255 | }, 256 | enumerable: true 257 | }, { 258 | key: 'childContextTypes', 259 | value: { 260 | styleTools: _react2['default'].PropTypes.object.isRequired 261 | }, 262 | enumerable: true 263 | }]); 264 | 265 | return _class; 266 | })(Component); 267 | }; 268 | 269 | exports.FlexBox = FlexBox; 270 | function partitionStyles(styles) { 271 | return _Object$keys(styles).reduce(function (partitions, property) { 272 | if ((0, _flexboxProps2['default'])(property)) { 273 | partitions.flexStyles[property] = styles[property]; 274 | } else { 275 | partitions.svgStyles[property] = styles[property]; 276 | } 277 | 278 | return partitions; 279 | }, { svgStyles: {}, flexStyles: {} }); 280 | } -------------------------------------------------------------------------------- /lib/flexbox-props.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _Set = require('babel-runtime/core-js/set')['default']; 4 | 5 | Object.defineProperty(exports, '__esModule', { 6 | value: true 7 | }); 8 | exports['default'] = isFlexBoxProperty; 9 | var flexboxProperties = new _Set(['flexDirection', 'justifyContent', 'alignItems', 'alignSelf', 'position', 'flexWrap', 'flex', 'width', 'height', 'maxWidth', 'maxHeight', 'minWidth', 'minHeight', 'margin', 'marginLeft', 'marginRight', 'marginTop', 'marginBottom', 'padding', 'paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom', 'borderWidth', 'borderLeftWidth', 'borderRightWidth', 'borderTopWidth', 'borderBottomWidth', 'left', 'top', 'right', 'bottom']); 10 | 11 | function isFlexBoxProperty(propertyName) { 12 | return flexboxProperties.has(propertyName); 13 | } 14 | 15 | module.exports = exports['default']; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-flexbox-svg", 3 | "version": "0.9.3", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test-serve": "webpack-dev-server --content-base test/ --hot --inline -d", 8 | "prepublish": "babel src --out-dir lib" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "peerDependencies": { 13 | "react": "0.14.0-alpha3" 14 | }, 15 | "dependencies": { 16 | "babel-runtime": "^5.1.11", 17 | "css-layout": "^1.0.0", 18 | "webpack": "^1.12.1", 19 | "webpack-dev-server": "^1.10.1", 20 | "wolfy87-eventemitter": "^4.2.11" 21 | }, 22 | "devDependencies": { 23 | "babel": "^5.1.11", 24 | "babel-core": "^5.1.10", 25 | "babel-loader": "^5.0.0", 26 | "webpack": "^1.8.5", 27 | "webpack-dev-server": "^1.8.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/flex.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import EventEmitter from 'wolfy87-eventemitter'; 3 | import computeLayout from 'css-layout'; 4 | import isFlexBoxProperty from './flexbox-props'; 5 | const { Component } = React; 6 | 7 | function setStyle (style = {}, styles, path = []) { 8 | if (styles.style === undefined) { 9 | styles.style = style; 10 | } else { 11 | let childStyle = { style: style, children: [] }; 12 | styles.children.push(childStyle); 13 | path.push(styles.children.length - 1); 14 | 15 | styles = childStyle; 16 | } 17 | 18 | return { 19 | path: path.slice(), 20 | setStyle : function (childStyle) { 21 | return setStyle(childStyle, styles, path.slice()); 22 | } 23 | } 24 | } 25 | 26 | export class FlexContext extends Component { 27 | static childContextTypes = { 28 | styleTools: React.PropTypes.object.isRequired, 29 | waitForLayoutCalculation: React.PropTypes.func.isRequired, 30 | deregister: React.PropTypes.func.isRequired 31 | } 32 | 33 | constructor (props, context) { 34 | super(props); 35 | 36 | this.layoutNotifier = new EventEmitter(); 37 | 38 | this.stylesRoot = { children: [] }; 39 | this.styleTools = {}; 40 | } 41 | 42 | deregister = cb => { 43 | this.layoutNotifier.removeListener('layout-update', cb) 44 | } 45 | 46 | waitForLayoutCalculation = (cb) => { 47 | this.layoutNotifier.once('layout-update', cb); 48 | } 49 | 50 | getChildContext () { 51 | return { 52 | styleTools: this.styleTools, 53 | waitForLayoutCalculation: this.waitForLayoutCalculation, 54 | deregister: this.deregister 55 | } 56 | } 57 | 58 | render () { 59 | return {this.props.children}; 60 | } 61 | 62 | startNewStyleTree () { 63 | this.stylesRoot = { children: [] }; 64 | ({ setStyle: this.styleTools.setStyle } = setStyle(undefined, this.stylesRoot)); 65 | } 66 | 67 | computeLayoutAndBroadcastResults () { 68 | computeLayout(this.stylesRoot); 69 | this.layoutNotifier.emit('layout-update', this.stylesRoot); 70 | } 71 | 72 | componentWillMount () { 73 | this.startNewStyleTree(); 74 | } 75 | 76 | componentDidMount () { 77 | this.computeLayoutAndBroadcastResults(); 78 | } 79 | 80 | componentWillUpdate () { 81 | this.startNewStyleTree(); 82 | } 83 | 84 | componentDidUpdate () { 85 | this.computeLayoutAndBroadcastResults(); 86 | } 87 | 88 | } 89 | 90 | export const FlexBox = (Composed, componentStyles = {}) => class extends Component { 91 | static displayName = 'FlexBox'; 92 | 93 | static contextTypes = { 94 | styleTools: React.PropTypes.object.isRequired, 95 | waitForLayoutCalculation: React.PropTypes.func.isRequired, 96 | deregister: React.PropTypes.func.isRequired 97 | } 98 | 99 | static childContextTypes = { 100 | styleTools: React.PropTypes.object.isRequired 101 | } 102 | 103 | getMyLayout (layout) { 104 | this.pathToNode.forEach(childIndex => { 105 | layout = layout.children[childIndex] 106 | }); 107 | 108 | return layout; 109 | } 110 | 111 | constructor (props, context) { 112 | super(props); 113 | 114 | const style = Object.assign(componentStyles, props.style); 115 | const { svgStyles, flexStyles } = partitionStyles(style) 116 | this.flexStyles = flexStyles; 117 | this.styleTools = {}; 118 | 119 | this.state = { 120 | layout: { top: 0, left: 0, width: 0, height: 0 }, 121 | styles: svgStyles 122 | }; 123 | } 124 | 125 | handleLayoutCalculation = layout => { 126 | this.setState({ layout: this.getMyLayout(layout).layout }); 127 | } 128 | 129 | componentWillMount () { 130 | ({ setStyle: this.styleTools.setStyle, 131 | path: this.pathToNode } = this.context.styleTools.setStyle(this.flexStyles)); 132 | 133 | this.context.waitForLayoutCalculation(this.handleLayoutCalculation); 134 | } 135 | 136 | componentWillReceiveProps (nextProps) { 137 | const { flexStyles } = partitionStyles(nextProps.style); 138 | 139 | ({ setStyle: this.styleTools.setStyle, 140 | path: this.pathToNode } = this.context.styleTools.setStyle(flexStyles)); 141 | 142 | this.context.waitForLayoutCalculation(this.handleLayoutCalculation); 143 | } 144 | 145 | getChildContext () { 146 | return { 147 | styleTools: this.styleTools 148 | } 149 | } 150 | 151 | render () { 152 | const transformation = `translate(${this.state.layout.left},${this.state.layout.top})`; 153 | const { style, ...other } = this.props; 154 | 155 | return ( 156 | 157 | 158 | 159 | ); 160 | } 161 | 162 | } 163 | 164 | function partitionStyles (styles) { 165 | return Object.keys(styles).reduce((partitions, property) => { 166 | if (isFlexBoxProperty(property)) { 167 | partitions.flexStyles[property] = styles[property]; 168 | } else { 169 | partitions.svgStyles[property] = styles[property]; 170 | } 171 | 172 | return partitions; 173 | }, { svgStyles: {}, flexStyles: {} }); 174 | } 175 | -------------------------------------------------------------------------------- /src/flexbox-props.js: -------------------------------------------------------------------------------- 1 | const flexboxProperties = new Set([ 2 | 'flexDirection', 3 | 'justifyContent', 4 | 'alignItems', 5 | 'alignSelf', 6 | 'position', 7 | 'flexWrap', 8 | 'flex', 9 | 'width', 10 | 'height', 11 | 'maxWidth', 12 | 'maxHeight', 13 | 'minWidth', 14 | 'minHeight', 15 | 'margin', 16 | 'marginLeft', 17 | 'marginRight', 18 | 'marginTop', 19 | 'marginBottom', 20 | 'padding', 21 | 'paddingLeft', 22 | 'paddingRight', 23 | 'paddingTop', 24 | 'paddingBottom', 25 | 'borderWidth', 26 | 'borderLeftWidth', 27 | 'borderRightWidth', 28 | 'borderTopWidth', 29 | 'borderBottomWidth', 30 | 'left', 31 | 'top', 32 | 'right', 33 | 'bottom' 34 | ]); 35 | 36 | export default function isFlexBoxProperty (propertyName) { 37 | return flexboxProperties.has(propertyName); 38 | } 39 | -------------------------------------------------------------------------------- /test/chessboard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FlexContext, FlexBox } from 'flex'; 3 | 4 | const { Component } = React; 5 | 6 | class Container extends Component { 7 | render () { 8 | return ( 9 | 10 | 18 | {this.props.children} 19 | 20 | ); 21 | } 22 | } 23 | 24 | const Backdrop = FlexBox(Container, { 25 | width: 400, 26 | height: 400, 27 | stroke: 'black', 28 | strokeWidth: 5, 29 | flex: 1, 30 | padding: 40, 31 | flexWrap: 'wrap', 32 | justifyContent: 'flex-start' 33 | }); 34 | 35 | const Square = FlexBox(Container, { 36 | stroke: 'black', 37 | height: 40, 38 | width: 40 39 | }); 40 | 41 | export default class Chessboard extends Component { 42 | render () { 43 | const board = []; 44 | 45 | for (let i = 0; i < 64; i++) { 46 | board[i] = i; 47 | } 48 | 49 | return ( 50 | 51 | 52 | 53 | {board.map(id => 54 | 62 | )} 63 | 64 | 65 | 66 | ) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | react-svg-flexbox 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /test/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { default as React, Component } from 'react'; 4 | // import Chessboard from './chessboard.jsx'; 5 | import Thing from './test.js'; 6 | 7 | window.React = React; 8 | 9 | class App extends Component { 10 | render () { 11 | return ; 12 | } 13 | } 14 | 15 | React.render(, document.getElementById('app')); -------------------------------------------------------------------------------- /test/ref.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flexbox reference 6 | 48 | 49 | 50 | 70 | 71 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { default as React, Component } from 'react'; 4 | import { FlexContext, FlexBox } from 'flex'; 5 | import { EventEmitter } from 'wolfy87-eventemitter'; 6 | 7 | @composeWith(FlexBox) 8 | class Container extends Component { 9 | render () { 10 | return {this.props.children} 11 | } 12 | } 13 | 14 | const circleStyle = { 15 | margin: 10, 16 | height: 100, 17 | width: 100 18 | }; 19 | 20 | @composeWith(FlexBox) 21 | class Circle extends Component { 22 | render () { 23 | return ; 24 | } 25 | } 26 | 27 | export default class Thing extends Component { 28 | static colors = (function (n) { 29 | const totalColors = 256**3; 30 | const stepSize = Math.floor(totalColors / n); 31 | const colorValues = [] 32 | 33 | for (let i = 0; i < n; i++) { 34 | colorValues.push(i * stepSize); 35 | } 36 | 37 | return colorValues.map(value => rgb(value)); 38 | 39 | function rgb (value) { 40 | const digits = []; 41 | 42 | for (let i = 0; i < 3; i++) { 43 | digits[i] = Math.floor(value / 256 ** (2 - i)); 44 | value %= 256 ** (2 - i); 45 | } 46 | 47 | return `rgb(${digits[0]},${digits[1]}, ${digits[2]})`; 48 | } 49 | 50 | })(18); 51 | 52 | state = { 53 | height: 700, 54 | width: 700 55 | }; 56 | 57 | componentDidMount () { 58 | let t = 0; 59 | 60 | setInterval(() => { 61 | const width = 275 * Math.cos(++t / (8 * Math.PI)) + 425; 62 | this.setState({ width }); 63 | }, 25); 64 | } 65 | 66 | render () { 67 | const { height, width } = this.state; 68 | 69 | const containerStyle = { 70 | height, 71 | width, 72 | flexDirection: 'row', 73 | flexWrap: 'wrap' 74 | }; 75 | 76 | return ( 77 | 78 | 79 | 80 | 81 | {Thing.colors.map((color, idx) => 82 | )} 83 | 84 | 85 | 86 | ); 87 | } 88 | } 89 | 90 | function composeWith (...hocs) { 91 | return target => 92 | hocs.reduce((composite, hoc) => 93 | hoc(composite), 94 | target); 95 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | module.exports = { 6 | context: __dirname + '/test', 7 | entry: './main.js', 8 | 9 | output: { 10 | publicPath: '/assets', 11 | filename: 'bundle.js' 12 | }, 13 | 14 | resolve: { 15 | modulesDirectories: ['src', 'node_modules'], 16 | extensions: ["", ".js", ".jsx"], 17 | fallback: path.join(__dirname, "node_modules") 18 | }, 19 | 20 | resolveLoader: { fallback: path.join(__dirname, "node_modules") }, 21 | 22 | module: { 23 | loaders: [ 24 | { 25 | test: /\.js(x)?$/, 26 | exclude: /node_modules/, 27 | loader: 'babel-loader', 28 | query: { 29 | optional: [ 30 | "es7.decorators", 31 | "es7.classProperties", 32 | "es7.objectRestSpread", 33 | "runtime" 34 | ] 35 | } 36 | } 37 | ] 38 | } 39 | 40 | }; 41 | --------------------------------------------------------------------------------