highlight.js

/* @flow */

import {jsdom} from 'jsdom';
import noop from 'lodash/noop';
import constant from 'lodash/constant';
import {load} from 'cheerio';
import {transformElements, arrayToJSX} from './jsx';
import {isNode} from './util';

/**
 * CodeMirror syntax highlighting that works in the browser and on Node.js
 *
 * @module highlight
 */

// on Node.js, polyfill specific DOM requirements of CodeMirror
if (isNode) {
  global.window = jsdom().defaultView;
  global.navigator = window.navigator;
  global.document = window.document;

  /* @flowignore */
  document.createRange = constant({
    setEnd: noop,
    setStart: noop,
    getBoundingClientRect: constant({}),
    getClientRects: constant([])
  });
}

// we have to use normal requires because of the import hoisting
const cm = require('codemirror');

require('codemirror/mode/jsx/jsx');

/**
 * Using the CodeMirror editor highlights a string of text representing JavaScript program code.
 *
 * @memberof module:highlight
 * @private
 * @method highlight
 * @param {string} value - any valid ES2015, TypeScript, JSX, Flow code
 * @return {Object} an object containing a CheerioDOM object and a CheerioCollection of the `pre.CodeMirror-line`
 *                  elements
 */
export function highlight(value: string): {dom: any; lines: any} {
  const el = document.createElement('div');

  cm(el, {value, mode: {name: 'jsx', typescript: true}, scrollbarStyle: 'null', inputStyle: 'contenteditable'});

  const dom = load(el.innerHTML),
    lines = dom('.CodeMirror-line');

  // eslint-disable-next-line lodash/prefer-lodash-method
  lines.find('> span').removeAttr('style');

  return {dom, lines};
}

/**
 * Using the CodeMirror editor highlights a string of text representing JavaScript program code and returns an HTML
 * string.
 *
 * @memberof module:highlight
 * @method highlightHTML
 * @param {string} [code=""] - any valid ES2015, TypeScript, JSX, Flow code
 * @return {string} an HTML string of the `pre.CodeMirror-line` elements
 * @example
 * import {highlightHTML} from 'webcompiler';
 * // or - import {highlightHTML} from 'webcompiler/lib/highlight';
 * // or - var highlightHTML = require('webcompiler').highlightHTML;
 * // or - var highlightHTML = require('webcompiler/lib/highlight').highlightHTML;
 *
 * highlightHTML('function myScript(){return 100;}'); // <pre class="CodeMirror-line">...</pre>
 */
export function highlightHTML(code: string = ''): string {
  if (!code) {
    return '';
  }
  const {dom, lines} = highlight(code);

  return dom.html(lines);
}

/**
 * Using the CodeMirror editor highlights a string of text representing JavaScript program code and returns an array of
 * plain objects describing React Elements for easy serialization/unserialization.
 *
 * @memberof module:highlight
 * @method highlightArray
 * @param {string} [code=""] - any valid ES2015, TypeScript, JSX, Flow code
 * @return {Array<string|Object>} an array of plain objects describing React Elements
 * @example
 * import {highlightArray} from 'webcompiler';
 * // or - import {highlightArray} from 'webcompiler/lib/highlight';
 * // or - var highlightArray = require('webcompiler').highlightArray;
 * // or - var highlightArray = require('webcompiler/lib/highlight').highlightArray;
 *
 * highlightArray('function myScript(){return 100;}');
 * // [{type: 'pre', props: {className: 'CodeMirror-line'}, children: [...]}]
 */
export function highlightArray(code: string = ''): Array<string | Object> {
  if (!code) {
    return [];
  }
  const {lines} = highlight(code);

  return transformElements(lines.toArray());
}

/**
 * Using the CodeMirror editor highlights a string of text representing JavaScript program code and returns an array
 * of React elements.
 *
 * @memberof module:highlight
 * @method highlightJSX
 * @param {string} [code=""] - any valid ES2015, TypeScript, JSX, Flow code
 * @return {Array<ReactElement>} React elements of the `pre.CodeMirror-line` elements
 * @example
 * import {highlightJSX} from 'webcompiler';
 * // or - import {highlightJSX} from 'webcompiler/lib/highlight';
 * // or - var highlightJSX = require('webcompiler').highlightJSX;
 * // or - var highlightJSX = require('webcompiler/lib/highlight').highlightJSX;
 *
 * <div className="CodeMirror cm-s-monokai">{highlightJSX('function myScript(){return 100;}')}</div>
 */
export function highlightJSX(code: string = ''): any[] {
  return arrayToJSX(highlightArray(code));
}