├── demo ├── src │ ├── js │ │ ├── data │ │ │ ├── simple │ │ │ │ ├── options.js │ │ │ │ ├── index.js │ │ │ │ └── html.html │ │ │ ├── entities │ │ │ │ ├── options.js │ │ │ │ ├── index.js │ │ │ │ └── html.html │ │ │ ├── transform │ │ │ │ ├── index.js │ │ │ │ ├── html.html │ │ │ │ ├── display.txt │ │ │ │ └── options.js │ │ │ └── index.js │ │ ├── containers │ │ │ ├── Html.js │ │ │ ├── Content.js │ │ │ └── Editor.js │ │ ├── actions.js │ │ ├── index.js │ │ ├── components │ │ │ ├── App.js │ │ │ ├── Html.js │ │ │ ├── Header.js │ │ │ └── Editor.js │ │ └── reducers.js │ ├── sass │ │ ├── html.scss │ │ ├── header.scss │ │ ├── editor.scss │ │ └── app.scss │ └── index.html ├── .babelrc ├── server.js ├── webpack.config.development.js ├── webpack.config.production.js ├── webpack.config.base.js └── package.json ├── .gitignore ├── .npmignore ├── .babelrc ├── tests.webpack.js ├── src ├── index.js ├── utils │ ├── isValidTagOrAttributeName.js │ ├── isEmptyTextNode.js │ ├── generatePropsFromAttributes.js │ ├── inlineStyleToObject.js │ └── htmlAttributesToReact.js ├── elementTypes │ ├── TextElementType.js │ ├── UnsupportedElementType.js │ ├── StyleElementType.js │ ├── index.js │ └── TagElementType.js ├── dom │ ├── elements │ │ └── VoidElements.js │ └── attributes │ │ ├── BooleanAttributes.js │ │ └── ReactAttributes.js ├── convertNodeToElement.js ├── HtmlParser.js └── processNodes.js ├── webpack.config.development.js ├── test ├── unit │ ├── elementTypes │ │ ├── TextElementType.spec.js │ │ ├── UnsupportedElementType.spec.js │ │ ├── StyleElementType.spec.js │ │ └── TagElementType.spec.js │ ├── utils │ │ ├── isEmptyTextNode.spec.js │ │ ├── isValidTagOrAttributeName.spec.js │ │ ├── inlineStyleToObject.spec.js │ │ ├── generatePropsFromAttributes.spec.js │ │ └── htmlAttributesToReact.spec.js │ ├── HtmlParser.spec.js │ ├── convertNodeToElement.spec.js │ └── processNodes.spec.js └── integration │ └── integration.spec.js ├── .travis.yml ├── webpack.config.production.js ├── webpack.config.test.js ├── .eslintrc ├── webpack.config.base.js ├── scripts └── attributes-diff.js ├── karma.conf.js ├── LICENSE.md ├── CHANGELOG.md ├── package.json └── README.md /demo/src/js/data/simple/options.js: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | .idea 3 | node_modules 4 | dist/ 5 | coverage/ 6 | lib/ -------------------------------------------------------------------------------- /demo/src/js/data/entities/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | decodeEntities: false 3 | }; 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .babelrc 3 | .eslintrc 4 | .travis.yml 5 | karma.conf.js 6 | tests.webpack.js 7 | webpack.config.*.js 8 | coverage/ 9 | test/ -------------------------------------------------------------------------------- /demo/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "react", 4 | "es2015", 5 | "stage-2" 6 | ], 7 | "plugins": ["react-hot-loader/babel"] 8 | } -------------------------------------------------------------------------------- /demo/src/js/data/entities/index.js: -------------------------------------------------------------------------------- 1 | import html from './html.html'; 2 | import options from './options'; 3 | 4 | export default { 5 | html, 6 | options 7 | }; 8 | -------------------------------------------------------------------------------- /demo/src/js/data/simple/index.js: -------------------------------------------------------------------------------- 1 | import html from './html.html'; 2 | import options from './options'; 3 | 4 | export default { 5 | html, 6 | options 7 | }; 8 | -------------------------------------------------------------------------------- /demo/src/js/data/entities/html.html: -------------------------------------------------------------------------------- 1 |

