├── .eslintignore
├── .prettierignore
├── .gitignore
├── codecov.yml
├── babel.config.js
├── src
├── index.ts
├── worker
│ ├── tsconfig.json
│ └── index.ts
├── __tests__
│ ├── __snapshots__
│ │ └── BareHighlight.spec.js.snap
│ └── BareHighlight.spec.js
├── Highlight.tsx
└── BareHighlight.tsx
├── renovate.json
├── .eslintrc.js
├── .editorconfig
├── tsconfig.json
├── test
└── setup-jest.js
├── LICENSE
├── .github
└── workflows
│ ├── ossar-analysis.yml
│ ├── codeql-analysis.yml
│ └── build.yml
├── package.json
├── README.md
└── CHANGELOG.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | lib
2 | node_modules
3 | typings
4 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | esm
2 | flow-typed
3 | lib
4 | node_modules
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .nyc_output
2 | coverage
3 | lib
4 | node_modules
5 | typings
6 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | parsers:
3 | javascript:
4 | enable_partials: yes
5 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | presets: ['@researchgate/babel-preset', '@babel/preset-typescript'],
5 | };
6 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import Highlight from './Highlight';
2 | import BareHighlight from './BareHighlight';
3 |
4 | export { Highlight, BareHighlight };
5 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": ["@researchgate:lib"],
4 | "labels": ["dependencies"]
5 | }
6 |
--------------------------------------------------------------------------------
/src/worker/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "lib": ["webworker"]
5 | },
6 | "include":["./index.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/BareHighlight.spec.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`className is passed through 1`] = `
4 |
5 |
8 | test
9 |
10 |
11 | `;
12 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This file was created by spire-plugin-eslint for editor support
4 | const config = require('@researchgate/spire-config/eslint/react-typescript');
5 |
6 | config.globals = config.globals || {};
7 | config.globals.ReturnType = false;
8 |
9 | module.exports = config;
10 |
--------------------------------------------------------------------------------
/src/worker/index.ts:
--------------------------------------------------------------------------------
1 | /* eslint-env worker */
2 | import hjs from 'highlight.js';
3 |
4 | onmessage = (event) => {
5 | const { code, languages } = event.data;
6 | let result;
7 | if (languages && languages.length === 1) {
8 | result = hjs.highlight(languages[0], code, true);
9 | } else {
10 | result = hjs.highlightAuto(code, languages);
11 | }
12 |
13 | postMessage(result);
14 | };
15 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://EditorConfig.org
2 | # top-most EditorConfig file
3 | root = true
4 |
5 | [*]
6 | end_of_line = lf
7 | trim_trailing_whitespace = true
8 | insert_final_newline = false
9 |
10 | [*.md]
11 | trim_trailing_whitespace = false
12 |
13 | [*.{js,scss}]
14 | charset = utf-8
15 | indent_style = space
16 | insert_final_newline = true
17 |
18 | [*.js]
19 | indent_size = 4
20 |
21 | [*.scss]
22 | indent_size = 2
23 |
24 | [{*.json,.travis.yml,.*rc}]
25 | indent_style = space
26 | indent_size = 2
27 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react",
4 | "declaration": true,
5 | "outDir": "./typings",
6 | "emitDeclarationOnly": true,
7 | "strict": true,
8 | "strictNullChecks": true,
9 | "noImplicitThis": true,
10 | "noImplicitAny": true,
11 | "noImplicitReturns": true,
12 | "noUnusedParameters": true,
13 | "noUnusedLocals": true,
14 | "moduleResolution": "node",
15 | "esModuleInterop": true,
16 | "forceConsistentCasingInFileNames": true
17 | },
18 | "include": ["src/*"]
19 | }
20 |
--------------------------------------------------------------------------------
/test/setup-jest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env jest */
2 | import Enzyme from 'enzyme';
3 | import Adapter from 'enzyme-adapter-react-16';
4 |
5 | Enzyme.configure({ adapter: new Adapter() });
6 |
7 | // eslint-disable-next-line no-console
8 | const consoleError = console.error;
9 |
10 | function logToError(error) {
11 | throw new Error(error);
12 | }
13 |
14 | beforeEach(() => {
15 | // eslint-disable-next-line no-console
16 | console.error = logToError;
17 | });
18 |
19 | afterEach(() => {
20 | // eslint-disable-next-line no-console
21 | console.error = consoleError;
22 | });
23 |
--------------------------------------------------------------------------------
/src/Highlight.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import PropTypes from 'prop-types';
3 | import hljs from 'highlight.js';
4 | import BareHighlight from './BareHighlight';
5 |
6 | interface Props {
7 | children: string;
8 | className?: string;
9 | languages?: Array;
10 | worker?: Worker;
11 | }
12 |
13 | const Highlight = (props: Props) => {
14 | const { children, ...rest } = props;
15 |
16 | return (
17 |
18 | {children}
19 |
20 | );
21 | };
22 |
23 | Highlight.defaultProps = {
24 | className: '',
25 | languages: [],
26 | worker: null,
27 | };
28 |
29 | Highlight.propTypes = {
30 | children: PropTypes.string.isRequired,
31 | className: PropTypes.string,
32 | languages: PropTypes.arrayOf(PropTypes.string),
33 | worker: PropTypes.object, // eslint-disable-line react/forbid-prop-types
34 | };
35 |
36 | export default Highlight;
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) ResearchGate GmbH and contributors
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/.github/workflows/ossar-analysis.yml:
--------------------------------------------------------------------------------
1 | # This workflow integrates a collection of open source static analysis tools
2 | # with GitHub code scanning. For documentation, or to provide feedback, visit
3 | # https://github.com/github/ossar-action
4 | name: OSSAR
5 |
6 | on:
7 | push:
8 | pull_request:
9 |
10 | jobs:
11 | OSSAR-Scan:
12 | # OSSAR runs on windows-latest.
13 | # ubuntu-latest and macos-latest support coming soon
14 | runs-on: windows-latest
15 |
16 | steps:
17 | # Checkout your code repository to scan
18 | - name: Checkout repository
19 | uses: actions/checkout@v3
20 | with:
21 | # We must fetch at least the immediate parents so that if this is
22 | # a pull request then we can checkout the head.
23 | fetch-depth: 2
24 |
25 | # If this run was triggered by a pull request event, then checkout
26 | # the head of the pull request instead of the merge commit.
27 | - run: git checkout HEAD^2
28 | if: ${{ github.event_name == 'pull_request' }}
29 |
30 | # Ensure a compatible version of dotnet is installed.
31 | # The [Microsoft Security Code Analysis CLI](https://aka.ms/mscadocs) is built with dotnet v3.1.201.
32 | # A version greater than or equal to v3.1.201 of dotnet must be installed on the agent in order to run this action.
33 | # GitHub hosted runners already have a compatible version of dotnet installed and this step may be skipped.
34 | # For self-hosted runners, ensure dotnet version 3.1.201 or later is installed by including this action:
35 | # - name: Install .NET
36 | # uses: actions/setup-dotnet@v1
37 | # with:
38 | # dotnet-version: '3.1.x'
39 |
40 | # Run open source static analysis tools
41 | - name: Run OSSAR
42 | uses: github/ossar-action@v1
43 | id: ossar
44 |
45 | # Upload results to the Security tab
46 | - name: Upload OSSAR results
47 | uses: github/codeql-action/upload-sarif@v1
48 | with:
49 | sarif_file: ${{ steps.ossar.outputs.sarifFile }}
50 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ main ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ main ]
20 | schedule:
21 | - cron: '34 9 * * 1'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 |
28 | strategy:
29 | fail-fast: false
30 | matrix:
31 | language: [ 'javascript' ]
32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
33 | # Learn more:
34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
35 |
36 | steps:
37 | - name: Checkout repository
38 | uses: actions/checkout@v3
39 |
40 | # Initializes the CodeQL tools for scanning.
41 | - name: Initialize CodeQL
42 | uses: github/codeql-action/init@v1
43 | with:
44 | languages: ${{ matrix.language }}
45 | # If you wish to specify custom queries, you can do so here or in a config file.
46 | # By default, queries listed here will override any specified in a config file.
47 | # Prefix the list here with "+" to use these queries and those in the config file.
48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
49 |
50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
51 | # If this step fails, then you should remove it and run the build manually (see below)
52 | - name: Autobuild
53 | uses: github/codeql-action/autobuild@v1
54 |
55 | # ℹ️ Command-line programs to run using the OS shell.
56 | # 📚 https://git.io/JvXDl
57 |
58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
59 | # and modify them (or add more) to build your code if your project
60 | # uses a compiled language
61 |
62 | #- run: |
63 | # make bootstrap
64 | # make release
65 |
66 | - name: Perform CodeQL Analysis
67 | uses: github/codeql-action/analyze@v1
68 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | env:
10 | NODE_VERSION: 14
11 |
12 | jobs:
13 | tests:
14 | name: Unit tests
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - name: Checkout
19 | uses: actions/checkout@v3
20 |
21 | - name: Setup Node.js
22 | uses: actions/setup-node@v3
23 | with:
24 | node-version: ${{ env.NODE_VERSION }}
25 |
26 | - name: Cache node_modules
27 | uses: actions/cache@v3
28 | id: cache-nodemodules
29 | with:
30 | path: node_modules
31 | key: ${{ runner.os }}-${{ env.NODE_VERSION }}-nodemodules-${{ hashFiles('**/yarn.lock') }}
32 |
33 | - name: Install dependencies
34 | if: steps.cache-nodemodules.outputs.cache-hit != 'true'
35 | run: yarn install --frozen-lockfile --non-interactive
36 |
37 | - name: Unit tests
38 | run: yarn test --coverage
39 |
40 | - name: Upload coverage to Codecov
41 | uses: codecov/codecov-action@v2
42 | with:
43 | files: ./coverage/coverage-final.json
44 | fail_ci_if_error: true
45 |
46 | lint:
47 | name: Lint
48 | runs-on: ubuntu-latest
49 |
50 | steps:
51 | - name: Checkout
52 | uses: actions/checkout@v3
53 |
54 | - name: Setup Node.js
55 | uses: actions/setup-node@v3
56 | with:
57 | node-version: ${{ env.NODE_VERSION }}
58 |
59 | - name: Cache node_modules
60 | uses: actions/cache@v3
61 | id: cache-nodemodules
62 | with:
63 | path: node_modules
64 | key: ${{ runner.os }}-${{ env.NODE_VERSION }}-nodemodules-${{ hashFiles('**/yarn.lock') }}
65 |
66 | - name: Install dependencies
67 | if: steps.cache-nodemodules.outputs.cache-hit != 'true'
68 | run: yarn install --frozen-lockfile --non-interactive
69 |
70 | - name: Lint
71 | run: yarn lint
72 |
73 | release:
74 | needs: [tests, lint]
75 | if: github.ref == 'refs/heads/main'
76 | name: Release
77 | runs-on: ubuntu-latest
78 |
79 | steps:
80 | - name: Checkout
81 | uses: actions/checkout@v3
82 | with:
83 | fetch-depth: 0
84 |
85 | - name: Setup Node.js
86 | uses: actions/setup-node@v3
87 | with:
88 | node-version: ${{ env.NODE_VERSION }}
89 |
90 | - name: Cache node_modules
91 | uses: actions/cache@v3
92 | id: cache-nodemodules
93 | with:
94 | path: node_modules
95 | key: ${{ runner.os }}-${{ env.NODE_VERSION }}-nodemodules-${{ hashFiles('**/yarn.lock') }}
96 |
97 | - name: Install dependencies
98 | if: steps.cache-nodemodules.outputs.cache-hit != 'true'
99 | run: yarn install --frozen-lockfile --non-interactive
100 |
101 | - name: Release
102 | run: yarn release
103 | env:
104 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
105 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
106 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-fast-highlight",
3 | "version": "4.1.0",
4 | "description": "A fast react component wrapper for highlight.js",
5 | "main": "lib/js/index.js",
6 | "module": "lib/esm/index.js",
7 | "types": "typings/index.d.ts",
8 | "scripts": {
9 | "clean": "rimraf lib",
10 | "build": "run-p build:*",
11 | "build:commonjs": "cross-env BABEL_ENV=production BABEL_OUTPUT=cjs babel src --out-dir lib/js --extensions .ts,.tsx --ignore '**/__tests__'",
12 | "build:esm": "cross-env BABEL_ENV=production BABEL_OUTPUT=esm babel src --out-dir lib/esm --extensions .ts,.tsx --ignore '**/__tests__'",
13 | "build:types": "tsc",
14 | "format": "spire format",
15 | "lint": "spire lint",
16 | "prepare": "run-s clean build",
17 | "release": "spire release --branches main",
18 | "test": "spire test"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "https://github.com/researchgate/react-fast-highlight.git"
23 | },
24 | "files": [
25 | "lib",
26 | "typings"
27 | ],
28 | "keywords": [
29 | "react",
30 | "component",
31 | "highlight",
32 | "syntax",
33 | "highlightjs"
34 | ],
35 | "author": "Daniel Tschinder ",
36 | "license": "MIT",
37 | "bugs": {
38 | "url": "https://github.com/researchgate/react-fast-highlight/issues"
39 | },
40 | "homepage": "https://github.com/researchgate/react-fast-highlight#readme",
41 | "peerDependencies": {
42 | "highlight.js": "^10.0.0 || ^11.0.0",
43 | "react": "^16.0.0"
44 | },
45 | "devDependencies": {
46 | "@babel/cli": "7.28.3",
47 | "@babel/core": "7.28.5",
48 | "@babel/preset-typescript": "7.28.5",
49 | "@researchgate/babel-preset": "2.0.14",
50 | "@researchgate/spire-config": "5.0.7",
51 | "@types/classnames": "2.3.4",
52 | "@types/prop-types": "15.7.15",
53 | "@types/react": "16.14.68",
54 | "cross-env": "7.0.3",
55 | "enzyme": "3.11.0",
56 | "enzyme-adapter-react-16": "1.15.8",
57 | "enzyme-to-json": "3.6.2",
58 | "highlight.js": "11.11.1",
59 | "jest-snapshot": "29.7.0",
60 | "npm-run-all": "4.1.5",
61 | "react": "16.14.0",
62 | "react-dom": "16.14.0",
63 | "rimraf": "3.0.2",
64 | "spire": "3.2.3",
65 | "spire-plugin-semantic-release": "3.2.3",
66 | "typescript": "4.9.5"
67 | },
68 | "dependencies": {
69 | "@types/highlight.js": "^9.12.3",
70 | "clsx": "^1.1.1",
71 | "prop-types": "^15.5.6"
72 | },
73 | "jest": {
74 | "collectCoverageFrom": [
75 | "src/**/*.ts",
76 | "src/**/*.tsx",
77 | "!src/**/__tests__/**/*"
78 | ],
79 | "setupFilesAfterEnv": [
80 | "/test/setup-jest.js"
81 | ],
82 | "snapshotSerializers": [
83 | "enzyme-to-json/serializer"
84 | ]
85 | },
86 | "spire": {
87 | "extends": [
88 | [
89 | "@researchgate/spire-config",
90 | {
91 | "eslint": "react-typescript"
92 | }
93 | ]
94 | ],
95 | "plugins": [
96 | "spire-plugin-semantic-release"
97 | ]
98 | },
99 | "prettier": "@researchgate/prettier-config"
100 | }
101 |
--------------------------------------------------------------------------------
/src/__tests__/BareHighlight.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { mount, shallow } from 'enzyme';
3 | import highlightjs from 'highlight.js';
4 | import BareHighlight from '../BareHighlight';
5 |
6 | test('no language - calls correct highlightCall', (done) => {
7 | const hljs = {
8 | highlightAuto: jest.fn(() => ({ value: 'othertest', language: 'xml' })),
9 | };
10 |
11 | const wrapper = mount(
12 | test
13 | );
14 |
15 | setTimeout(() => {
16 | expect(wrapper.state('language')).toBe('xml');
17 | expect(wrapper.state('highlightedCode')).toBe('othertest');
18 | expect(hljs.highlightAuto).toHaveBeenCalledWith('test', []);
19 | expect(hljs.highlightAuto).toHaveBeenCalledTimes(1);
20 |
21 | done();
22 | }, 1);
23 | });
24 |
25 | test('can correctly rerender code', (done) => {
26 | let value = 'initalresult';
27 | const hljs = {
28 | highlightAuto: jest.fn(() => ({ value, language: 'xml' })),
29 | };
30 |
31 | const wrapper = mount(
32 | test
33 | );
34 |
35 | setTimeout(() => {
36 | value = 'changed';
37 | wrapper.setProps({ children: 'newtest' });
38 | setTimeout(() => {
39 | expect(wrapper.state('language')).toBe('xml');
40 | expect(wrapper.state('highlightedCode')).toBe('changed');
41 | expect(hljs.highlightAuto).toHaveBeenCalledWith('newtest', []);
42 | expect(hljs.highlightAuto).toHaveBeenCalledTimes(2);
43 |
44 | done();
45 | }, 1);
46 | }, 1);
47 | });
48 |
49 | test('one language - calls correct highlightCall', (done) => {
50 | const hljs = {
51 | highlight: jest.fn(() => ({ value: 'othertest', language: 'js' })),
52 | };
53 |
54 | const wrapper = mount(
55 |
56 | test
57 |
58 | );
59 |
60 | setTimeout(() => {
61 | expect(wrapper.state('language')).toBe('js');
62 | expect(wrapper.state('highlightedCode')).toBe('othertest');
63 | expect(hljs.highlight).toHaveBeenCalledWith("test", { "language": "js" });
64 | expect(hljs.highlight).toHaveBeenCalledTimes(1);
65 |
66 | done();
67 | }, 1);
68 | });
69 |
70 | test('multiple languages - calls correct highlightCall', (done) => {
71 | const hljs = {
72 | highlightAuto: jest.fn(() => ({ value: 'othertest', language: 'js' })),
73 | };
74 |
75 | const wrapper = mount(
76 |
77 | test
78 |
79 | );
80 |
81 | setTimeout(() => {
82 | expect(wrapper.state('language')).toBe('js');
83 | expect(wrapper.state('highlightedCode')).toBe('othertest');
84 | expect(hljs.highlightAuto).toHaveBeenCalledWith('test', ['js', 'html']);
85 | expect(hljs.highlightAuto).toHaveBeenCalledTimes(1);
86 |
87 | done();
88 | }, 1);
89 | });
90 |
91 | test('className is passed through', () => {
92 | const wrapper = shallow(
93 |
94 | test
95 |
96 | );
97 | expect(wrapper).toMatchSnapshot();
98 | });
99 |
--------------------------------------------------------------------------------
/src/BareHighlight.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import PropTypes from 'prop-types';
3 | import cx from 'clsx';
4 | import type hljs from 'highlight.js';
5 |
6 | interface Props {
7 | children: string;
8 | className?: string;
9 | highlightjs: typeof hljs;
10 | languages?: Array;
11 | worker?: Worker;
12 | }
13 |
14 | interface State {
15 | highlightedCode?: string;
16 | language?: string;
17 | }
18 |
19 | export default class BareHighlight extends React.PureComponent {
20 | static defaultProps = {
21 | className: '',
22 | languages: [],
23 | worker: null,
24 | };
25 |
26 | static propTypes = {
27 | children: PropTypes.string.isRequired,
28 | className: PropTypes.string,
29 | highlightjs: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
30 | languages: PropTypes.arrayOf(PropTypes.string),
31 | worker: PropTypes.object, // eslint-disable-line react/forbid-prop-types
32 | };
33 |
34 | state: State = {};
35 |
36 | componentDidMount() {
37 | this.highlightCode();
38 | }
39 |
40 | componentDidUpdate(prevProps: Props) {
41 | // If the text changed make sure to reset the state
42 | // This way we ensure that the new text is immediately displayed.
43 | if (prevProps.children !== this.props.children) {
44 | this.setState({ highlightedCode: undefined, language: undefined });
45 | return;
46 | }
47 |
48 | // Do not call highlight.js if we already have highlighted code
49 | // If the children changed highlightedCode will be null
50 | if (this.state.highlightedCode) return;
51 |
52 | this.highlightCode();
53 | }
54 |
55 | getInitialCode() {
56 | const type = typeof this.props.children;
57 | if (type !== 'string') {
58 | throw new Error(
59 | `Children of must be a string. '${type}' supplied`
60 | );
61 | }
62 |
63 | return this.props.children;
64 | }
65 |
66 | getHighlightPromise(): Promise> {
67 | const { highlightjs, languages } = this.props;
68 | return new Promise((resolve) => {
69 | if (languages && languages.length === 1) {
70 | resolve(highlightjs.highlight(this.getInitialCode(), {
71 | language: languages[0],
72 | }));
73 | } else {
74 | resolve(highlightjs.highlightAuto(this.getInitialCode(), languages));
75 | }
76 | });
77 | }
78 |
79 | highlightCode() {
80 | const { languages, worker } = this.props;
81 |
82 | if (worker) {
83 | worker.onmessage = (event) =>
84 | this.setState({
85 | highlightedCode: event.data.value,
86 | language: event.data.language,
87 | });
88 | worker.postMessage({ code: this.getInitialCode(), languages });
89 | } else {
90 | this.getHighlightPromise().then((result) =>
91 | this.setState({
92 | highlightedCode: result.value,
93 | language: result.language,
94 | })
95 | );
96 | }
97 | }
98 |
99 | render() {
100 | const code = this.state.highlightedCode;
101 | const classes = cx(this.props.className, 'hljs', this.state.language);
102 |
103 | if (code) {
104 | return (
105 |
106 |
110 |
111 | );
112 | }
113 |
114 | return (
115 |
116 | {this.getInitialCode()}
117 |
118 | );
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-fast-highlight
2 |
3 | ###### A fast react component wrapper for highlight.js
4 |
5 | [](https://codecov.io/gh/researchgate/react-fast-highlight)
6 |
7 | ## Requirements
8 |
9 | - Requires `react` version 16
10 | - Requires `highlight.js` version 9 or 10
11 |
12 | ## Install
13 |
14 | `npm install --save react-fast-highlight react highlight.js`
15 |
16 | or
17 |
18 | `yarn add react-fast-highlight react highlight.js`
19 |
20 | ## Usage
21 |
22 | ```js
23 | import React, { Component } from 'react';
24 | import { Highlight } from 'react-fast-highlight';
25 |
26 | class App extends Component {
27 |
28 | render() {
29 | const code = 'let t = 0.5 * 5 * 4;';
30 |
31 | return (
32 | {/*
33 | `languages` is an optional property to set the languages that highlight.js should pick from.
34 | By default it is empty, which means highlight.js will pick from all available languages.
35 | If only one language is provided, this language will be used without doing checks beforehand.
36 | (Be aware that automatic language detection is not as fast as when specifing a language.)
37 |
38 | `worker` is used to take advantage of the possibility to do the highlighting work in a
39 | web-worker. As different module-bundlers use different ways to load web-workers, it is up
40 | to you to load the webworker and supply it to the highlight component. (see example)
41 | Currently every instance of the highlight component needs its own web-worker.
42 |
43 | `className` sets additional class names on the generated html markup.
44 | */}
45 |
49 | {code}
50 |
51 | );
52 | }
53 | ```
54 |
55 | ### Advanced Usage
56 |
57 | #### Custom highlight.js distribution
58 |
59 | In cases where you bundle this component with a module bundler such as webpack,
60 | rollup or browserify and you know upfront which languages you want to support
61 | you can supply a custom distribution of `highlight.js`. This ensures you are not
62 | bundling all available languages of `highlight.js` which reduces the size of
63 | your bundle.
64 |
65 | A custom distribution might look like this
66 |
67 | ```js
68 | import hljs from 'highlight.js/lib/highlight';
69 |
70 | // Lets only register javascript, scss, html/xml
71 | hljs.registerLanguage('scss', require('highlight.js/lib/languages/scss'));
72 | hljs.registerLanguage(
73 | 'javascript',
74 | require('highlight.js/lib/languages/javascript')
75 | );
76 | hljs.registerLanguage('xml', require('highlight.js/lib/languages/xml'));
77 |
78 | export default hljs;
79 | ```
80 |
81 | To actually use a custom distribution you need to use the `BareHighlight`
82 | component. With it you can build your wrapper for highlighting code.
83 |
84 | ```js
85 | import React, { Component } from 'react';
86 | import BareHighlight from 'react-fast-highlight/lib/js/BareHighlight';
87 | import hljs from './customhljs';
88 |
89 | class CustomHighlight extends Component {
90 | render() {
91 | const { children, ...props } = this.props;
92 | return (
93 |
94 | {children}
95 |
96 | );
97 | }
98 | ```
99 |
100 | Now you can use this wrapper the same way as the default `Highlight` component
101 | with only support for certain languages included.
102 |
103 | > In case you also want to use a webworker you should not use the supplied
104 | > worker, as it includes all languages. Instead you will need to copy the worker
105 | > from this repo and adjust the `highlight.js` import.
106 |
107 | #### Webworker
108 |
109 | It wasn't tested with browserify and rollup but it should work. If you managed
110 | to get it working please open a PR with the necessary changes and the
111 | documentation.
112 |
113 | ##### Webpack
114 |
115 | To make web-workers working with webpack you additionally need to install
116 | `worker-loader`.
117 |
118 | `npm install --save worker-loader react-fast-highlight`
119 |
120 | ```js
121 | import React from 'react';
122 | import { Highlight } from 'react-fast-highlight';
123 | import Worker from 'worker!react-fast-highlight/lib/js/worker';
124 |
125 | class App extends React.Component {
126 |
127 | render() {
128 | const code = 'let t = 0.5 * 5 * 4;';
129 |
130 | return (
131 |
135 | {code}
136 |
137 | );
138 | }
139 | ```
140 |
141 | ## License
142 |
143 | react-fast-highlight is licensed under the MIT license.
144 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # [4.1.0](https://github.com/researchgate/react-fast-highlight/compare/v4.0.0...v4.1.0) (2021-03-07)
2 |
3 |
4 | ### Features
5 |
6 | * Replace classnames dependency with clsx ([7f6222f](https://github.com/researchgate/react-fast-highlight/commit/7f6222fef56cd1c4c5cf13f70196ae3e07a7fc8f))
7 |
8 | # [4.0.0](https://github.com/researchgate/react-fast-highlight/compare/v3.0.0...v4.0.0) (2020-04-30)
9 |
10 | ### Bug Fixes
11 |
12 | - **deps:** support dependency highlight.js v10 in addition to v9
13 | ([5fbbf75](https://github.com/researchgate/react-fast-highlight/commit/5fbbf7516bd95a53acd7cba994cec6f0e8c0326f))
14 |
15 | ### BREAKING CHANGES
16 |
17 | - **deps:** highlight.js is now a peer dependency (v9 or v10). Please install it
18 | in your project.
19 |
20 | # [3.0.0](https://github.com/researchgate/react-fast-highlight/compare/v2.2.2...v3.0.0) (2019-11-30)
21 |
22 | ### Features
23 |
24 | - Migrate to typescript and spire
25 | ([e2ae43f](https://github.com/researchgate/react-fast-highlight/commit/e2ae43f6a07319c40adb95554d73c686deb4ec36))
26 |
27 | ### BREAKING CHANGES
28 |
29 | - No default export anymore. Use named exports.
30 |
31 | # Change Log
32 |
33 | All notable changes to this project will be documented in this file. See
34 | [standard-version](https://github.com/conventional-changelog/standard-version)
35 | for commit guidelines.
36 |
37 |
38 |
39 | ## [2.2.2](https://github.com/researchgate/react-fast-highlight/compare/v2.2.1...v2.2.2) (2018-06-09)
40 |
41 | ### Bug Fixes
42 |
43 | - **babel:** Do not include tests in published package
44 | ([03d0fc6](https://github.com/researchgate/react-fast-highlight/commit/03d0fc6))
45 |
46 |
47 |
48 | ## [2.2.1](https://github.com/researchgate/react-fast-highlight/compare/v2.2.0...v2.2.1) (2018-06-09)
49 |
50 | ### Bug Fixes
51 |
52 | - **react:** Remove deprecation warning with react 16.4
53 | ([3a9b707](https://github.com/researchgate/react-fast-highlight/commit/3a9b707))
54 |
55 |
56 |
57 | # [2.2.0](https://github.com/researchgate/react-fast-highlight/compare/v2.1.2...v2.2.0) (2017-10-11)
58 |
59 | ### Features
60 |
61 | - **api:** Export Highlight and BareHighlight also as named exports
62 | ([613f98c](https://github.com/researchgate/react-fast-highlight/commit/613f98c))
63 |
64 |
65 |
66 | ## [2.1.2](https://github.com/researchgate/react-fast-highlight/compare/v2.1.1...v2.1.2) (2017-10-11)
67 |
68 | ### Bug Fixes
69 |
70 | - **dependencies:** Allow react 16
71 | ([29e4467](https://github.com/researchgate/react-fast-highlight/commit/29e4467))
72 |
73 |
74 |
75 | ## [2.1.1](https://github.com/researchgate/react-fast-highlight/compare/v2.1.0...v2.1.1) (2017-04-11)
76 |
77 | ### Bug Fixes
78 |
79 | - Fix deprecation warnings with react 15.5
80 |
81 |
82 |
83 | # [2.1.0](https://github.com/researchgate/react-fast-highlight/compare/v2.0.0...v2.1.0) (2016-12-06)
84 |
85 | ### Features
86 |
87 | - Support custom highlight.js distributions
88 |
89 |
90 |
91 | # [2.0.0](https://github.com/researchgate/react-fast-highlight/compare/v1.1.2...v2.0.0) (2016-08-03)
92 |
93 | ### Features
94 |
95 | - Use PureComponent of react 15.3
96 |
97 | ### BREAKING CHANGES
98 |
99 | - Requires now react version 15.3
100 |
101 |
102 |
103 | ## [1.1.2](https://github.com/researchgate/react-fast-highlight/compare/v1.1.1...v1.1.2) (2016-08-03)
104 |
105 | ### Bug Fixes
106 |
107 | - Correctly set upper limit for react version (< 15.3)
108 |
109 |
110 |
111 | ## [1.1.1](https://github.com/researchgate/react-fast-highlight/compare/v1.1.0...v1.1.1) (2016-05-23)
112 |
113 | ### Bug Fixes
114 |
115 | - Correctly export the component
116 |
117 |
118 |
119 | # [1.1.1](https://github.com/researchgate/react-fast-highlight/compare/v1.0.2...v1.1.0) (2016-05-20)
120 |
121 | ### Features
122 |
123 | - Use upstream highlight.js
124 | - Fixes errors with removed/renamed languages in newer versions.
125 | - Stays always updated for new languages
126 | - Fixes webpack bug with autoit language.
127 |
128 |
129 |
130 | ## [1.0.2](https://github.com/researchgate/react-fast-highlight/compare/v1.0.1...v1.0.2) (2016-04-28)
131 |
132 | ### Bug Fixes
133 |
134 | - Allow react 15
135 |
136 |
137 |
138 | ## [1.0.1](https://github.com/researchgate/react-fast-highlight/compare/v1.0.0...v1.0.1) (2016-03-14)
139 |
140 | ### Bug Fixes
141 |
142 | - Lower dependency version for react-addons-shallow-compare
143 |
144 |
145 |
146 | # [1.0.0](https://github.com/researchgate/react-fast-highlight/compare/v0.3.0...v1.0.0) (2016-02-21)
147 |
148 | ### Bug Fixes
149 |
150 | - Use react-addons-shallow-compare instead of the file in react itself
151 |
152 |
153 |
154 | # 0.3.0 (2016-01-20)
155 |
156 | ### Bug Fixes
157 |
158 | - Initial version
159 |
--------------------------------------------------------------------------------