├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── package.json ├── src ├── index.js └── optimized.js └── test └── highlight.test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "react" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-extra-parens": 0, 4 | "react/jsx-uses-vars": 1, 5 | "global-strict": 1, 6 | "quotes": [2, "single"], 7 | "no-underscore-dangle": 0, 8 | "space-infix-ops": 0, 9 | "no-alert": 0 10 | }, 11 | "ecmaFeatures": { 12 | "jsx": true, 13 | }, 14 | "env": { 15 | "node": true, 16 | "browser": true, 17 | "es6": true, 18 | "jasmine": true 19 | }, 20 | "parser": "babel-eslint", 21 | "plugins": [ 22 | "react" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | .sass-cache 30 | build 31 | lib 32 | package-lock.json 33 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "stable" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Kiran Abburi 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 | # react-highlight 2 | 3 | React component for syntax highlighting using highlight.js 4 | 5 | ![Build Status](https://travis-ci.org/akiran/react-highlight.svg?branch=master) 6 | 7 | ### Latest version 8 | 9 | `0.11.1` 10 | 11 | ## [Documentation](https://react-highlight.neostack.com/) 12 | 13 | ### CodeSandbox Example 14 | 15 | [![Edit new](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/mj6wlmor9p) 16 | 17 | ### Installation 18 | 19 | ```bash 20 | npm install react-highlight --save 21 | ``` 22 | 23 | ### Usage 24 | 25 | #### Importing component 26 | 27 | ```js 28 | import Highlight from 'react-highlight' 29 | ``` 30 | 31 | #### Adding styles 32 | 33 | Choose the [theme](https://highlightjs.org/static/demo/) for syntax highlighting and add corresponding styles of highlight.js 34 | 35 | ```css 36 | 37 | ``` 38 | 39 | The styles will most likely be in `node_modules/highlight.js/styles` folder. 40 | 41 | Props: 42 | 43 | * className: custom class name 44 | * innerHTML: enable to render markup with dangerouslySetInnerHTML 45 | * element: render code snippet inside specified element 46 | 47 | #### Syntax highlighting of single code snippet 48 | 49 | Code snippet that requires syntax highlighting should be passed as children to Highlight component in string format. Language name of code snippet should be specified as className. 50 | 51 | ```html 52 | 53 | {"code snippet to be highlighted"} 54 | 55 | ``` 56 | 57 | #### Syntax highlighting of mutiple code snippets 58 | 59 | Set `innerHTML=true` to highlight multiple code snippets at a time. 60 | This is especially usefull if html with multiple code snippets is generated from preprocesser tools like markdown. 61 | 62 | **Warning:** If innerHTML is set to true, make sure the html generated with code snippets is from trusted source. 63 | 64 | ```html 65 | 66 | {"html with multiple code snippets"} 67 | 68 | ``` 69 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib'); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-highlight", 3 | "version": "0.15.0", 4 | "description": "React component for syntax highlighting", 5 | "main": "index.js", 6 | "scripts": { 7 | "prepublish": "babel ./src --out-dir ./lib --plugins=transform-class-properties,transform-react-jsx --presets=env", 8 | "test": "BABEL_ENV=test jest" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/akiran/react-highlight.git" 13 | }, 14 | "keywords": [ 15 | "react", 16 | "highlight.js", 17 | "syntax", 18 | "highlighting", 19 | "react-component" 20 | ], 21 | "author": "Kiran Abburi", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/akiran/react-highlight/issues" 25 | }, 26 | "homepage": "https://github.com/akiran/react-highlight", 27 | "dependencies": { 28 | "highlight.js": "^10.5.0" 29 | }, 30 | "devDependencies": { 31 | "autoprefixer": "^6.7.7", 32 | "babel-cli": "^6.26.0", 33 | "babel-core": "^6.26.0", 34 | "babel-eslint": "^6.1.2", 35 | "babel-jest": "^22.4.3", 36 | "babel-loader": "^7.1.2", 37 | "babel-plugin-transform-class-properties": "^6.24.1", 38 | "babel-plugin-transform-react-jsx": "^6.24.1", 39 | "babel-preset-env": "^1.6.1", 40 | "babel-preset-es2015": "^6.24.1", 41 | "babel-preset-react": "^6.24.1", 42 | "babel-preset-stage-0": "^6.24.1", 43 | "eslint": "^3.19.0", 44 | "eslint-plugin-react": "^6.10.3", 45 | "html-loader": "^0.4.5", 46 | "jest": "^22.4.2", 47 | "jsx-loader": "^0.13.2", 48 | "markdown-loader": "^2.0.0", 49 | "multiline": "^1.0.2", 50 | "node-libs-browser": "^2.0.0", 51 | "raw-loader": "^0.5.1", 52 | "react": "^15.5.4", 53 | "react-dom": "^15.5.4", 54 | "react-test-renderer": "^16.2.0", 55 | "regenerator-runtime": "^0.11.1", 56 | "webpack": "^3.6.0", 57 | "webpack-dev-server": "^2.9.1" 58 | }, 59 | "peerDependencies": {} 60 | } 61 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import hljs from 'highlight.js'; 2 | import React from 'react'; 3 | 4 | class Highlight extends React.Component { 5 | constructor(props) { 6 | super(props) 7 | this.setEl = this.setEl.bind(this) 8 | } 9 | componentDidMount() { 10 | this.highlightCode(); 11 | } 12 | 13 | componentDidUpdate() { 14 | this.highlightCode(); 15 | } 16 | 17 | highlightCode() { 18 | const nodes = this.el.querySelectorAll('pre code'); 19 | 20 | for (let i = 0; i < nodes.length; i++) { 21 | hljs.highlightBlock(nodes[i]) 22 | } 23 | } 24 | 25 | setEl(el) { 26 | this.el = el; 27 | }; 28 | 29 | render() { 30 | const {children, className, element: Element, innerHTML} = this.props; 31 | const props = { ref: this.setEl, className }; 32 | 33 | if (innerHTML) { 34 | props.dangerouslySetInnerHTML = { __html: children }; 35 | if (Element) { 36 | return ; 37 | } 38 | return
; 39 | } 40 | 41 | if (Element) { 42 | return {children}; 43 | } 44 | return
{children}
; 45 | } 46 | } 47 | 48 | Highlight.defaultProps = { 49 | innerHTML: false, 50 | className: null, 51 | element: null, 52 | }; 53 | 54 | export default Highlight; 55 | -------------------------------------------------------------------------------- /src/optimized.js: -------------------------------------------------------------------------------- 1 | import hljs from'highlight.js/lib/highlight'; 2 | import React from'react'; 3 | 4 | class Highlight extends React.Component { 5 | componentDidMount() { 6 | this.highlightCode(); 7 | } 8 | 9 | componentDidUpdate() { 10 | this.highlightCode(); 11 | } 12 | 13 | highlightCode() { 14 | const {className, languages} = this.props; 15 | const nodes = this.el.querySelectorAll('pre code'); 16 | 17 | if ((languages.length === 0) && className) { 18 | languages.push(className); 19 | } 20 | 21 | languages.forEach(lang => { 22 | hljs.registerLanguage(lang, require('highlight.js/lib/languages/' + lang)); 23 | }); 24 | 25 | for (let i = 0; i < nodes.length; i++) { 26 | hljs.highlightBlock(nodes[i]) 27 | } 28 | } 29 | 30 | setEl = (el) => { 31 | this.el = el; 32 | }; 33 | 34 | render() { 35 | const {children, className, element: Element, innerHTML} = this.props; 36 | const props = { ref: this.setEl, className }; 37 | 38 | if (innerHTML) { 39 | props.dangerouslySetInnerHTML = { __html: children }; 40 | if (Element) { 41 | return ; 42 | } 43 | return
; 44 | } 45 | 46 | if (Element) { 47 | return {children}; 48 | } 49 | return
{children}
; 50 | } 51 | } 52 | 53 | Highlight.defaultProps = { 54 | innerHTML: false, 55 | className: '', 56 | languages: [], 57 | }; 58 | 59 | export default Highlight; 60 | -------------------------------------------------------------------------------- /test/highlight.test.js: -------------------------------------------------------------------------------- 1 | import Highlight from '../src' 2 | import ReactDOM from 'react-dom' 3 | import TestUtils from 'react-dom/test-utils' 4 | import ReactDOMServer from 'react-dom/server' 5 | import React from 'react' 6 | 7 | describe('highlight', () => { 8 | test('should display text inside it', () => { 9 | const text = TestUtils.renderIntoDocument(Some text) 10 | 11 | expect(ReactDOM.findDOMNode(text).textContent).toBe('Some text') 12 | }) 13 | 14 | test('should have pre and code tags in markup', () => { 15 | const text = ReactDOMServer.renderToStaticMarkup( 16 | Some text 17 | ) 18 | 19 | expect(text).toBe('
Some text
') 20 | }) 21 | 22 | test('should assign className prop', () => { 23 | const text = ReactDOMServer.renderToStaticMarkup( 24 | Some text 25 | ) 26 | 27 | expect(text).toBe('
Some text
') 28 | }) 29 | 30 | test('should render children in span', () => { 31 | const text = ReactDOMServer.renderToStaticMarkup( 32 | Some text 33 | ) 34 | 35 | expect(text).toBe('Some text') 36 | }) 37 | 38 | test('should render innerHTML in span', () => { 39 | const text = ReactDOMServer.renderToStaticMarkup( 40 | 41 | Some text 42 | 43 | ) 44 | 45 | expect(text).toBe('Some text') 46 | }) 47 | 48 | test('should accept innerHTML prop', () => { 49 | const text = TestUtils.renderIntoDocument( 50 | {'
Sometext
'}
51 | ) 52 | 53 | expect(ReactDOM.findDOMNode(text).textContent).toBe('Sometext') 54 | }) 55 | }) 56 | --------------------------------------------------------------------------------