This example does not decode HTML entities

2 |

HTML entities such as £ » and & will be displayed as written

-------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "react", 4 | "es2015" 5 | ], 6 | "plugins": [ 7 | "transform-object-assign", 8 | "transform-object-rest-spread" 9 | ] 10 | } -------------------------------------------------------------------------------- /demo/src/js/data/transform/index.js: -------------------------------------------------------------------------------- 1 | import html from './html.html'; 2 | import options from './options'; 3 | import display from './display.txt'; 4 | 5 | export default { 6 | html, 7 | options, 8 | display 9 | }; 10 | -------------------------------------------------------------------------------- /demo/src/sass/html.scss: -------------------------------------------------------------------------------- 1 | #html { 2 | background: #fff; 3 | border: 1px solid #aaa; 4 | padding: 0.5rem; 5 | font-size: 0.9rem; 6 | overflow-y: scroll; 7 | h1, h2, h3, h4, h5, p, ul { 8 | margin: 0.5rem 0; 9 | } 10 | } -------------------------------------------------------------------------------- /demo/src/js/data/index.js: -------------------------------------------------------------------------------- 1 | import simple from './simple/index'; 2 | import entities from './entities/index'; 3 | import transform from './transform/index'; 4 | 5 | export default { 6 | simple, 7 | entities, 8 | transform 9 | }; 10 | -------------------------------------------------------------------------------- /tests.webpack.js: -------------------------------------------------------------------------------- 1 | // require all test files 2 | const testsContext = require.context('./test', true, /\.spec\.js$/); 3 | testsContext.keys().forEach(testsContext); 4 | 5 | // require all src files 6 | const componentsContext = require.context('./src/', true, /\.js$/); 7 | componentsContext.keys().forEach(componentsContext); 8 | -------------------------------------------------------------------------------- /demo/src/js/containers/Html.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import Content from '../components/Html'; 3 | 4 | const mapStateToProps = state => { 5 | return { 6 | html: state.html, 7 | selectedExample: state.selectedExample 8 | }; 9 | }; 10 | 11 | export default connect( 12 | mapStateToProps 13 | )(Content); 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import HtmlParser from './HtmlParser'; 2 | export default HtmlParser; 3 | 4 | export { default as processNodes } from './processNodes'; 5 | export { default as convertNodeToElement } from './convertNodeToElement'; 6 | 7 | // expose htmlparser2 so it can be used if required 8 | export { default as htmlparser2 } from 'htmlparser2'; 9 | -------------------------------------------------------------------------------- /src/utils/isValidTagOrAttributeName.js: -------------------------------------------------------------------------------- 1 | const VALID_TAG_REGEX = /^[a-zA-Z][a-zA-Z:_\.\-\d]*$/; 2 | 3 | const nameCache = {}; 4 | 5 | export default function isValidTagOrAttributeName(tagName) { 6 | if (!nameCache.hasOwnProperty(tagName)) { 7 | nameCache[tagName] = VALID_TAG_REGEX.test(tagName); 8 | } 9 | return nameCache[tagName]; 10 | } 11 | -------------------------------------------------------------------------------- /src/elementTypes/TextElementType.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts a text node to a React text element 3 | * 4 | * @param {Object} node The text node 5 | * @returns {String} The text 6 | */ 7 | export default function TextElementType(node) { 8 | 9 | // React will accept plain text for rendering so just return the node data 10 | return node.data; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /webpack.config.development.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var config = require('./webpack.config.base'); 3 | 4 | var config = Object.create(config); 5 | 6 | config.plugins = [ 7 | new webpack.optimize.OccurenceOrderPlugin(), 8 | new webpack.DefinePlugin({ 9 | 'process.env.NODE_ENV': JSON.stringify('development') 10 | }) 11 | ]; 12 | 13 | module.exports = config; -------------------------------------------------------------------------------- /test/unit/elementTypes/TextElementType.spec.js: -------------------------------------------------------------------------------- 1 | import TextElementType from 'elementTypes/TextElementType'; 2 | 3 | describe('Testing `elementTypes/TextElementType', () => { 4 | 5 | it('should return the value from the node data property', () => { 6 | const node = { 7 | data: 'test' 8 | }; 9 | expect(TextElementType(node)).toBe('test'); 10 | }); 11 | 12 | }); 13 | -------------------------------------------------------------------------------- /demo/src/js/actions.js: -------------------------------------------------------------------------------- 1 | export function updateHtml(html) { 2 | return { 3 | type: 'UPDATE_HTML', 4 | html 5 | }; 6 | } 7 | 8 | export function updateSelectedExample(example) { 9 | return { 10 | type: 'UPDATE_SELECTED_EXAMPLE', 11 | example 12 | }; 13 | } 14 | 15 | export function setView(view) { 16 | return { 17 | type: 'SET_VIEW', 18 | view 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /src/elementTypes/UnsupportedElementType.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Handles an unsupported element type by returning null so nothing is rendered 3 | * @returns {null} 4 | */ 5 | export default function UnsupportedElementType() { 6 | 7 | // do nothing because the element type is unsupported 8 | // comment, directive, script, cdata, doctype are all currently unsupported 9 | return null; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 8 5 | 6 | env: 7 | - REACT_VERSION=16 8 | - REACT_VERSION=15 9 | 10 | script: 11 | - npm run lint 12 | - npm test 13 | 14 | before_script: 15 | - npm install react@$REACT_VERSION react-dom@$REACT_VERSION 16 | 17 | after_script: 18 | - "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js" # Send coverage data to Coveralls 19 | -------------------------------------------------------------------------------- /src/dom/elements/VoidElements.js: -------------------------------------------------------------------------------- 1 | /** 2 | * List of void elements 3 | * These elements are not allowed to have children 4 | * @type {Array} 5 | */ 6 | export default [ 7 | 'area', 8 | 'base', 9 | 'br', 10 | 'col', 11 | 'command', 12 | 'embed', 13 | 'hr', 14 | 'img', 15 | 'input', 16 | 'keygen', 17 | 'link', 18 | 'meta', 19 | 'param', 20 | 'source', 21 | 'track', 22 | 'wbr' 23 | ]; 24 | -------------------------------------------------------------------------------- /demo/src/js/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import { createStore } from 'redux'; 4 | import { Provider } from 'react-redux'; 5 | import App from './components/App'; 6 | import reducer from './reducers'; 7 | 8 | let store = createStore(reducer); 9 | 10 | render( 11 | 12 | 13 | , 14 | document.getElementById('root') 15 | ); 16 | -------------------------------------------------------------------------------- /demo/src/js/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import 'sass/app'; 3 | 4 | import Header from './Header'; 5 | import Editor from '../containers/Editor'; 6 | import Html from '../containers/Html'; 7 | 8 | export default function App() { 9 | return ( 10 |
11 |
12 |
13 | 14 | 15 |
16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /test/unit/elementTypes/UnsupportedElementType.spec.js: -------------------------------------------------------------------------------- 1 | import UnsupportedElementType from 'elementTypes/UnsupportedElementType'; 2 | 3 | describe('Testing `elementTypes/UnsupportedElementType', () => { 4 | 5 | it('should always return null', () => { 6 | expect(UnsupportedElementType()).toBeNull(); 7 | expect(UnsupportedElementType('test')).toBeNull(); 8 | expect(UnsupportedElementType({}, 'test')).toBeNull(); 9 | }); 10 | 11 | }); 12 | -------------------------------------------------------------------------------- /demo/server.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var WebpackDevServer = require('webpack-dev-server'); 3 | var config = require('./webpack.config.development'); 4 | 5 | new WebpackDevServer(webpack(config), { 6 | hot: true, 7 | historyApiFallback: true, 8 | stats: { 9 | colors: true 10 | } 11 | }).listen(3000, 'localhost', function (err, result) { 12 | if (err) { 13 | console.log(err); 14 | } 15 | 16 | console.log('Listening at localhost:3000'); 17 | }); -------------------------------------------------------------------------------- /webpack.config.production.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var config = require('./webpack.config.base'); 3 | 4 | var config = Object.create(config); 5 | 6 | config.plugins = [ 7 | new webpack.optimize.OccurenceOrderPlugin(), 8 | new webpack.DefinePlugin({ 9 | 'process.env.NODE_ENV': JSON.stringify('production') 10 | }), 11 | new webpack.optimize.UglifyJsPlugin({ 12 | compressor: { 13 | warnings: false 14 | } 15 | }) 16 | ]; 17 | 18 | module.exports = config; -------------------------------------------------------------------------------- /webpack.config.test.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpackConfig = require('./webpack.config.base'); 3 | webpackConfig.watch = true; 4 | webpackConfig.isparta = { 5 | embedSource: true, 6 | noAutoWrap: true 7 | }; 8 | webpackConfig.module.preLoaders = [ 9 | { 10 | test: /\.jsx?$/, 11 | include: path.join(__dirname, 'src'), 12 | loader: 'isparta' 13 | } 14 | ]; 15 | delete webpackConfig.externals; 16 | delete webpackConfig.output; 17 | 18 | module.exports = webpackConfig; -------------------------------------------------------------------------------- /demo/src/js/containers/Content.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { updateHtml } from '../actions'; 3 | import Content from '../components/Content'; 4 | 5 | const mapStateToProps = state => { 6 | return { 7 | html: state.html 8 | }; 9 | }; 10 | 11 | const mapDispatchToProps = dispatch => { 12 | return { 13 | onUpdateHtml: html => { 14 | dispatch(updateHtml(html)); 15 | } 16 | }; 17 | }; 18 | 19 | export default connect( 20 | mapStateToProps, 21 | mapDispatchToProps 22 | )(Content); 23 | -------------------------------------------------------------------------------- /src/convertNodeToElement.js: -------------------------------------------------------------------------------- 1 | import ElementTypes from './elementTypes'; 2 | 3 | /** 4 | * Converts a htmlparser2 node to a React element 5 | * 6 | * @param {Object} node The htmlparser2 node to convert 7 | * @param {Number} index The index of the current node 8 | * @param {Function} transform Transform function to apply to children of the node 9 | * @returns {React.Element} 10 | */ 11 | export default function convertNodeToElement(node, index, transform) { 12 | return ElementTypes[node.type](node, index, transform); 13 | } 14 | -------------------------------------------------------------------------------- /demo/src/js/components/Html.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import ReactHtmlParser from 'react-html-parser'; 4 | 5 | import 'sass/html'; 6 | 7 | import data from '../data'; 8 | 9 | export default function Html(props) { 10 | const { html, selectedExample } = props; 11 | const options = data[selectedExample].options; 12 | return ( 13 |
14 | { ReactHtmlParser(html, options) } 15 |
16 | ); 17 | } 18 | 19 | Html.propTypes = { 20 | html: PropTypes.string.isRequired 21 | }; 22 | -------------------------------------------------------------------------------- /src/utils/isEmptyTextNode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tests a htmlparser2 node and returns whether is it a text node at the start and end of the line containing only 3 | * white space. This allows these node types to be excluded from the rendering because they are unnecessary. 4 | * 5 | * @param {Object} node The element object as created by htmlparser2 6 | * @returns {boolean} Whether the node is an empty text node 7 | */ 8 | export default function isEmptyTextNode(node) { 9 | return node.type === 'text' && /\r?\n/.test(node.data) && node.data.trim() === ''; 10 | } 11 | -------------------------------------------------------------------------------- /demo/src/sass/header.scss: -------------------------------------------------------------------------------- 1 | $headerLinkColor: #cc0000; 2 | 3 | header { 4 | h1 { 5 | padding: 0 0 1rem; 6 | } 7 | a { 8 | float: right; 9 | text-decoration: none; 10 | color: $headerLinkColor; 11 | line-height: 32px; 12 | svg { 13 | margin-right: 5px; 14 | position: relative; 15 | top: 2px; 16 | #github { 17 | fill: $headerLinkColor; 18 | } 19 | } 20 | &:hover { 21 | color: darken($headerLinkColor, 10%); 22 | #github { 23 | fill: darken($headerLinkColor, 10%); 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /demo/src/js/data/simple/html.html: -------------------------------------------------------------------------------- 1 |

