[epiphany] highlight.js: update to version 10.1.0



commit 9cb2748867137b1e1fccc16fabca32427df075f1
Author: Jim Mason <jmason ibinx com>
Date:   Fri Jun 12 16:47:37 2020 +0100

    highlight.js: update to version 10.1.0
    
    fixes #1230

 third-party/highlightjs/highlight.js | 1403 +++++++++++++++++++++++++---------
 1 file changed, 1043 insertions(+), 360 deletions(-)
---
diff --git a/third-party/highlightjs/highlight.js b/third-party/highlightjs/highlight.js
index c62a3082e..5d3214cb6 100644
--- a/third-party/highlightjs/highlight.js
+++ b/third-party/highlightjs/highlight.js
@@ -1,5 +1,5 @@
 /*
-  Highlight.js 10.0.2 (e29f8f7d)
+  Highlight.js 10.1.0 (74de6eaa)
   License: BSD-3-Clause
   Copyright (c) 2006-2020, Ivan Sagalaev
 */
@@ -7,61 +7,106 @@ var hljs = (function () {
   'use strict';
 
   // https://github.com/substack/deep-freeze/blob/master/index.js
-  function deepFreeze (o) {
-    Object.freeze(o);
+  
+  function deepFreeze(obj) {
+    Object.freeze(obj);
 
-    var objIsFunction = typeof o === 'function';
+    var objIsFunction = typeof obj === 'function';
 
-    Object.getOwnPropertyNames(o).forEach(function (prop) {
-      if (o.hasOwnProperty(prop)
-      && o[prop] !== null
-      && (typeof o[prop] === "object" || typeof o[prop] === "function")
+    Object.getOwnPropertyNames(obj).forEach(function(prop) {
+      if (Object.hasOwnProperty.call(obj, prop)
+      && obj[prop] !== null
+      && (typeof obj[prop] === "object" || typeof obj[prop] === "function")
       // IE11 fix: https://github.com/highlightjs/highlight.js/issues/2318
       // TODO: remove in the future
       && (objIsFunction ? prop !== 'caller' && prop !== 'callee' && prop !== 'arguments' : true)
-      && !Object.isFrozen(o[prop])) {
-        deepFreeze(o[prop]);
+      && !Object.isFrozen(obj[prop])) {
+        deepFreeze(obj[prop]);
       }
     });
 
-    return o;
+    return obj;
   }
 
-  function escapeHTML(value) {
-    return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+  class Response {
+    /**
+     * @param {CompiledMode} mode
+     */
+    constructor(mode) {
+      // eslint-disable-next-line no-undefined
+      if (mode.data === undefined) mode.data = {};
+
+      this.data = mode.data;
+    }
+
+    ignoreMatch() {
+      this.ignore = true;
+    }
   }
 
+  /**
+   * @param {string} value
+   * @returns {string}
+   */
+  function escapeHTML(value) {
+    return value
+      .replace(/&/g, '&amp;')
+      .replace(/</g, '&lt;')
+      .replace(/>/g, '&gt;')
+      .replace(/"/g, '&quot;')
+      .replace(/'/g, '&#x27;');
+  }
 
-  
-  function inherit(parent) {  // inherit(parent, override_obj, override_obj, ...)
-    var key;
+  /**
+   * performs a shallow merge of multiple objects into one
+   *
+   * @template T
+   * @param {T} original
+   * @param {Record<string,any>[]} objects
+   * @returns {T} a single new object
+   */
+  function inherit(original, ...objects) {
+    /** @type Record<string,any> */
     var result = {};
-    var objects = Array.prototype.slice.call(arguments, 1);
 
-    for (key in parent)
-      result[key] = parent[key];
+    for (const key in original) {
+      result[key] = original[key];
+    }
     objects.forEach(function(obj) {
-      for (key in obj)
+      for (const key in obj) {
         result[key] = obj[key];
+      }
     });
-    return result;
+    return /** @type {T} */ (result);
   }
 
   /* Stream merging */
 
+  /**
+   * @typedef Event
+   * @property {'start'|'stop'} event
+   * @property {number} offset
+   * @property {Node} node
+   */
 
+  /**
+   * @param {Node} node
+   */
   function tag(node) {
     return node.nodeName.toLowerCase();
   }
 
-
+  /**
+   * @param {Node} node
+   */
   function nodeStream(node) {
+    /** @type Event[] */
     var result = [];
     (function _nodeStream(node, offset) {
       for (var child = node.firstChild; child; child = child.nextSibling) {
-        if (child.nodeType === 3)
+        if (child.nodeType === 3) {
           offset += child.nodeValue.length;
-        else if (child.nodeType === 1) {
+        } else if (child.nodeType === 1) {
           result.push({
             event: 'start',
             offset: offset,
@@ -85,6 +130,11 @@ var hljs = (function () {
     return result;
   }
 
+  /**
+   * @param {any} original - the original stream
+   * @param {any} highlighted - stream of the highlighted source
+   * @param {string} value - the original source itself
+   */
   function mergeStreams(original, highlighted, value) {
     var processed = 0;
     var result = '';
@@ -116,17 +166,28 @@ var hljs = (function () {
       return highlighted[0].event === 'start' ? original : highlighted;
     }
 
+    /**
+     * @param {Node} node
+     */
     function open(node) {
-      function attr_str(a) {
-        return ' ' + a.nodeName + '="' + escapeHTML(a.value).replace(/"/g, '&quot;') + '"';
+      /** @param {Attr} attr */
+      function attr_str(attr) {
+        return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"';
       }
+      // @ts-ignore
       result += '<' + tag(node) + [].map.call(node.attributes, attr_str).join('') + '>';
     }
 
+    /**
+     * @param {Node} node
+     */
     function close(node) {
       result += '</' + tag(node) + '>';
     }
 
+    /**
+     * @param {Event} event
+     */
     function render(event) {
       (event.event === 'start' ? open : close)(event.node);
     }
@@ -168,76 +229,128 @@ var hljs = (function () {
     mergeStreams: mergeStreams
   });
 
+  /**
+   * @typedef {object} Renderer
+   * @property {(text: string) => void} addText
+   * @property {(node: Node) => void} openNode
+   * @property {(node: Node) => void} closeNode
+   * @property {() => string} value
+   */
+
+  /** @typedef {{kind?: string, sublanguage?: boolean}} Node */
+  /** @typedef {{walk: (r: Renderer) => void}} Tree */
+  /** */
+
   const SPAN_CLOSE = '</span>';
 
+  /**
+   * Determines if a node needs to be wrapped in <span>
+   *
+   * @param {Node} node */
   const emitsWrappingTags = (node) => {
     return !!node.kind;
   };
 
+  /** @type {Renderer} */
   class HTMLRenderer {
-    constructor(tree, options) {
+    /**
+     * Creates a new HTMLRenderer
+     *
+     * @param {Tree} parseTree - the parse tree (must support `walk` API)
+     * @param {{classPrefix: string}} options
+     */
+    constructor(parseTree, options) {
       this.buffer = "";
       this.classPrefix = options.classPrefix;
-      tree.walk(this);
+      parseTree.walk(this);
     }
 
-    // renderer API
-
+    /**
+     * Adds texts to the output stream
+     *
+     * @param {string} text */
     addText(text) {
       this.buffer += escapeHTML(text);
     }
 
+    /**
+     * Adds a node open to the output stream (if needed)
+     *
+     * @param {Node} node */
     openNode(node) {
       if (!emitsWrappingTags(node)) return;
 
       let className = node.kind;
-      if (!node.sublanguage)
+      if (!node.sublanguage) {
         className = `${this.classPrefix}${className}`;
+      }
       this.span(className);
     }
 
+    /**
+     * Adds a node close to the output stream (if needed)
+     *
+     * @param {Node} node */
     closeNode(node) {
       if (!emitsWrappingTags(node)) return;
 
       this.buffer += SPAN_CLOSE;
     }
 
+    /**
+     * returns the accumulated buffer
+    */
+    value() {
+      return this.buffer;
+    }
+
     // helpers
 
+    /**
+     * Builds a span element
+     *
+     * @param {string} className */
     span(className) {
       this.buffer += `<span class="${className}">`;
     }
-
-    value() {
-      return this.buffer;
-    }
   }
 
+  /** @typedef {{kind?: string, sublanguage?: boolean, children: Node[]} | string} Node */
+  /** @typedef {{kind?: string, sublanguage?: boolean, children: Node[]} } DataNode */
+  /**  */
+
   class TokenTree {
     constructor() {
+      /** @type DataNode */
       this.rootNode = { children: [] };
-      this.stack = [ this.rootNode ];
+      this.stack = [this.rootNode];
     }
 
     get top() {
       return this.stack[this.stack.length - 1];
     }
 
-    get root() { return this.rootNode };
+    get root() { return this.rootNode; }
 
+    /** @param {Node} node */
     add(node) {
       this.top.children.push(node);
     }
 
+    /** @param {string} kind */
     openNode(kind) {
-      let node = { kind, children: [] };
+      /** @type Node */
+      const node = { kind, children: [] };
       this.add(node);
       this.stack.push(node);
     }
 
     closeNode() {
-      if (this.stack.length > 1)
+      if (this.stack.length > 1) {
         return this.stack.pop();
+      }
+      // eslint-disable-next-line no-undefined
+      return undefined;
     }
 
     closeAllNodes() {
@@ -248,10 +361,21 @@ var hljs = (function () {
       return JSON.stringify(this.rootNode, null, 4);
     }
 
+    /**
+     * @typedef { import("./html_renderer").Renderer } Renderer
+     * @param {Renderer} builder
+     */
     walk(builder) {
+      // this does not
       return this.constructor._walk(builder, this.rootNode);
+      // this works
+      // return TokenTree._walk(builder, this.rootNode);
     }
 
+    /**
+     * @param {Renderer} builder
+     * @param {Node} node
+     */
     static _walk(builder, node) {
       if (typeof node === "string") {
         builder.addText(node);
@@ -263,16 +387,19 @@ var hljs = (function () {
       return builder;
     }
 
+    /**
+     * @param {Node} node
+     */
     static _collapse(node) {
-      if (!node.children) {
-        return;
-      }
+      if (typeof node === "string") return;
+      if (!node.children) return;
+
       if (node.children.every(el => typeof el === "string")) {
-        node.text = node.children.join("");
-        delete node["children"];
+        // node.text = node.children.join("");
+        // delete node.children;
+        node.children = [node.children.join("")];
       } else {
         node.children.forEach((child) => {
-          if (typeof child === "string") return;
           TokenTree._collapse(child);
         });
       }
@@ -287,7 +414,7 @@ var hljs = (function () {
 
     - addKeyword(text, kind)
     - addText(text)
-    - addSublanguage(emitter, subLangaugeName)
+    - addSublanguage(emitter, subLanguageName)
     - finalize()
     - openNode(kind)
     - closeNode()
@@ -295,12 +422,23 @@ var hljs = (function () {
     - toHTML()
 
   */
+
+  /**
+   * @implements {Emitter}
+   */
   class TokenTreeEmitter extends TokenTree {
+    /**
+     * @param {*} options
+     */
     constructor(options) {
       super();
       this.options = options;
     }
 
+    /**
+     * @param {string} text
+     * @param {string} kind
+     */
     addKeyword(text, kind) {
       if (text === "") { return; }
 
@@ -309,44 +447,78 @@ var hljs = (function () {
       this.closeNode();
     }
 
+    /**
+     * @param {string} text
+     */
     addText(text) {
       if (text === "") { return; }
 
       this.add(text);
     }
 
+    /**
+     * @param {Emitter & {root: DataNode}} emitter
+     * @param {string} name
+     */
     addSublanguage(emitter, name) {
-      let node = emitter.root;
+      /** @type DataNode */
+      const node = emitter.root;
       node.kind = name;
       node.sublanguage = true;
       this.add(node);
     }
 
     toHTML() {
-      let renderer = new HTMLRenderer(this, this.options);
+      const renderer = new HTMLRenderer(this, this.options);
       return renderer.value();
     }
 
     finalize() {
-      return;
+      return true;
     }
-
   }
 
+  /**
+   * @param {string} value
+   * @returns {RegExp}
+   * */
   function escape(value) {
-    return new RegExp(value.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), 'm');
+    return new RegExp(value.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), 'm');
   }
 
+  /**
+   * @param {RegExp | string } re
+   * @returns {string}
+   */
   function source(re) {
-    // if it's a regex get it's source,
-    // otherwise it's a string already so just return it
-    return (re && re.source) || re;
+    if (!re) return null;
+    if (typeof re === "string") return re;
+
+    return re.source;
+  }
+
+  /**
+   * @param {...(RegExp | string) } args
+   * @returns {string}
+   */
+  function concat(...args) {
+    const joined = args.map((x) => source(x)).join("");
+    return joined;
   }
 
+  /**
+   * @param {RegExp} re
+   * @returns {number}
+   */
   function countMatchGroups(re) {
     return (new RegExp(re.toString() + '|')).exec('').length - 1;
   }
 
+  /**
+   * Does lexeme start with a regular expression match at the beginning
+   * @param {RegExp} re
+   * @param {string} lexeme
+   */
   function startsWith(re, lexeme) {
     var match = re && re.exec(lexeme);
     return match && match.index === 0;
@@ -357,7 +529,12 @@ var hljs = (function () {
   // it also places each individual regular expression into it's own
   // match group, keeping track of the sequencing of those match groups
   // is currently an exercise for the caller. :-)
-  function join(regexps, separator) {
+  /**
+   * @param {(string | RegExp)[]} regexps
+   * @param {string} separator
+   * @returns {string}
+   */
+  function join(regexps, separator = "|") {
     // backreferenceRe matches an open parenthesis or backreference. To avoid
     // an incorrect parse, it additionally matches the following:
     // - [...] elements, where the meaning of parentheses and escapes change
@@ -384,12 +561,12 @@ var hljs = (function () {
         }
         ret += re.substring(0, match.index);
         re = re.substring(match.index + match[0].length);
-        if (match[0][0] == '\\' && match[1]) {
+        if (match[0][0] === '\\' && match[1]) {
           // Adjust the backreference.
           ret += '\\' + String(Number(match[1]) + offset);
         } else {
           ret += match[0];
-          if (match[0] == '(') {
+          if (match[0] === '(') {
             numCaptures++;
           }
         }
@@ -407,38 +584,73 @@ var hljs = (function () {
   const BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b...
   const RE_STARTERS_RE = 
'!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~';
 
+  /**
+  * @param { Partial<Mode> & {binary?: string | RegExp} } opts
+  */
+  const SHEBANG = (opts = {}) => {
+    const beginShebang = /^#![ ]*\//;
+    if (opts.binary) {
+      opts.begin = concat(
+        beginShebang,
+        /.*\b/,
+        opts.binary,
+        /\b.*/);
+    }
+    return inherit({
+      className: 'meta',
+      begin: beginShebang,
+      end: /$/,
+      relevance: 0,
+      /** @type {ModeCallback} */
+      "on:begin": (m, resp) => {
+        if (m.index !== 0) resp.ignoreMatch();
+      }
+    }, opts);
+  };
+
   // Common modes
   const BACKSLASH_ESCAPE = {
     begin: '\\\\[\\s\\S]', relevance: 0
   };
   const APOS_STRING_MODE = {
     className: 'string',
-    begin: '\'', end: '\'',
+    begin: '\'',
+    end: '\'',
     illegal: '\\n',
     contains: [BACKSLASH_ESCAPE]
   };
   const QUOTE_STRING_MODE = {
     className: 'string',
-    begin: '"', end: '"',
+    begin: '"',
+    end: '"',
     illegal: '\\n',
     contains: [BACKSLASH_ESCAPE]
   };
   const PHRASAL_WORDS_MODE = {
     begin: 
/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/
   };
-  const COMMENT = function (begin, end, inherits) {
+  /**
+   * Creates a comment mode
+   *
+   * @param {string | RegExp} begin
+   * @param {string | RegExp} end
+   * @param {Mode | {}} [modeOptions]
+   * @returns {Partial<Mode>}
+   */
+  const COMMENT = function(begin, end, modeOptions = {}) {
     var mode = inherit(
       {
         className: 'comment',
-        begin: begin, end: end,
+        begin,
+        end,
         contains: []
       },
-      inherits || {}
+      modeOptions
     );
     mode.contains.push(PHRASAL_WORDS_MODE);
     mode.contains.push({
       className: 'doctag',
-      begin: '(?:TODO|FIXME|NOTE|BUG|XXX):',
+      begin: '(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):',
       relevance: 0
     });
     return mode;
@@ -464,7 +676,7 @@ var hljs = (function () {
   const CSS_NUMBER_MODE = {
     className: 'number',
     begin: NUMBER_RE + '(' +
-      '%|em|ex|ch|rem'  +
+      '%|em|ex|ch|rem' +
       '|vw|vh|vmin|vmax' +
       '|cm|mm|in|pt|pc|px' +
       '|deg|grad|rad|turn' +
@@ -481,15 +693,17 @@ var hljs = (function () {
     //     3 / something
     //
     // (which will then blow up when regex's `illegal` sees the newline)
-    begin: /(?=\/[^\/\n]*\/)/,
+    begin: /(?=\/[^/\n]*\/)/,
     contains: [{
       className: 'regexp',
-      begin: /\//, end: /\/[gimuy]*/,
+      begin: /\//,
+      end: /\/[gimuy]*/,
       illegal: /\n/,
       contains: [
         BACKSLASH_ESCAPE,
         {
-          begin: /\[/, end: /\]/,
+          begin: /\[/,
+          end: /\]/,
           relevance: 0,
           contains: [BACKSLASH_ESCAPE]
         }
@@ -512,6 +726,23 @@ var hljs = (function () {
     relevance: 0
   };
 
+  /**
+   * Adds end same as begin mechanics to a mode
+   *
+   * Your mode must include at least a single () match group as that first match
+   * group is what is used for comparison
+   * @param {Partial<Mode>} mode
+   */
+  const END_SAME_AS_BEGIN = function(mode) {
+    return Object.assign(mode,
+      {
+        /** @type {ModeCallback} */
+        'on:begin': (m, resp) => { resp.data._beginMatch = m[1]; },
+        /** @type {ModeCallback} */
+        'on:end': (m, resp) => { if (resp.data._beginMatch !== m[1]) resp.ignoreMatch(); }
+      });
+  };
+
   var MODES = /*#__PURE__*/Object.freeze({
     __proto__: null,
     IDENT_RE: IDENT_RE,
@@ -520,6 +751,7 @@ var hljs = (function () {
     C_NUMBER_RE: C_NUMBER_RE,
     BINARY_NUMBER_RE: BINARY_NUMBER_RE,
     RE_STARTERS_RE: RE_STARTERS_RE,
+    SHEBANG: SHEBANG,
     BACKSLASH_ESCAPE: BACKSLASH_ESCAPE,
     APOS_STRING_MODE: APOS_STRING_MODE,
     QUOTE_STRING_MODE: QUOTE_STRING_MODE,
@@ -535,7 +767,8 @@ var hljs = (function () {
     REGEXP_MODE: REGEXP_MODE,
     TITLE_MODE: TITLE_MODE,
     UNDERSCORE_TITLE_MODE: UNDERSCORE_TITLE_MODE,
-    METHOD_GUARD: METHOD_GUARD
+    METHOD_GUARD: METHOD_GUARD,
+    END_SAME_AS_BEGIN: END_SAME_AS_BEGIN
   });
 
   // keywords that should have no default relevance value
@@ -543,8 +776,21 @@ var hljs = (function () {
 
   // compilation
 
+  /**
+   * Compiles a language definition result
+   *
+   * Given the raw result of a language definition (Language), compiles this so
+   * that it is ready for highlighting code.
+   * @param {Language} language
+   * @returns {CompiledLanguage}
+   */
   function compileLanguage(language) {
-
+    /**
+     * Builds a regex with the case sensativility of the current language
+     *
+     * @param {RegExp | string} value
+     * @param {boolean} [global]
+     */
     function langRe(value, global) {
       return new RegExp(
         source(value),
@@ -568,13 +814,16 @@ var hljs = (function () {
     class MultiRegex {
       constructor() {
         this.matchIndexes = {};
+        // @ts-ignore
         this.regexes = [];
         this.matchAt = 1;
         this.position = 0;
       }
 
+      // @ts-ignore
       addRule(re, opts) {
         opts.position = this.position++;
+        // @ts-ignore
         this.matchIndexes[this.matchAt] = opts;
         this.regexes.push([opts, re]);
         this.matchAt += countMatchGroups(re) + 1;
@@ -583,20 +832,27 @@ var hljs = (function () {
       compile() {
         if (this.regexes.length === 0) {
           // avoids the need to check length every time exec is called
+          // @ts-ignore
           this.exec = () => null;
         }
-        let terminators = this.regexes.map(el => el[1]);
-        this.matcherRe = langRe(join(terminators, '|'), true);
+        const terminators = this.regexes.map(el => el[1]);
+        this.matcherRe = langRe(join(terminators), true);
         this.lastIndex = 0;
       }
 
+      /** @param {string} s */
       exec(s) {
         this.matcherRe.lastIndex = this.lastIndex;
-        let match = this.matcherRe.exec(s);
+        const match = this.matcherRe.exec(s);
         if (!match) { return null; }
 
-        let i = match.findIndex((el, i) => i>0 && el!=undefined);
-        let matchData = this.matchIndexes[i];
+        // eslint-disable-next-line no-undefined
+        const i = match.findIndex((el, i) => i > 0 && el !== undefined);
+        // @ts-ignore
+        const matchData = this.matchIndexes[i];
+        // trim off any earlier non-relevant match groups (ie, the other regex
+        // match groups that make up the multi-matcher)
+        match.splice(0, i);
 
         return Object.assign(match, matchData);
       }
@@ -635,7 +891,9 @@ var hljs = (function () {
     */
     class ResumableMultiRegex {
       constructor() {
+        // @ts-ignore
         this.rules = [];
+        // @ts-ignore
         this.multiRegexes = [];
         this.count = 0;
 
@@ -643,11 +901,12 @@ var hljs = (function () {
         this.regexIndex = 0;
       }
 
+      // @ts-ignore
       getMatcher(index) {
         if (this.multiRegexes[index]) return this.multiRegexes[index];
 
-        let matcher = new MultiRegex();
-        this.rules.slice(index).forEach(([re, opts])=> matcher.addRule(re,opts));
+        const matcher = new MultiRegex();
+        this.rules.slice(index).forEach(([re, opts]) => matcher.addRule(re, opts));
         matcher.compile();
         this.multiRegexes[index] = matcher;
         return matcher;
@@ -657,19 +916,22 @@ var hljs = (function () {
         this.regexIndex = 0;
       }
 
+      // @ts-ignore
       addRule(re, opts) {
         this.rules.push([re, opts]);
-        if (opts.type==="begin") this.count++;
+        if (opts.type === "begin") this.count++;
       }
 
+      /** @param {string} s */
       exec(s) {
-        let m = this.getMatcher(this.regexIndex);
+        const m = this.getMatcher(this.regexIndex);
         m.lastIndex = this.lastIndex;
-        let result = m.exec(s);
+        const result = m.exec(s);
         if (result) {
           this.regexIndex += result.position + 1;
-          if (this.regexIndex === this.count) // wrap-around
+          if (this.regexIndex === this.count) { // wrap-around
             this.regexIndex = 0;
+          }
         }
 
         // this.regexIndex = 0;
@@ -677,26 +939,43 @@ var hljs = (function () {
       }
     }
 
+    /**
+     * Given a mode, builds a huge ResumableMultiRegex that can be used to walk
+     * the content and find matches.
+     *
+     * @param {CompiledMode} mode
+     * @returns {ResumableMultiRegex}
+     */
     function buildModeRegex(mode) {
+      const mm = new ResumableMultiRegex();
 
-      let mm = new ResumableMultiRegex();
-
-      mode.contains.forEach(term => mm.addRule(term.begin, {rule: term, type: "begin" }));
+      mode.contains.forEach(term => mm.addRule(term.begin, { rule: term, type: "begin" }));
 
-      if (mode.terminator_end)
-        mm.addRule(mode.terminator_end, {type: "end"} );
-      if (mode.illegal)
-        mm.addRule(mode.illegal, {type: "illegal"} );
+      if (mode.terminator_end) {
+        mm.addRule(mode.terminator_end, { type: "end" });
+      }
+      if (mode.illegal) {
+        mm.addRule(mode.illegal, { type: "illegal" });
+      }
 
       return mm;
     }
 
     // TODO: We need negative look-behind support to do this properly
-    function skipIfhasPrecedingOrTrailingDot(match) {
-      let before = match.input[match.index-1];
-      let after = match.input[match.index + match[0].length];
+    /**
+     * Skip a match if it has a preceding or trailing dot
+     *
+     * This is used for `beginKeywords` to prevent matching expressions such as
+     * `bob.keyword.do()`. The mode compiler automatically wires this up as a
+     * special _internal_ 'on:begin' callback for modes with `beginKeywords`
+     * @param {RegExpMatchArray} match
+     * @param {CallbackResponse} response
+     */
+    function skipIfhasPrecedingOrTrailingDot(match, response) {
+      const before = match.input[match.index - 1];
+      const after = match.input[match.index + match[0].length];
       if (before === "." || after === ".") {
-        return {ignoreMatch: true };
+        response.ignoreMatch();
       }
     }
 
@@ -730,19 +1009,43 @@ var hljs = (function () {
      *             - The parser cursor is not moved forward.
      */
 
+    /**
+     * Compiles an individual mode
+     *
+     * This can raise an error if the mode contains certain detectable known logic
+     * issues.
+     * @param {Mode} mode
+     * @param {CompiledMode | null} [parent]
+     * @returns {CompiledMode | never}
+     */
     function compileMode(mode, parent) {
-      if (mode.compiled)
-        return;
+      const cmode = /** @type CompiledMode */ (mode);
+      if (mode.compiled) return cmode;
       mode.compiled = true;
 
-      // __onBegin is considered private API, internal use only
-      mode.__onBegin = null;
+      // __beforeBegin is considered private API, internal use only
+      mode.__beforeBegin = null;
 
       mode.keywords = mode.keywords || mode.beginKeywords;
-      if (mode.keywords)
+
+      let kw_pattern = null;
+      if (typeof mode.keywords === "object") {
+        kw_pattern = mode.keywords.$pattern;
+        delete mode.keywords.$pattern;
+      }
+
+      if (mode.keywords) {
         mode.keywords = compileKeywords(mode.keywords, language.case_insensitive);
+      }
+
+      // both are not allowed
+      if (mode.lexemes && kw_pattern) {
+        throw new Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode 
reference) ");
+      }
 
-      mode.lexemesRe = langRe(mode.lexemes || /\w+/, true);
+      // `mode.lexemes` was the old standard before we added and now recommend
+      // using `keywords.$pattern` to pass the keyword pattern
+      cmode.keywordPatternRe = langRe(mode.lexemes || kw_pattern || /\w+/, true);
 
       if (parent) {
         if (mode.beginKeywords) {
@@ -752,122 +1055,171 @@ var hljs = (function () {
           // doesn't allow spaces in keywords anyways and we still check for the boundary
           // first
           mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')(?=\\b|\\s)';
-          mode.__onBegin = skipIfhasPrecedingOrTrailingDot;
+          mode.__beforeBegin = skipIfhasPrecedingOrTrailingDot;
+        }
+        if (!mode.begin) mode.begin = /\B|\b/;
+        cmode.beginRe = langRe(mode.begin);
+        if (mode.endSameAsBegin) mode.end = mode.begin;
+        if (!mode.end && !mode.endsWithParent) mode.end = /\B|\b/;
+        if (mode.end) cmode.endRe = langRe(mode.end);
+        cmode.terminator_end = source(mode.end) || '';
+        if (mode.endsWithParent && parent.terminator_end) {
+          cmode.terminator_end += (mode.end ? '|' : '') + parent.terminator_end;
         }
-        if (!mode.begin)
-          mode.begin = /\B|\b/;
-        mode.beginRe = langRe(mode.begin);
-        if (mode.endSameAsBegin)
-          mode.end = mode.begin;
-        if (!mode.end && !mode.endsWithParent)
-          mode.end = /\B|\b/;
-        if (mode.end)
-          mode.endRe = langRe(mode.end);
-        mode.terminator_end = source(mode.end) || '';
-        if (mode.endsWithParent && parent.terminator_end)
-          mode.terminator_end += (mode.end ? '|' : '') + parent.terminator_end;
-      }
-      if (mode.illegal)
-        mode.illegalRe = langRe(mode.illegal);
-      if (mode.relevance == null)
-        mode.relevance = 1;
-      if (!mode.contains) {
-        mode.contains = [];
       }
+      if (mode.illegal) cmode.illegalRe = langRe(mode.illegal);
+      // eslint-disable-next-line no-undefined
+      if (mode.relevance === undefined) mode.relevance = 1;
+      if (!mode.contains) mode.contains = [];
+
       mode.contains = [].concat(...mode.contains.map(function(c) {
         return expand_or_clone_mode(c === 'self' ? mode : c);
       }));
-      mode.contains.forEach(function(c) {compileMode(c, mode);});
+      mode.contains.forEach(function(c) { compileMode(/** @type Mode */ (c), cmode); });
 
       if (mode.starts) {
         compileMode(mode.starts, parent);
       }
 
-      mode.matcher = buildModeRegex(mode);
+      cmode.matcher = buildModeRegex(cmode);
+      return cmode;
     }
 
     // self is not valid at the top-level
     if (language.contains && language.contains.includes('self')) {
-      throw new Error("ERR: contains `self` is not supported at the top-level of a language.  See 
documentation.")
+      throw new Error("ERR: contains `self` is not supported at the top-level of a language.  See 
documentation.");
     }
-    compileMode(language);
+    return compileMode(/** @type Mode */ (language));
   }
 
+  /**
+   * Determines if a mode has a dependency on it's parent or not
+   *
+   * If a mode does have a parent dependency then often we need to clone it if
+   * it's used in multiple places so that each copy points to the correct parent,
+   * where-as modes without a parent can often safely be re-used at the bottom of
+   * a mode chain.
+   *
+   * @param {Mode | null} mode
+   * @returns {boolean} - is there a dependency on the parent?
+   * */
   function dependencyOnParent(mode) {
     if (!mode) return false;
 
     return mode.endsWithParent || dependencyOnParent(mode.starts);
   }
 
+  /**
+   * Expands a mode or clones it if necessary
+   *
+   * This is necessary for modes with parental dependenceis (see notes on
+   * `dependencyOnParent`) and for nodes that have `variants` - which must then be
+   * exploded into their own individual modes at compile time.
+   *
+   * @param {Mode} mode
+   * @returns {Mode | Mode[]}
+   * */
   function expand_or_clone_mode(mode) {
     if (mode.variants && !mode.cached_variants) {
       mode.cached_variants = mode.variants.map(function(variant) {
-        return inherit(mode, {variants: null}, variant);
+        return inherit(mode, { variants: null }, variant);
       });
     }
 
     // EXPAND
     // if we have variants then essentially "replace" the mode with the variants
     // this happens in compileMode, where this function is called from
-    if (mode.cached_variants)
+    if (mode.cached_variants) {
       return mode.cached_variants;
+    }
 
     // CLONE
     // if we have dependencies on parents then we need a unique
     // instance of ourselves, so we can be reused with many
     // different parents without issue
-    if (dependencyOnParent(mode))
+    if (dependencyOnParent(mode)) {
       return inherit(mode, { starts: mode.starts ? inherit(mode.starts) : null });
+    }
 
-    if (Object.isFrozen(mode))
+    if (Object.isFrozen(mode)) {
       return inherit(mode);
+    }
 
     // no special dependency issues, just return ourselves
     return mode;
   }
 
+  /***********************************************
+    Keywords
+  ***********************************************/
 
-  // keywords
-
+  /**
+   * Given raw keywords from a language definition, compile them.
+   *
+   * @param {string | Record<string,string>} rawKeywords
+   * @param {boolean} case_insensitive
+   */
   function compileKeywords(rawKeywords, case_insensitive) {
+    /** @type KeywordDict */
     var compiled_keywords = {};
 
     if (typeof rawKeywords === 'string') { // string
       splitAndCompile('keyword', rawKeywords);
     } else {
-      Object.keys(rawKeywords).forEach(function (className) {
+      Object.keys(rawKeywords).forEach(function(className) {
         splitAndCompile(className, rawKeywords[className]);
       });
     }
-  return compiled_keywords;
+    return compiled_keywords;
 
-  // ---
+    // ---
 
-  function splitAndCompile(className, str) {
-    if (case_insensitive) {
-      str = str.toLowerCase();
+    /**
+     * Compiles an individual list of keywords
+     *
+     * Ex: "for if when while|5"
+     *
+     * @param {string} className
+     * @param {string} keywordList
+     */
+    function splitAndCompile(className, keywordList) {
+      if (case_insensitive) {
+        keywordList = keywordList.toLowerCase();
+      }
+      keywordList.split(' ').forEach(function(keyword) {
+        var pair = keyword.split('|');
+        compiled_keywords[pair[0]] = [className, scoreForKeyword(pair[0], pair[1])];
+      });
     }
-    str.split(' ').forEach(function(keyword) {
-      var pair = keyword.split('|');
-      compiled_keywords[pair[0]] = [className, scoreForKeyword(pair[0], pair[1])];
-    });
-  }
   }
 
+  /**
+   * Returns the proper score for a given keyword
+   *
+   * Also takes into account comment keywords, which will be scored 0 UNLESS
+   * another score has been manually assigned.
+   * @param {string} keyword
+   * @param {string} [providedScore]
+   */
   function scoreForKeyword(keyword, providedScore) {
-  // manual scores always win over common keywords
-  // so you can force a score of 1 if you really insist
-  if (providedScore)
-    return Number(providedScore);
+    // manual scores always win over common keywords
+    // so you can force a score of 1 if you really insist
+    if (providedScore) {
+      return Number(providedScore);
+    }
 
-  return commonKeyword(keyword) ? 0 : 1;
+    return commonKeyword(keyword) ? 0 : 1;
   }
 
-  function commonKeyword(word) {
-  return COMMON_KEYWORDS.includes(word.toLowerCase());
+  /**
+   * Determines if a given keyword is common or not
+   *
+   * @param {string} keyword */
+  function commonKeyword(keyword) {
+    return COMMON_KEYWORDS.includes(keyword.toLowerCase());
   }
 
-  var version = "10.0.2";
+  var version = "10.1.0";
 
   /*
   Syntax highlighting with language autodetection.
@@ -878,36 +1230,42 @@ var hljs = (function () {
   const inherit$1 = inherit;
 
   const { nodeStream: nodeStream$1, mergeStreams: mergeStreams$1 } = utils;
+  const NO_MATCH = Symbol("nomatch");
 
-
+  /**
+   * @param {any} hljs - object that is extended (legacy)
+   */
   const HLJS = function(hljs) {
-
     // Convenience variables for build-in objects
+    /** @type {unknown[]} */
     var ArrayProto = [];
 
     // Global internal variables used within the highlight.js library.
-    var languages = {},
-        aliases   = {},
-        plugins   = [];
+    /** @type {Record<string, Language>} */
+    var languages = {};
+    /** @type {Record<string, string>} */
+    var aliases = {};
+    /** @type {HLJSPlugin[]} */
+    var plugins = [];
 
     // safe/production mode - swallows more errors, tries to keep running
     // even if a single syntax or parse hits a fatal error
     var SAFE_MODE = true;
-
-    // Regular expressions used throughout the highlight.js library.
-    var fixMarkupRe      = /((^(<[^>]+>|\t|)+|(?:\n)))/gm;
-
+    var fixMarkupRe = /(^(<[^>]+>|\t|)+|\n)/gm;
     var LANGUAGE_NOT_FOUND = "Could not find the language '{}', did you forget to load/include a language 
module?";
+    /** @type {Language} */
+    const PLAINTEXT_LANGUAGE = { disableAutodetect: true, name: 'Plain text', contains: [] };
 
     // Global options used when within external APIs. This is modified when
     // calling the `hljs.configure` function.
+    /** @type HLJSOptions */
     var options = {
       noHighlightRe: /^(no-?highlight)$/i,
       languageDetectRe: /\blang(?:uage)?-([\w-]+)\b/i,
       classPrefix: 'hljs-',
       tabReplace: null,
       useBR: false,
-      languages: undefined,
+      languages: null,
       // beta configuration options, subject to change, welcome to discuss
       // https://github.com/highlightjs/highlight.js/issues/1086
       __emitter: TokenTreeEmitter
@@ -915,18 +1273,24 @@ var hljs = (function () {
 
     /* Utility functions */
 
-    function shouldNotHighlight(language) {
-      return options.noHighlightRe.test(language);
+    /**
+     * Tests a language name to see if highlighting should be skipped
+     * @param {string} languageName
+     */
+    function shouldNotHighlight(languageName) {
+      return options.noHighlightRe.test(languageName);
     }
 
+    /**
+     * @param {HighlightedHTMLElement} block - the HTML element to determine language for
+     */
     function blockLanguage(block) {
-      var match;
       var classes = block.className + ' ';
 
       classes += block.parentNode ? block.parentNode.className : '';
 
       // language-* takes precedence over non-prefixed class names.
-      match = options.languageDetectRe.exec(classes);
+      const match = options.languageDetectRe.exec(classes);
       if (match) {
         var language = getLanguage(match[1]);
         if (!language) {
@@ -946,18 +1310,19 @@ var hljs = (function () {
      *
      * @param {string} languageName - the language to use for highlighting
      * @param {string} code - the code to highlight
-     * @param {boolean} ignore_illegals - whether to ignore illegal matches, default is to bail
-     * @param {array<mode>} continuation - array of continuation modes
+     * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail
+     * @param {Mode} [continuation] - current continuation mode, if any
      *
-     * @returns an object that represents the result
+     * @returns {HighlightResult} Result - an object that represents the result
      * @property {string} language - the language name
      * @property {number} relevance - the relevance score
      * @property {string} value - the highlighted HTML code
      * @property {string} code - the original raw code
-     * @property {mode} top - top of the current mode stack
+     * @property {Mode} top - top of the current mode stack
      * @property {boolean} illegal - indicates whether any illegal matches were found
     */
-    function highlight(languageName, code, ignore_illegals, continuation) {
+    function highlight(languageName, code, ignoreIllegals, continuation) {
+      /** @type {{ code: string, language: string, result?: any }} */
       var context = {
         code,
         language: languageName
@@ -970,7 +1335,7 @@ var hljs = (function () {
       // in which case we don't even need to call highlight
       var result = context.result ?
         context.result :
-        _highlight(context.language, context.code, ignore_illegals, continuation);
+        _highlight(context.language, context.code, ignoreIllegals, continuation);
 
       result.code = context.code;
       // the plugin can change anything in result to suite it
@@ -979,56 +1344,54 @@ var hljs = (function () {
       return result;
     }
 
-    // private highlight that's used internally and does not fire callbacks
-    function _highlight(languageName, code, ignore_illegals, continuation) {
+    /**
+     * private highlight that's used internally and does not fire callbacks
+     *
+     * @param {string} languageName - the language to use for highlighting
+     * @param {string} code - the code to highlight
+     * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail
+     * @param {Mode} [continuation] - current continuation mode, if any
+    */
+    function _highlight(languageName, code, ignoreIllegals, continuation) {
       var codeToHighlight = code;
 
-      function endOfMode(mode, lexeme) {
-        if (startsWith(mode.endRe, lexeme)) {
-          while (mode.endsParent && mode.parent) {
-            mode = mode.parent;
-          }
-          return mode;
-        }
-        if (mode.endsWithParent) {
-          return endOfMode(mode.parent, lexeme);
-        }
-      }
-
-      function keywordMatch(mode, match) {
-        var match_str = language.case_insensitive ? match[0].toLowerCase() : match[0];
-        return mode.keywords.hasOwnProperty(match_str) && mode.keywords[match_str];
+      /**
+       * Return keyword data if a match is a keyword
+       * @param {CompiledMode} mode - current mode
+       * @param {RegExpMatchArray} match - regexp match data
+       * @returns {KeywordData | false}
+       */
+      function keywordData(mode, match) {
+        var matchText = language.case_insensitive ? match[0].toLowerCase() : match[0];
+        return Object.prototype.hasOwnProperty.call(mode.keywords, matchText) && mode.keywords[matchText];
       }
 
       function processKeywords() {
-        var keyword_match, last_index, match, buf;
-
         if (!top.keywords) {
           emitter.addText(mode_buffer);
           return;
         }
 
-        last_index = 0;
-        top.lexemesRe.lastIndex = 0;
-        match = top.lexemesRe.exec(mode_buffer);
-        buf = "";
+        let last_index = 0;
+        top.keywordPatternRe.lastIndex = 0;
+        let match = top.keywordPatternRe.exec(mode_buffer);
+        let buf = "";
 
         while (match) {
           buf += mode_buffer.substring(last_index, match.index);
-          keyword_match = keywordMatch(top, match);
-          var kind = null;
-          if (keyword_match) {
+          const data = keywordData(top, match);
+          if (data) {
+            const [kind, keywordRelevance] = data;
             emitter.addText(buf);
             buf = "";
 
-            relevance += keyword_match[1];
-            kind = keyword_match[0];
+            relevance += keywordRelevance;
             emitter.addKeyword(match[0], kind);
           } else {
             buf += match[0];
           }
-          last_index = top.lexemesRe.lastIndex;
-          match = top.lexemesRe.exec(mode_buffer);
+          last_index = top.keywordPatternRe.lastIndex;
+          match = top.keywordPatternRe.exec(mode_buffer);
         }
         buf += mode_buffer.substr(last_index);
         emitter.addText(buf);
@@ -1036,18 +1399,20 @@ var hljs = (function () {
 
       function processSubLanguage() {
         if (mode_buffer === "") return;
+        /** @type HighlightResult */
+        var result = null;
 
-        var explicit = typeof top.subLanguage === 'string';
-
-        if (explicit && !languages[top.subLanguage]) {
-          emitter.addText(mode_buffer);
-          return;
+        if (typeof top.subLanguage === 'string') {
+          if (!languages[top.subLanguage]) {
+            emitter.addText(mode_buffer);
+            return;
+          }
+          result = _highlight(top.subLanguage, mode_buffer, true, continuations[top.subLanguage]);
+          continuations[top.subLanguage] = result.top;
+        } else {
+          result = highlightAuto(mode_buffer, top.subLanguage.length ? top.subLanguage : null);
         }
 
-        var result = explicit ?
-                     _highlight(top.subLanguage, mode_buffer, true, continuations[top.subLanguage]) :
-                     highlightAuto(mode_buffer, top.subLanguage.length ? top.subLanguage : undefined);
-
         // Counting embedded language score towards the host language may be disabled
         // with zeroing the containing mode relevance. Use case in point is Markdown that
         // allows XML everywhere and makes every XML snippet to have a much larger Markdown
@@ -1055,27 +1420,64 @@ var hljs = (function () {
         if (top.relevance > 0) {
           relevance += result.relevance;
         }
-        if (explicit) {
-          continuations[top.subLanguage] = result.top;
-        }
         emitter.addSublanguage(result.emitter, result.language);
       }
 
       function processBuffer() {
-        if (top.subLanguage != null)
+        if (top.subLanguage != null) {
           processSubLanguage();
-        else
+        } else {
           processKeywords();
+        }
         mode_buffer = '';
       }
 
+      /**
+       * @param {Mode} mode - new mode to start
+       */
       function startNewMode(mode) {
         if (mode.className) {
           emitter.openNode(mode.className);
         }
-        top = Object.create(mode, {parent: {value: top}});
+        top = Object.create(mode, { parent: { value: top } });
+        return top;
       }
 
+      /**
+       * @param {CompiledMode } mode - the mode to potentially end
+       * @param {RegExpMatchArray} match - the latest match
+       * @param {string} matchPlusRemainder - match plus remainder of content
+       * @returns {CompiledMode | void} - the next mode, or if void continue on in current mode
+       */
+      function endOfMode(mode, match, matchPlusRemainder) {
+        let matched = startsWith(mode.endRe, matchPlusRemainder);
+
+        if (matched) {
+          if (mode["on:end"]) {
+            const resp = new Response(mode);
+            mode["on:end"](match, resp);
+            if (resp.ignore) matched = false;
+          }
+
+          if (matched) {
+            while (mode.endsParent && mode.parent) {
+              mode = mode.parent;
+            }
+            return mode;
+          }
+        }
+        // even if on:end fires an `ignore` it's still possible
+        // that we might trigger the end node because of a parent mode
+        if (mode.endsWithParent) {
+          return endOfMode(mode.parent, match, matchPlusRemainder);
+        }
+      }
+
+      /**
+       * Handle matching but then ignoring a sequence of text
+       *
+       * @param {string} lexeme - string containing full match text
+       */
       function doIgnore(lexeme) {
         if (top.matcher.regexIndex === 0) {
           // no more regexs to potentially match here, so we move the cursor forward one
@@ -1090,18 +1492,27 @@ var hljs = (function () {
         }
       }
 
+      /**
+       * Handle the start of a new potential mode match
+       *
+       * @param {EnhancedMatch} match - the current match
+       * @returns {number} how far to advance the parse cursor
+       */
       function doBeginMatch(match) {
         var lexeme = match[0];
         var new_mode = match.rule;
 
-        if (new_mode.__onBegin) {
-          let res = new_mode.__onBegin(match) || {};
-          if (res.ignoreMatch)
-            return doIgnore(lexeme);
+        const resp = new Response(new_mode);
+        // first internal before callbacks, then the public ones
+        const beforeCallbacks = [new_mode.__beforeBegin, new_mode["on:begin"]];
+        for (const cb of beforeCallbacks) {
+          if (!cb) continue;
+          cb(match, resp);
+          if (resp.ignore) return doIgnore(lexeme);
         }
 
         if (new_mode && new_mode.endSameAsBegin) {
-          new_mode.endRe = escape( lexeme );
+          new_mode.endRe = escape(lexeme);
         }
 
         if (new_mode.skip) {
@@ -1116,14 +1527,24 @@ var hljs = (function () {
           }
         }
         startNewMode(new_mode);
+        // if (mode["after:begin"]) {
+        //   let resp = new Response(mode);
+        //   mode["after:begin"](match, resp);
+        // }
         return new_mode.returnBegin ? 0 : lexeme.length;
       }
 
+      /**
+       * Handle the potential end of mode
+       *
+       * @param {RegExpMatchArray} match - the current match
+       */
       function doEndMatch(match) {
         var lexeme = match[0];
         var matchPlusRemainder = codeToHighlight.substr(match.index);
-        var end_mode = endOfMode(top, matchPlusRemainder);
-        if (!end_mode) { return; }
+
+        var end_mode = endOfMode(top, match, matchPlusRemainder);
+        if (!end_mode) { return NO_MATCH; }
 
         var origin = top;
         if (origin.skip) {
@@ -1157,7 +1578,7 @@ var hljs = (function () {
 
       function processContinuations() {
         var list = [];
-        for(var current = top; current !== language; current = current.parent) {
+        for (var current = top; current !== language; current = current.parent) {
           if (current.className) {
             list.unshift(current.className);
           }
@@ -1165,50 +1586,57 @@ var hljs = (function () {
         list.forEach(item => emitter.openNode(item));
       }
 
+      /** @type {{type?: MatchType, index?: number, rule?: Mode}}} */
       var lastMatch = {};
-      function processLexeme(text_before_match, match) {
 
-        var err;
+      /**
+       *  Process an individual match
+       *
+       * @param {string} textBeforeMatch - text preceeding the match (since the last match)
+       * @param {EnhancedMatch} [match] - the match itself
+       */
+      function processLexeme(textBeforeMatch, match) {
         var lexeme = match && match[0];
 
         // add non-matched text to the current mode buffer
-        mode_buffer += text_before_match;
+        mode_buffer += textBeforeMatch;
 
         if (lexeme == null) {
           processBuffer();
           return 0;
         }
 
-
-
         // we've found a 0 width match and we're stuck, so we need to advance
         // this happens when we have badly behaved rules that have optional matchers to the degree that
         // sometimes they can end up matching nothing at all
         // Ref: https://github.com/highlightjs/highlight.js/issues/2140
-        if (lastMatch.type=="begin" && match.type=="end" && lastMatch.index == match.index && lexeme === "") 
{
+        if (lastMatch.type === "begin" && match.type === "end" && lastMatch.index === match.index && lexeme 
=== "") {
           // spit the "skipped" character that our regex choked on back into the output sequence
           mode_buffer += codeToHighlight.slice(match.index, match.index + 1);
           if (!SAFE_MODE) {
-            err = new Error('0 width match regex');
+            /** @type {AnnotatedError} */
+            const err = new Error('0 width match regex');
             err.languageName = languageName;
             err.badRule = lastMatch.rule;
-            throw(err);
+            throw err;
           }
           return 1;
         }
         lastMatch = match;
 
-        if (match.type==="begin") {
+        if (match.type === "begin") {
           return doBeginMatch(match);
-        } else if (match.type==="illegal" && !ignore_illegals) {
+        } else if (match.type === "illegal" && !ignoreIllegals) {
           // illegal match, we do not continue processing
-          err = new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.className || '<unnamed>') + 
'"');
+          /** @type {AnnotatedError} */
+          const err = new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.className || 
'<unnamed>') + '"');
           err.mode = top;
           throw err;
-        } else if (match.type==="end") {
+        } else if (match.type === "end") {
           var processed = doEndMatch(match);
-          if (processed != undefined)
+          if (processed !== NO_MATCH) {
             return processed;
+          }
         }
 
         // edge case for when illegal matches $ (end of line) which is technically
@@ -1250,16 +1678,16 @@ var hljs = (function () {
         throw new Error('Unknown language: "' + languageName + '"');
       }
 
-      compileLanguage(language);
-      var top = continuation || language;
+      var md = compileLanguage(language);
+      var result = '';
+      /** @type {CompiledMode} */
+      var top = continuation || md;
+      /** @type Record<string,Mode> */
       var continuations = {}; // keep continuations for sub-languages
-      var result;
       var emitter = new options.__emitter(options);
       processContinuations();
       var mode_buffer = '';
       var relevance = 0;
-      var match;
-      var processedCount;
       var index = 0;
       var iterations = 0;
       var continueScanAtSamePosition = false;
@@ -1270,19 +1698,19 @@ var hljs = (function () {
         for (;;) {
           iterations++;
           if (continueScanAtSamePosition) {
-            continueScanAtSamePosition = false;
             // only regexes not matched previously will now be
             // considered for a potential match
+            continueScanAtSamePosition = false;
           } else {
             top.matcher.lastIndex = index;
             top.matcher.considerAll();
           }
-          match = top.matcher.exec(codeToHighlight);
+          const match = top.matcher.exec(codeToHighlight);
           // console.log("match", match[0], match.rule && match.rule.begin)
-          if (!match)
-            break;
-          let beforeMatch = codeToHighlight.substring(index, match.index);
-          processedCount = processLexeme(beforeMatch, match);
+          if (!match) break;
+
+          const beforeMatch = codeToHighlight.substring(index, match.index);
+          const processedCount = processLexeme(beforeMatch, match);
           index = match.index + processedCount;
         }
         processLexeme(codeToHighlight.substr(index));
@@ -1304,16 +1732,17 @@ var hljs = (function () {
             illegal: true,
             illegalBy: {
               msg: err.message,
-              context: codeToHighlight.slice(index-100,index+100),
+              context: codeToHighlight.slice(index - 100, index + 100),
               mode: err.mode
             },
             sofar: result,
             relevance: 0,
             value: escape$1(codeToHighlight),
-            emitter: emitter,
+            emitter: emitter
           };
         } else if (SAFE_MODE) {
           return {
+            illegal: false,
             relevance: 0,
             value: escape$1(codeToHighlight),
             emitter: emitter,
@@ -1327,10 +1756,13 @@ var hljs = (function () {
       }
     }
 
-    // returns a valid highlight result, without actually
-    // doing any actual work, auto highlight starts with
-    // this and it's possible for small snippets that
-    // auto-detection may not find a better match
+    /**
+     * returns a valid highlight result, without actually doing any actual work,
+     * auto highlight starts with this and it's possible for small snippets that
+     * auto-detection may not find a better match
+     * @param {string} code
+     * @returns {HighlightResult}
+     */
     function justTextHighlightResult(code) {
       const result = {
         relevance: 0,
@@ -1343,7 +1775,7 @@ var hljs = (function () {
       return result;
     }
 
-    /*
+    /**
     Highlighting with language detection. Accepts a string with the code to
     highlight. Returns an object with the following properties:
 
@@ -1353,53 +1785,66 @@ var hljs = (function () {
     - second_best (object with the same structure for second-best heuristically
       detected language, may be absent)
 
+      @param {string} code
+      @param {Array<string>} [languageSubset]
+      @returns {AutoHighlightResult}
     */
     function highlightAuto(code, languageSubset) {
       languageSubset = languageSubset || options.languages || Object.keys(languages);
       var result = justTextHighlightResult(code);
-      var second_best = result;
+      var secondBest = result;
       languageSubset.filter(getLanguage).filter(autoDetection).forEach(function(name) {
         var current = _highlight(name, code, false);
         current.language = name;
-        if (current.relevance > second_best.relevance) {
-          second_best = current;
+        if (current.relevance > secondBest.relevance) {
+          secondBest = current;
         }
         if (current.relevance > result.relevance) {
-          second_best = result;
+          secondBest = result;
           result = current;
         }
       });
-      if (second_best.language) {
-        result.second_best = second_best;
+      if (secondBest.language) {
+        // second_best (with underscore) is the expected API
+        result.second_best = secondBest;
       }
       return result;
     }
 
-    /*
+    /**
     Post-processing of the highlighted markup:
 
     - replace TABs with something more useful
     - replace real line-breaks with '<br>' for non-pre containers
 
+      @param {string} html
+      @returns {string}
     */
-    function fixMarkup(value) {
+    function fixMarkup(html) {
       if (!(options.tabReplace || options.useBR)) {
-        return value;
+        return html;
       }
 
-      return value.replace(fixMarkupRe, function(match, p1) {
-          if (options.useBR && match === '\n') {
-            return '<br>';
-          } else if (options.tabReplace) {
-            return p1.replace(/\t/g, options.tabReplace);
-          }
-          return '';
+      return html.replace(fixMarkupRe, match => {
+        if (match === '\n') {
+          return options.useBR ? '<br>' : match;
+        } else if (options.tabReplace) {
+          return match.replace(/\t/g, options.tabReplace);
+        }
+        return match;
       });
     }
 
+    /**
+     * Builds new class name for block given the language name
+     *
+     * @param {string} prevClassName
+     * @param {string} [currentLang]
+     * @param {string} [resultLang]
+     */
     function buildClassName(prevClassName, currentLang, resultLang) {
-      var language = currentLang ? aliases[currentLang] : resultLang,
-          result   = [prevClassName.trim()];
+      var language = currentLang ? aliases[currentLang] : resultLang;
+      var result = [prevClassName.trim()];
 
       if (!prevClassName.match(/\bhljs\b/)) {
         result.push('hljs');
@@ -1412,86 +1857,99 @@ var hljs = (function () {
       return result.join(' ').trim();
     }
 
-    /*
-    Applies highlighting to a DOM node containing code. Accepts a DOM node and
-    two optional parameters for fixMarkup.
+    /**
+     * Applies highlighting to a DOM node containing code. Accepts a DOM node and
+     * two optional parameters for fixMarkup.
+     *
+     * @param {HighlightedHTMLElement} element - the HTML element to highlight
     */
-    function highlightBlock(block) {
-      var node, originalStream, result, resultNode, text;
-      var language = blockLanguage(block);
+    function highlightBlock(element) {
+      /** @type HTMLElement */
+      let node = null;
+      const language = blockLanguage(element);
 
-      if (shouldNotHighlight(language))
-          return;
+      if (shouldNotHighlight(language)) return;
 
       fire("before:highlightBlock",
-        { block: block, language: language});
+        { block: element, language: language });
 
       if (options.useBR) {
         node = document.createElement('div');
-        node.innerHTML = block.innerHTML.replace(/\n/g, '').replace(/<br[ \/]*>/g, '\n');
+        node.innerHTML = element.innerHTML.replace(/\n/g, '').replace(/<br[ /]*>/g, '\n');
       } else {
-        node = block;
+        node = element;
       }
-      text = node.textContent;
-      result = language ? highlight(language, text, true) : highlightAuto(text);
+      const text = node.textContent;
+      const result = language ? highlight(language, text, true) : highlightAuto(text);
 
-      originalStream = nodeStream$1(node);
+      const originalStream = nodeStream$1(node);
       if (originalStream.length) {
-        resultNode = document.createElement('div');
+        const resultNode = document.createElement('div');
         resultNode.innerHTML = result.value;
         result.value = mergeStreams$1(originalStream, nodeStream$1(resultNode), text);
       }
       result.value = fixMarkup(result.value);
 
-      fire("after:highlightBlock", { block: block, result: result});
+      fire("after:highlightBlock", { block: element, result: result });
 
-      block.innerHTML = result.value;
-      block.className = buildClassName(block.className, language, result.language);
-      block.result = {
+      element.innerHTML = result.value;
+      element.className = buildClassName(element.className, language, result.language);
+      element.result = {
         language: result.language,
-        re: result.relevance
+        // TODO: remove with version 11.0
+        re: result.relevance,
+        relavance: result.relevance
       };
       if (result.second_best) {
-        block.second_best = {
+        element.second_best = {
           language: result.second_best.language,
-          re: result.second_best.relevance
+          // TODO: remove with version 11.0
+          re: result.second_best.relevance,
+          relavance: result.second_best.relevance
         };
       }
     }
 
-    /*
-    Updates highlight.js global options with values passed in the form of an object.
-    */
-    function configure(user_options) {
-      options = inherit$1(options, user_options);
+    /**
+     * Updates highlight.js global options with the passed options
+     *
+     * @param {{}} userOptions
+     */
+    function configure(userOptions) {
+      options = inherit$1(options, userOptions);
     }
 
-    /*
-    Applies highlighting to all <pre><code>..</code></pre> blocks on a page.
-    */
-    function initHighlighting() {
-      if (initHighlighting.called)
-        return;
+    /**
+     * Highlights to all <pre><code> blocks on a page
+     *
+     * @type {Function & {called?: boolean}}
+     */
+    const initHighlighting = () => {
+      if (initHighlighting.called) return;
       initHighlighting.called = true;
 
       var blocks = document.querySelectorAll('pre code');
       ArrayProto.forEach.call(blocks, highlightBlock);
-    }
+    };
 
-    /*
-    Attaches highlighting to the page load event.
-    */
+    // Higlights all when DOMContentLoaded fires
     function initHighlightingOnLoad() {
+      // @ts-ignore
       window.addEventListener('DOMContentLoaded', initHighlighting, false);
     }
 
-    const PLAINTEXT_LANGUAGE = { disableAutodetect: true, name: 'Plain text' };
-
-    function registerLanguage(name, language) {
-      var lang;
-      try { lang = language(hljs); }
-      catch (error) {
-        console.error("Language definition for '{}' could not be registered.".replace("{}", name));
+    /**
+     * Register a language grammar module
+     *
+     * @param {string} languageName
+     * @param {LanguageFn} languageDefinition
+     */
+    function registerLanguage(languageName, languageDefinition) {
+      var lang = null;
+      try {
+        lang = languageDefinition(hljs);
+      } catch (error) {
+        console.error("Language definition for '{}' could not be registered.".replace("{}", languageName));
         // hard or soft error
         if (!SAFE_MODE) { throw error; } else { console.error(error); }
         // languages that have serious errors are replaced with essentially a
@@ -1501,51 +1959,84 @@ var hljs = (function () {
         lang = PLAINTEXT_LANGUAGE;
       }
       // give it a temporary name if it doesn't have one in the meta-data
-      if (!lang.name)
-        lang.name = name;
-      languages[name] = lang;
-      lang.rawDefinition = language.bind(null,hljs);
+      if (!lang.name) lang.name = languageName;
+      languages[languageName] = lang;
+      lang.rawDefinition = languageDefinition.bind(null, hljs);
 
       if (lang.aliases) {
-        lang.aliases.forEach(function(alias) {aliases[alias] = name;});
+        registerAliases(lang.aliases, { languageName });
       }
     }
 
+    /**
+     * @returns {string[]} List of language internal names
+     */
     function listLanguages() {
       return Object.keys(languages);
     }
 
-    /*
+    /**
       intended usage: When one language truly requires another
 
       Unlike `getLanguage`, this will throw when the requested language
       is not available.
+
+      @param {string} name - name of the language to fetch/require
+      @returns {Language | never}
     */
     function requireLanguage(name) {
       var lang = getLanguage(name);
       if (lang) { return lang; }
 
-      var err = new Error('The \'{}\' language is required, but not loaded.'.replace('{}',name));
+      var err = new Error('The \'{}\' language is required, but not loaded.'.replace('{}', name));
       throw err;
     }
 
+    /**
+     * @param {string} name - name of the language to retrieve
+     * @returns {Language | undefined}
+     */
     function getLanguage(name) {
       name = (name || '').toLowerCase();
       return languages[name] || languages[aliases[name]];
     }
 
+    /**
+     *
+     * @param {string|string[]} aliasList - single alias or list of aliases
+     * @param {{languageName: string}} opts
+     */
+    function registerAliases(aliasList, { languageName }) {
+      if (typeof aliasList === 'string') {
+        aliasList = [aliasList];
+      }
+      aliasList.forEach(alias => { aliases[alias] = languageName; });
+    }
+
+    /**
+     * Determines if a given language has auto-detection enabled
+     * @param {string} name - name of the language
+     */
     function autoDetection(name) {
       var lang = getLanguage(name);
       return lang && !lang.disableAutodetect;
     }
 
-    function addPlugin(plugin, options) {
+    /**
+     * @param {HLJSPlugin} plugin
+     */
+    function addPlugin(plugin) {
       plugins.push(plugin);
     }
 
+    /**
+     *
+     * @param {PluginEvent} event
+     * @param {any} args
+     */
     function fire(event, args) {
       var cb = event;
-      plugins.forEach(function (plugin) {
+      plugins.forEach(function(plugin) {
         if (plugin[cb]) {
           plugin[cb](args);
         }
@@ -1554,7 +2045,7 @@ var hljs = (function () {
 
     /* Interface definition */
 
-    Object.assign(hljs,{
+    Object.assign(hljs, {
       highlight,
       highlightAuto,
       fixMarkup,
@@ -1565,6 +2056,7 @@ var hljs = (function () {
       registerLanguage,
       listLanguages,
       getLanguage,
+      registerAliases,
       requireLanguage,
       autoDetection,
       inherit: inherit$1,
@@ -1576,8 +2068,11 @@ var hljs = (function () {
     hljs.versionString = version;
 
     for (const key in MODES) {
-      if (typeof MODES[key] === "object")
+      // @ts-ignore
+      if (typeof MODES[key] === "object") {
+        // @ts-ignore
         deepFreeze(MODES[key]);
+      }
     }
 
     // merge all the modes/regexs into our main object
@@ -1603,6 +2098,7 @@ hljs.registerLanguage('css', function () {
   Website: https://developer.mozilla.org/en-US/docs/Web/CSS
   */
 
+  /** @type LanguageFn */
   function css(hljs) {
     var FUNCTION_LIKE = {
       begin: /[\w-]+\(/, returnBegin: true,
@@ -1738,6 +2234,177 @@ hljs.registerLanguage('css', function () {
 hljs.registerLanguage('javascript', function () {
   'use strict';
 
+  const IDENT_RE = '[A-Za-z$_][0-9A-Za-z$_]*';
+  const KEYWORDS = [
+    "as", // for exports
+    "in",
+    "of",
+    "if",
+    "for",
+    "while",
+    "finally",
+    "var",
+    "new",
+    "function",
+    "do",
+    "return",
+    "void",
+    "else",
+    "break",
+    "catch",
+    "instanceof",
+    "with",
+    "throw",
+    "case",
+    "default",
+    "try",
+    "switch",
+    "continue",
+    "typeof",
+    "delete",
+    "let",
+    "yield",
+    "const",
+    "class",
+    // JS handles these with a special rule
+    // "get",
+    // "set",
+    "debugger",
+    "async",
+    "await",
+    "static",
+    "import",
+    "from",
+    "export",
+    "extends"
+  ];
+  const LITERALS = [
+    "true",
+    "false",
+    "null",
+    "undefined",
+    "NaN",
+    "Infinity"
+  ];
+
+  const TYPES = [
+    "Intl",
+    "DataView",
+    "Number",
+    "Math",
+    "Date",
+    "String",
+    "RegExp",
+    "Object",
+    "Function",
+    "Boolean",
+    "Error",
+    "Symbol",
+    "Set",
+    "Map",
+    "WeakSet",
+    "WeakMap",
+    "Proxy",
+    "Reflect",
+    "JSON",
+    "Promise",
+    "Float64Array",
+    "Int16Array",
+    "Int32Array",
+    "Int8Array",
+    "Uint16Array",
+    "Uint32Array",
+    "Float32Array",
+    "Array",
+    "Uint8Array",
+    "Uint8ClampedArray",
+    "ArrayBuffer"
+  ];
+
+  const ERROR_TYPES = [
+    "EvalError",
+    "InternalError",
+    "RangeError",
+    "ReferenceError",
+    "SyntaxError",
+    "TypeError",
+    "URIError"
+  ];
+
+  const BUILT_IN_GLOBALS = [
+    "setInterval",
+    "setTimeout",
+    "clearInterval",
+    "clearTimeout",
+
+    "require",
+    "exports",
+
+    "eval",
+    "isFinite",
+    "isNaN",
+    "parseFloat",
+    "parseInt",
+    "decodeURI",
+    "decodeURIComponent",
+    "encodeURI",
+    "encodeURIComponent",
+    "escape",
+    "unescape"
+  ];
+
+  const BUILT_IN_VARIABLES = [
+    "arguments",
+    "this",
+    "super",
+    "console",
+    "window",
+    "document",
+    "localStorage",
+    "module",
+    "global" // Node.js
+  ];
+
+  const BUILT_INS = [].concat(
+    BUILT_IN_GLOBALS,
+    BUILT_IN_VARIABLES,
+    TYPES,
+    ERROR_TYPES
+  );
+
+  /**
+   * @param {string} value
+   * @returns {RegExp}
+   * */
+
+  /**
+   * @param {RegExp | string } re
+   * @returns {string}
+   */
+  function source(re) {
+    if (!re) return null;
+    if (typeof re === "string") return re;
+
+    return re.source;
+  }
+
+  /**
+   * @param {RegExp | string } re
+   * @returns {string}
+   */
+  function lookahead(re) {
+    return concat('(?=', re, ')');
+  }
+
+  /**
+   * @param {...(RegExp | string) } args
+   * @returns {string}
+   */
+  function concat(...args) {
+    const joined = args.map((x) => source(x)).join("");
+    return joined;
+  }
+
   /*
   Language: JavaScript
   Description: JavaScript (JS) is a lightweight, interpreted, or just-in-time compiled programming language 
with first-class functions.
@@ -1746,6 +2413,7 @@ hljs.registerLanguage('javascript', function () {
   */
 
   function javascript(hljs) {
+    var IDENT_RE$1 = IDENT_RE;
     var FRAGMENT = {
       begin: '<>',
       end: '</>'
@@ -1754,26 +2422,11 @@ hljs.registerLanguage('javascript', function () {
       begin: /<[A-Za-z0-9\\._:-]+/,
       end: /\/[A-Za-z0-9\\._:-]+>|\/>/
     };
-    var IDENT_RE = '[A-Za-z$_][0-9A-Za-z$_]*';
-    var KEYWORDS = {
-      keyword:
-        'in of if for while finally var new function do return void else break catch ' +
-        'instanceof with throw case default try this switch continue typeof delete ' +
-        'let yield const export super debugger as async await static ' +
-        // ECMAScript 6 modules import
-        'import from as'
-      ,
-      literal:
-        'true false null undefined NaN Infinity',
-      built_in:
-        'eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent ' +
-        'encodeURI encodeURIComponent escape unescape Object Function Boolean Error ' +
-        'EvalError InternalError RangeError ReferenceError StopIteration SyntaxError ' +
-        'TypeError URIError Number Math Date String RegExp Array Float32Array ' +
-        'Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array ' +
-        'Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require ' +
-        'module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect ' +
-        'Promise'
+    var KEYWORDS$1 = {
+      $pattern: IDENT_RE,
+      keyword: KEYWORDS.join(" "),
+      literal: LITERALS.join(" "),
+      built_in: BUILT_INS.join(" ")
     };
     var NUMBER = {
       className: 'number',
@@ -1787,7 +2440,7 @@ hljs.registerLanguage('javascript', function () {
     var SUBST = {
       className: 'subst',
       begin: '\\$\\{', end: '\\}',
-      keywords: KEYWORDS,
+      keywords: KEYWORDS$1,
       contains: []  // defined later
     };
     var HTML_TEMPLATE = {
@@ -1830,6 +2483,10 @@ hljs.registerLanguage('javascript', function () {
       hljs.REGEXP_MODE
     ];
     var PARAMS_CONTAINS = SUBST.contains.concat([
+      // eat recursive parens in sub expressions
+      { begin: /\(/, end: /\)/,
+        contains: ["self"].concat(SUBST.contains, [hljs.C_BLOCK_COMMENT_MODE, hljs.C_LINE_COMMENT_MODE])
+      },
       hljs.C_BLOCK_COMMENT_MODE,
       hljs.C_LINE_COMMENT_MODE
     ]);
@@ -1844,17 +2501,17 @@ hljs.registerLanguage('javascript', function () {
     return {
       name: 'JavaScript',
       aliases: ['js', 'jsx', 'mjs', 'cjs'],
-      keywords: KEYWORDS,
+      keywords: KEYWORDS$1,
       contains: [
+        hljs.SHEBANG({
+          binary: "node",
+          relevance: 5
+        }),
         {
           className: 'meta',
           relevance: 10,
           begin: /^\s*['"]use (strict|asm)['"]/
         },
-        {
-          className: 'meta',
-          begin: /^#!/, end: /$/
-        },
         hljs.APOS_STRING_MODE,
         hljs.QUOTE_STRING_MODE,
         HTML_TEMPLATE,
@@ -1879,7 +2536,7 @@ hljs.registerLanguage('javascript', function () {
                   },
                   {
                     className: 'variable',
-                    begin: IDENT_RE + '(?=\\s*(-)|$)',
+                    begin: IDENT_RE$1 + '(?=\\s*(-)|$)',
                     endsParent: true,
                     relevance: 0
                   },
@@ -1897,13 +2554,29 @@ hljs.registerLanguage('javascript', function () {
         hljs.C_BLOCK_COMMENT_MODE,
         NUMBER,
         { // object attr container
-          begin: /[{,\n]\s*/, relevance: 0,
+          begin: concat(/[{,\n]\s*/,
+            // we need to look ahead to make sure that we actually have an
+            // attribute coming up so we don't steal a comma from a potential
+            // "value" container
+            //
+            // NOTE: this might not work how you think.  We don't actually always
+            // enter this mode and stay.  Instead it might merely match `,
+            // <comments up next>` and then immediately end after the , because it
+            // fails to find any actual attrs. But this still does the job because
+            // it prevents the value contain rule from grabbing this instead and
+            // prevening this rule from firing when we actually DO have keys.
+            lookahead(concat(
+              // we also need to allow for multiple possible comments inbetween
+              // the first key:value pairing
+              /(((\/\/.*)|(\/\*(.|\n)*\*\/))\s*)*/,
+              IDENT_RE$1 + '\\s*:'))),
+          relevance: 0,
           contains: [
             {
-              begin: IDENT_RE + '\\s*:', returnBegin: true,
+              className: 'attr',
+              begin: IDENT_RE$1 + lookahead('\\s*:'),
               relevance: 0,
-              contains: [{className: 'attr', begin: IDENT_RE, relevance: 0}]
-            }
+            },
           ]
         },
         { // "value" container
@@ -1915,22 +2588,32 @@ hljs.registerLanguage('javascript', function () {
             hljs.REGEXP_MODE,
             {
               className: 'function',
-              begin: '(\\(.*?\\)|' + IDENT_RE + ')\\s*=>', returnBegin: true,
+              // we have to count the parens to make sure we actually have the
+              // correct bounding ( ) before the =>.  There could be any number of
+              // sub-expressions inside also surrounded by parens.
+              begin: '(\\([^(]*' +
+                '(\\([^(]*' +
+                  '(\\([^(]*' +
+                  '\\))?' +
+                '\\))?' +
+              '\\)|' + hljs.UNDERSCORE_IDENT_RE + ')\\s*=>', returnBegin: true,
               end: '\\s*=>',
               contains: [
                 {
                   className: 'params',
                   variants: [
                     {
-                      begin: IDENT_RE
+                      begin: hljs.UNDERSCORE_IDENT_RE
                     },
                     {
+                      className: null,
                       begin: /\(\s*\)/,
+                      skip: true
                     },
                     {
                       begin: /\(/, end: /\)/,
                       excludeBegin: true, excludeEnd: true,
-                      keywords: KEYWORDS,
+                      keywords: KEYWORDS$1,
                       contains: PARAMS_CONTAINS
                     }
                   ]
@@ -1966,7 +2649,7 @@ hljs.registerLanguage('javascript', function () {
           className: 'function',
           beginKeywords: 'function', end: /\{/, excludeEnd: true,
           contains: [
-            hljs.inherit(hljs.TITLE_MODE, {begin: IDENT_RE}),
+            hljs.inherit(hljs.TITLE_MODE, {begin: IDENT_RE$1}),
             PARAMS
           ],
           illegal: /\[|%/
@@ -1989,11 +2672,11 @@ hljs.registerLanguage('javascript', function () {
           beginKeywords: 'constructor', end: /\{/, excludeEnd: true
         },
         {
-          begin:'(get|set)\\s*(?=' + IDENT_RE+ '\\()',
+          begin: '(get|set)\\s+(?=' + IDENT_RE$1 + '\\()',
           end: /{/,
           keywords: "get set",
           contains: [
-            hljs.inherit(hljs.TITLE_MODE, {begin: IDENT_RE}),
+            hljs.inherit(hljs.TITLE_MODE, {begin: IDENT_RE$1}),
             { begin: /\(\)/ }, // eat to avoid empty params
             PARAMS
           ]


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]