Example HTML

2 |

Update this HTML to see React HTML Parser in action.

3 | 13 |

Attributes will be converted to React equivalent props

14 |

Inline styles can also be used

15 |

HTML entities such as £, » and & are decoded by default

-------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module", 6 | "ecmaFeatures": { 7 | "jsx": true, 8 | "experimentalObjectRestSpread": true 9 | } 10 | }, 11 | "env": { 12 | "browser": true, 13 | "node": true, 14 | "jasmine": true, 15 | "es6": true 16 | }, 17 | "plugins": [ 18 | "react" 19 | ], 20 | "rules": { 21 | "semi": [2, "always"], 22 | "quotes": [2, "single"], 23 | "indent": [2, 2, {"SwitchCase": 1}], 24 | "eol-last": 2, 25 | "react/jsx-uses-react": 1, 26 | "react/jsx-uses-vars": 1 27 | } 28 | } -------------------------------------------------------------------------------- /src/HtmlParser.js: -------------------------------------------------------------------------------- 1 | import htmlparser2 from 'htmlparser2'; 2 | import processNodes from './processNodes'; 3 | 4 | /** 5 | * Parses a HTML string and returns a list of React components generated from it 6 | * 7 | * @param {String} html The HTML to convert into React component 8 | * @param {Object} options Options to pass 9 | * @returns {Array} List of top level React elements 10 | */ 11 | export default function HtmlParser(html, { 12 | decodeEntities = true, 13 | transform, 14 | preprocessNodes = nodes => nodes 15 | }={}) { 16 | const nodes = preprocessNodes(htmlparser2.parseDOM(html, { decodeEntities })); 17 | return processNodes(nodes, transform); 18 | } 19 | -------------------------------------------------------------------------------- /demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React HTML Parser Demo 6 | 7 | 8 |
9 | 17 | 18 | -------------------------------------------------------------------------------- /demo/src/js/data/transform/html.html: -------------------------------------------------------------------------------- 1 |

Transform Example

2 | 3 | span elements are banned and won't be included in the output 4 | 5 | 9 | 10 |

11 | React components can be returned directly. 12 | This bold tag will be replaced directly with manually specified React element 13 |

14 | 15 |

16 | Attributes can also be modified. 17 | All links like this one 18 | and this one 19 | will automatically have the target="_blank" attribute added to them. 20 |

-------------------------------------------------------------------------------- /demo/src/sass/editor.scss: -------------------------------------------------------------------------------- 1 | $button-background: #3498db; 2 | 3 | #editor { 4 | border: 1px solid #aaa; 5 | display: flex; 6 | flex-direction: column; 7 | #HTML_EDITOR { 8 | flex: 1; 9 | } 10 | .presets { 11 | padding: 5px 10px; 12 | border-bottom: 1px solid #aaa; 13 | display: flex; 14 | 15 | ul { 16 | margin: 0; 17 | padding: 0; 18 | flex: 1; 19 | text-align: right; 20 | } 21 | 22 | li { 23 | margin: 0; 24 | padding: 0 10px; 25 | list-style-type: none; 26 | display: inline; 27 | border-right: 1px solid #aaa; 28 | &:last-of-type { 29 | border: none; 30 | padding-right: 0; 31 | } 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /demo/src/sass/app.scss: -------------------------------------------------------------------------------- 1 | html, body { 2 | background: #f5f5f5; 3 | margin: 0; 4 | height: 100%; 5 | } 6 | 7 | body { 8 | padding: 1rem; 9 | color: #000; 10 | font-size: 1rem; 11 | font-family: 'Proxima Nova', 'proxima-nova', sans-serif; 12 | font-weight: normal; 13 | line-height: 1.6; 14 | letter-spacing: 0.8px; 15 | } 16 | 17 | h1 { 18 | line-height: 1; 19 | margin: 0; 20 | } 21 | 22 | * { 23 | box-sizing: border-box; 24 | } 25 | 26 | #root { 27 | height: 100%; 28 | } 29 | 30 | #app { 31 | height: 100%; 32 | display: flex; 33 | flex-direction: column; 34 | } 35 | 36 | main { 37 | display: flex; 38 | flex: 1; 39 | overflow: hidden; 40 | > div { 41 | flex: 1; 42 | margin: 0 1rem 0 0; 43 | } 44 | } -------------------------------------------------------------------------------- /demo/src/js/containers/Editor.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { updateHtml, updateSelectedExample, setView } from '../actions'; 3 | import Editor from '../components/Editor'; 4 | 5 | const mapStateToProps = state => { 6 | return { 7 | html: state.html, 8 | examples: state.examples, 9 | selectedExample: state.selectedExample, 10 | view: state.view 11 | }; 12 | }; 13 | 14 | const mapDispatchToProps = dispatch => { 15 | return { 16 | onUpdateHtml: html => dispatch(updateHtml(html)), 17 | onUpdateExample: example => dispatch(updateSelectedExample(example)), 18 | onSetView: view => dispatch(setView(view)) 19 | }; 20 | }; 21 | 22 | export default connect( 23 | mapStateToProps, 24 | mapDispatchToProps 25 | )(Editor); 26 | -------------------------------------------------------------------------------- /src/dom/attributes/BooleanAttributes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * List of boolean attributes 3 | * These attributes should have their React attribute value set to be the same as their name 4 | * E.g. = 5 | * = 6 | * = 7 | * @type {Array} 8 | */ 9 | export default [ 10 | 'allowfullScreen', 11 | 'async', 12 | 'autoplay', 13 | 'capture', 14 | 'checked', 15 | 'controls', 16 | 'default', 17 | 'defer', 18 | 'disabled', 19 | 'formnovalidate', 20 | 'hidden', 21 | 'loop', 22 | 'multiple', 23 | 'muted', 24 | 'novalidate', 25 | 'open', 26 | 'playsinline', 27 | 'readonly', 28 | 'required', 29 | 'reversed', 30 | 'scoped', 31 | 'seamless', 32 | 'selected', 33 | 'itemscope' 34 | ]; 35 | -------------------------------------------------------------------------------- /webpack.config.base.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | module: { 5 | loaders: [ 6 | { 7 | test: /\.js$/, 8 | exclude: [ 9 | /node_modules/ 10 | ], 11 | loader: 'babel', 12 | query: { 13 | presets: [ 14 | 'react', 15 | 'es2015' 16 | ], 17 | plugins: [ 18 | 'transform-object-assign' 19 | ] 20 | } 21 | }, 22 | { 23 | test: /\.json$/, 24 | loader: 'json' 25 | } 26 | ] 27 | }, 28 | resolve: { 29 | root: [ 30 | path.join(__dirname, 'src') 31 | ], 32 | extensions: ['', '.js'] 33 | }, 34 | output: { 35 | library: 'ReactHtmlParser', 36 | libraryTarget: 'umd' 37 | }, 38 | externals: { 39 | 'react': 'React' 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /src/utils/generatePropsFromAttributes.js: -------------------------------------------------------------------------------- 1 | import htmlAttributesToReact from './htmlAttributesToReact'; 2 | import inlineStyleToObject from './inlineStyleToObject'; 3 | 4 | /** 5 | * Generates props for a React element from an object of HTML attributes 6 | * 7 | * @param {Object} attributes The HTML attributes 8 | * @param {String} key The key to give the react element 9 | */ 10 | export default function generatePropsFromAttributes(attributes, key) { 11 | 12 | // generate props 13 | const props = Object.assign({}, htmlAttributesToReact(attributes), { key }); 14 | 15 | // if there is an inline/string style prop then convert it to a React style object 16 | // otherwise, it is invalid and omitted 17 | if (typeof props.style === 'string' || props.style instanceof String) { 18 | props.style = inlineStyleToObject(props.style); 19 | } else { 20 | delete props.style; 21 | } 22 | 23 | return props; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/elementTypes/StyleElementType.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import generateElementProps from '../utils/generatePropsFromAttributes'; 3 | 4 | /** 5 | * Converts a