lowlight
Advanced tools
Comparing version 1.2.0 to 1.3.0
26
index.js
@@ -12,9 +12,7 @@ /** | ||
/* eslint-env commonjs */ | ||
/* Expose. */ | ||
var low = module.exports = require('./lib/core.js'); | ||
var low = require('./lib/core.js'); | ||
/* jscs:disable maximumLineLength */ | ||
low.registerLanguage('1c', require('highlight.js/lib/languages/1c')); | ||
low.registerLanguage('abnf', require('highlight.js/lib/languages/abnf')); | ||
low.registerLanguage('accesslog', require('highlight.js/lib/languages/accesslog')); | ||
@@ -34,2 +32,3 @@ low.registerLanguage('actionscript', require('highlight.js/lib/languages/actionscript')); | ||
low.registerLanguage('avrasm', require('highlight.js/lib/languages/avrasm')); | ||
low.registerLanguage('awk', require('highlight.js/lib/languages/awk')); | ||
low.registerLanguage('axapta', require('highlight.js/lib/languages/axapta')); | ||
@@ -47,2 +46,3 @@ low.registerLanguage('bash', require('highlight.js/lib/languages/bash')); | ||
low.registerLanguage('coffeescript', require('highlight.js/lib/languages/coffeescript')); | ||
low.registerLanguage('coq', require('highlight.js/lib/languages/coq')); | ||
low.registerLanguage('cos', require('highlight.js/lib/languages/cos')); | ||
@@ -63,4 +63,6 @@ low.registerLanguage('crmsh', require('highlight.js/lib/languages/crmsh')); | ||
low.registerLanguage('dos', require('highlight.js/lib/languages/dos')); | ||
low.registerLanguage('dsconfig', require('highlight.js/lib/languages/dsconfig')); | ||
low.registerLanguage('dts', require('highlight.js/lib/languages/dts')); | ||
low.registerLanguage('dust', require('highlight.js/lib/languages/dust')); | ||
low.registerLanguage('ebnf', require('highlight.js/lib/languages/ebnf')); | ||
low.registerLanguage('elixir', require('highlight.js/lib/languages/elixir')); | ||
@@ -72,2 +74,3 @@ low.registerLanguage('elm', require('highlight.js/lib/languages/elm')); | ||
low.registerLanguage('erlang', require('highlight.js/lib/languages/erlang')); | ||
low.registerLanguage('excel', require('highlight.js/lib/languages/excel')); | ||
low.registerLanguage('fix', require('highlight.js/lib/languages/fix')); | ||
@@ -101,2 +104,3 @@ low.registerLanguage('fortran', require('highlight.js/lib/languages/fortran')); | ||
low.registerLanguage('lasso', require('highlight.js/lib/languages/lasso')); | ||
low.registerLanguage('ldif', require('highlight.js/lib/languages/ldif')); | ||
low.registerLanguage('less', require('highlight.js/lib/languages/less')); | ||
@@ -106,2 +110,3 @@ low.registerLanguage('lisp', require('highlight.js/lib/languages/lisp')); | ||
low.registerLanguage('livescript', require('highlight.js/lib/languages/livescript')); | ||
low.registerLanguage('lsl', require('highlight.js/lib/languages/lsl')); | ||
low.registerLanguage('lua', require('highlight.js/lib/languages/lua')); | ||
@@ -131,2 +136,3 @@ low.registerLanguage('makefile', require('highlight.js/lib/languages/makefile')); | ||
low.registerLanguage('php', require('highlight.js/lib/languages/php')); | ||
low.registerLanguage('pony', require('highlight.js/lib/languages/pony')); | ||
low.registerLanguage('powershell', require('highlight.js/lib/languages/powershell')); | ||
@@ -161,4 +167,7 @@ low.registerLanguage('processing', require('highlight.js/lib/languages/processing')); | ||
low.registerLanguage('stylus', require('highlight.js/lib/languages/stylus')); | ||
low.registerLanguage('subunit', require('highlight.js/lib/languages/subunit')); | ||
low.registerLanguage('swift', require('highlight.js/lib/languages/swift')); | ||
low.registerLanguage('taggerscript', require('highlight.js/lib/languages/taggerscript')); | ||
low.registerLanguage('yaml', require('highlight.js/lib/languages/yaml')); | ||
low.registerLanguage('tap', require('highlight.js/lib/languages/tap')); | ||
low.registerLanguage('tcl', require('highlight.js/lib/languages/tcl')); | ||
@@ -180,9 +189,2 @@ low.registerLanguage('tex', require('highlight.js/lib/languages/tex')); | ||
low.registerLanguage('xquery', require('highlight.js/lib/languages/xquery')); | ||
low.registerLanguage('yaml', require('highlight.js/lib/languages/yaml')); | ||
low.registerLanguage('zephir', require('highlight.js/lib/languages/zephir')); | ||
/* | ||
* Expose. | ||
*/ | ||
module.exports = low; |
1246
lib/core.js
@@ -12,26 +12,28 @@ /** | ||
/* eslint-env commonjs */ | ||
/* Dependencies. */ | ||
var high = require('highlight.js/lib/highlight.js'); | ||
/* | ||
* Dependencies. | ||
*/ | ||
/* The lowlight interface, which has to be compatible | ||
* with highlight.js, as this object is passed to | ||
* highlight.js syntaxes. */ | ||
var high = require('highlight.js/lib/highlight.js'); | ||
/** High constructor. */ | ||
function High() {} | ||
/* | ||
* Methods. | ||
*/ | ||
High.prototype = high; | ||
var inherit = high.inherit; | ||
/* Expose. */ | ||
var low = new High(); // Ha! | ||
/* | ||
* Low - Populated later. | ||
*/ | ||
module.exports = low; | ||
var low; | ||
low.highlight = highlight; | ||
low.highlightAuto = autoHighlight; | ||
low.registerLanguage = registerLanguage; | ||
low.getLanguage = getLanguage; | ||
/* | ||
* Constants. | ||
*/ | ||
/* Methods. */ | ||
var inherit = high.inherit; | ||
/* Constants. */ | ||
var DEFAULT_PREFIX = 'hljs-'; | ||
@@ -41,13 +43,7 @@ var KEY_INSENSITIVE = 'case_insensitive'; | ||
/* | ||
* Constant characters. | ||
*/ | ||
/* Constant characters. */ | ||
var C_SPACE = ' '; | ||
var C_PIPE = '|'; | ||
/* | ||
* Constant types. | ||
*/ | ||
/* Constant types. */ | ||
var T_ELEMENT = 'element'; | ||
@@ -57,6 +53,3 @@ var T_TEXT = 'text'; | ||
/* | ||
* Maps of syntaxes. | ||
*/ | ||
/* Maps of syntaxes. */ | ||
var languageNames = []; | ||
@@ -66,229 +59,86 @@ var languages = {}; | ||
/** | ||
* Get a language by `name`. | ||
* | ||
* @private | ||
* @param {string} name - Name or alias of language. | ||
* @return {Object?} - Syntax. | ||
*/ | ||
function getLanguage(name) { | ||
name = name.toLowerCase(); | ||
/* Methods. */ | ||
var own = {}.hasOwnProperty; | ||
return languages[name] || languages[aliases[name]]; | ||
} | ||
/** | ||
* No-op exec. | ||
* Highlighting with language detection. Accepts a string | ||
* with the code to highlight. Returns an object with the | ||
* following properties: | ||
* | ||
* @private | ||
* @return {null} - Void result. | ||
*/ | ||
function execNoop() { | ||
return null; | ||
} | ||
/** | ||
* Check. | ||
* - language (detected language) | ||
* - relevance (int) | ||
* - value (an HTML string with highlighting markup) | ||
* - secondBest (object with the same structure for | ||
* second-best heuristically detected language, may | ||
* be absent). | ||
* | ||
* @private | ||
* @param {RegExp} expression - Expression. | ||
* @param {string} lexeme - Value. | ||
* @return {boolean} - whether `lexeme` matches `re`. | ||
* @param {string} value - Source to highlight. | ||
* @param {Object?} [options={}] - Configuration. | ||
* @param {string} [options.prefix='hljs-'] - Highlight | ||
* prefix. | ||
* @param {Array.<string>} [options.subset] - List of | ||
* allowed languages. | ||
* @return {Object} - Highlighted `value`. | ||
*/ | ||
function test(expression, lexeme) { | ||
var match = expression && expression.exec(lexeme); | ||
function autoHighlight(value, options) { | ||
var settings = options || {}; | ||
var prefix = settings.prefix || DEFAULT_PREFIX; | ||
var subset = settings.subset || languageNames; | ||
var length = subset.length; | ||
var index = -1; | ||
var result; | ||
var secondBest; | ||
var current; | ||
var name; | ||
return match && match.index == 0; | ||
} | ||
if (typeof value !== 'string') { | ||
throw new Error('Expected `string` for value, got `' + value + '`'); | ||
} | ||
/** | ||
* Normalize a syntax result. | ||
* | ||
* @param {Object} result - Syntax result. | ||
* @return {Object} - Normalized syntax result. | ||
*/ | ||
function normalize(result) { | ||
return { | ||
'relevance': result.relevance || 0, | ||
'language': result.language || null, | ||
'value': result.value || [] | ||
}; | ||
} | ||
secondBest = normalize({}); | ||
result = normalize({}); | ||
/** | ||
* Compile a language. | ||
* | ||
* @private | ||
* @param {Object} language - Language to compile. | ||
*/ | ||
function compileLanguage(language) { | ||
/** | ||
* Get the source of an expression or string. | ||
* | ||
* @private | ||
* @param {RegExp|string} re - Value. | ||
* @return {string} - Source. | ||
*/ | ||
function source(re) { | ||
return (re && re.source) || re; | ||
} | ||
while (++index < length) { | ||
name = subset[index]; | ||
/** | ||
* Get the source of an expression or string. | ||
* | ||
* @private | ||
* @param {RegExp|string} value - Expression. | ||
* @param {boolean?} [global] - Whether to execute | ||
* globally. | ||
* @return {RegExp} - Compiled expression. | ||
*/ | ||
function langRe(value, global) { | ||
return new RegExp( | ||
source(value), | ||
'm' + (language[KEY_INSENSITIVE] ? 'i' : '') + | ||
(global ? 'g' : '') | ||
); | ||
if (!getLanguage(name)) { | ||
continue; | ||
} | ||
/** | ||
* Compile a mode. | ||
* | ||
* @private | ||
* @param {Object} mode - Language mode to compile. | ||
* @param {Object} [parent] - Parent mode. | ||
*/ | ||
function compileMode(mode, parent) { | ||
var compiledKeywords = {}; | ||
var expandedContains = []; | ||
var terminators; | ||
current = normalize(coreHighlight(name, value, false, prefix)); | ||
/** | ||
* Flatten a class-name. | ||
* | ||
* @private | ||
* @param {string} className - Classname to flatten. | ||
* @param {string} value - Value. | ||
*/ | ||
function flatten(className, value) { | ||
var pairs; | ||
var pair; | ||
var index; | ||
var length; | ||
current.language = name; | ||
if (language[KEY_INSENSITIVE]) { | ||
value = value.toLowerCase(); | ||
} | ||
if (current.relevance > secondBest.relevance) { | ||
secondBest = current; | ||
} | ||
pairs = value.split(C_SPACE); | ||
length = pairs.length; | ||
index = -1; | ||
if (current.relevance > result.relevance) { | ||
secondBest = result; | ||
result = current; | ||
} | ||
} | ||
while (++index < length) { | ||
pair = pairs[index].split(C_PIPE); | ||
if (secondBest.language) { | ||
result.secondBest = secondBest; | ||
} | ||
compiledKeywords[pair[0]] = [ | ||
className, | ||
pair[1] ? Number(pair[1]) : 1 | ||
]; | ||
} | ||
} | ||
return result; | ||
} | ||
if (mode.compiled) { | ||
return; | ||
} | ||
/** | ||
* Highlighting `value` in the language `language`. | ||
* | ||
* @param {string} language - Language name. | ||
* @param {string} value - Source to highlight. | ||
* @param {Object?} [options={}] - Configuration. | ||
* @param {string} [options.prefix='hljs-'] - Highlight | ||
* prefix. | ||
* @return {Object} - Highlighted `value`. | ||
*/ | ||
function highlight(language, value, options) { | ||
var settings = options || {}; | ||
var prefix = settings.prefix || DEFAULT_PREFIX; | ||
mode.compiled = true; | ||
mode.keywords = mode.keywords || mode.beginKeywords; | ||
if (mode.keywords) { | ||
if (typeof mode.keywords == 'string') { | ||
flatten('keyword', mode.keywords); | ||
} else { | ||
Object.keys(mode.keywords).forEach(function (className) { | ||
flatten(className, mode.keywords[className]); | ||
}); | ||
} | ||
mode.keywords = compiledKeywords; | ||
} | ||
mode.lexemesRe = langRe(mode.lexemes || /\w+/, true); | ||
if (parent) { | ||
if (mode.beginKeywords) { | ||
mode.begin = '\\b(' + | ||
mode.beginKeywords.split(C_SPACE).join(C_PIPE) + | ||
')\\b'; | ||
} | ||
if (!mode.begin) { | ||
mode.begin = /\B|\b/; | ||
} | ||
mode.beginRe = langRe(mode.begin); | ||
if (!mode.end && !mode.endsWithParent) { | ||
mode.end = /\B|\b/; | ||
} | ||
if (mode.end) { | ||
mode.endRe = langRe(mode.end); | ||
} | ||
mode.terminatorEnd = source(mode.end) || EMPTY; | ||
if (mode.endsWithParent && parent.terminatorEnd) { | ||
mode.terminatorEnd += (mode.end ? C_PIPE : EMPTY) + | ||
parent.terminatorEnd; | ||
} | ||
} | ||
if (mode.illegal) { | ||
mode.illegalRe = langRe(mode.illegal); | ||
} | ||
if (mode.relevance === undefined) { | ||
mode.relevance = 1; | ||
} | ||
if (!mode.contains) { | ||
mode.contains = []; | ||
} | ||
mode.contains.forEach(function (c) { | ||
if (c.variants) { | ||
c.variants.forEach(function (v) { | ||
expandedContains.push(inherit(c, v)); | ||
}); | ||
} else { | ||
expandedContains.push(c == 'self' ? mode : c); | ||
} | ||
}); | ||
mode.contains = expandedContains; | ||
mode.contains.forEach(function (c) { | ||
compileMode(c, mode); | ||
}); | ||
if (mode.starts) { | ||
compileMode(mode.starts, parent); | ||
} | ||
terminators = | ||
mode.contains.map(function (c) { | ||
return c.beginKeywords ? | ||
'\\.?(' + c.begin + ')\\.?' : | ||
c.begin; | ||
}) | ||
.concat([mode.terminatorEnd, mode.illegal]) | ||
.map(source) | ||
.filter(Boolean); | ||
mode.terminators = terminators.length ? | ||
langRe(terminators.join(C_PIPE), true) : | ||
{'exec': execNoop}; | ||
} | ||
compileMode(language); | ||
return normalize(coreHighlight(language, value, true, prefix)); | ||
} | ||
@@ -303,12 +153,12 @@ | ||
function registerLanguage(name, syntax) { | ||
var lang = languages[name] = syntax(low); | ||
var values = lang.aliases; | ||
var length = values && values.length; | ||
var index = -1; | ||
var lang = languages[name] = syntax(low); | ||
var values = lang.aliases; | ||
var length = values && values.length; | ||
var index = -1; | ||
languageNames.push(name); | ||
languageNames.push(name); | ||
while (++index < length) { | ||
aliases[values[index]] = name; | ||
} | ||
while (++index < length) { | ||
aliases[values[index]] = name; | ||
} | ||
} | ||
@@ -321,3 +171,2 @@ | ||
* | ||
* @private | ||
* @param {string} name - Language name. | ||
@@ -327,4 +176,3 @@ * @param {string} value - Source to highlight. | ||
* illegals. | ||
* @param {string?} [prefix] - Whether to continue | ||
* processing with `continuation`. | ||
* @param {string?} [prefix] - Highlight prefix. | ||
* @param {boolean} [continuation] - Whether to continue | ||
@@ -335,597 +183,541 @@ * processing with `continuation`. | ||
function coreHighlight(name, value, ignore, prefix, continuation) { | ||
var continuations = {}; | ||
var stack = []; | ||
var modeBuffer = EMPTY; | ||
var relevance = 0; | ||
var language; | ||
var top; | ||
var current; | ||
var currentChildren; | ||
var offset; | ||
var count; | ||
var match; | ||
var children; | ||
var continuations = {}; | ||
var stack = []; | ||
var modeBuffer = EMPTY; | ||
var relevance = 0; | ||
var language; | ||
var top; | ||
var current; | ||
var currentChildren; | ||
var offset; | ||
var count; | ||
var match; | ||
var children; | ||
if (typeof name !== 'string') { | ||
throw new Error('Expected `string` for name, got `' + name + '`'); | ||
} | ||
if (typeof name !== 'string') { | ||
throw new Error('Expected `string` for name, got `' + name + '`'); | ||
} | ||
if (typeof value !== 'string') { | ||
throw new Error('Expected `string` for value, got `' + value + '`'); | ||
} | ||
if (typeof value !== 'string') { | ||
throw new Error('Expected `string` for value, got `' + value + '`'); | ||
} | ||
language = getLanguage(name); | ||
current = top = continuation || language; | ||
currentChildren = children = []; | ||
language = getLanguage(name); | ||
current = top = continuation || language; | ||
currentChildren = children = []; | ||
if (!language) { | ||
throw new Error('Expected `' + name + '` to be registered'); | ||
} | ||
if (!language) { | ||
throw new Error('Unknown language: `' + name + '` is not registered'); | ||
} | ||
compileLanguage(language); | ||
compileLanguage(language); | ||
/** | ||
* Exit the current context. | ||
* | ||
* @private | ||
*/ | ||
function pop() { | ||
/* istanbul ignore next - removed in hljs 9.3 */ | ||
currentChildren = stack.pop() || children; | ||
} | ||
try { | ||
offset = top.terminators.lastIndex = 0; | ||
match = top.terminators.exec(value); | ||
/** | ||
* Check a sub-mode. | ||
* | ||
* @private | ||
* @param {string} lexeme - Value. | ||
* @param {Object} mode - Sub-mode. | ||
* @return {boolean} - Whether the lexeme matches. | ||
*/ | ||
function subMode(lexeme, mode) { | ||
var values = mode.contains; | ||
var length = values.length; | ||
var index = -1; | ||
while (match) { | ||
count = processLexeme( | ||
value.substr(offset, match.index - offset), | ||
match[0] | ||
); | ||
while (++index < length) { | ||
if (test(values[index].beginRe, lexeme)) { | ||
return values[index]; | ||
} | ||
} | ||
offset = top.terminators.lastIndex = match.index + count; | ||
match = top.terminators.exec(value); | ||
} | ||
/** | ||
* Check if `lexeme` ends `mode`. | ||
* | ||
* @private | ||
* @param {Object} mode - Sub-mode. | ||
* @param {string} lexeme - Value. | ||
* @return {boolean} - Whether `lexeme` ends `mode`. | ||
*/ | ||
function endOfMode(mode, lexeme) { | ||
if (test(mode.endRe, lexeme)) { | ||
while (mode.endsParent && mode.parent) { | ||
mode = mode.parent; | ||
} | ||
return mode; | ||
} | ||
if (mode.endsWithParent) { | ||
return endOfMode(mode.parent, lexeme); | ||
} | ||
processLexeme(value.substr(offset)); | ||
current = top; | ||
while (current.parent) { | ||
if (current.className) { | ||
pop(); | ||
} | ||
current = current.parent; | ||
} | ||
/** | ||
* Check if `lexeme` is illegal according to `mode`. | ||
* | ||
* @private | ||
* @param {string} lexeme - Value. | ||
* @param {Object} mode - Sub-mode. | ||
* @return {boolean} - Whether `lexeme` is illegal | ||
* according to `mode`. | ||
*/ | ||
function isIllegal(lexeme, mode) { | ||
return !ignore && test(mode.illegalRe, lexeme); | ||
return { | ||
relevance: relevance, | ||
value: currentChildren, | ||
language: name, | ||
top: top | ||
}; | ||
} catch (err) { | ||
/* istanbul ignore if - Catch-all */ | ||
if (err.message.indexOf('Illegal') === -1) { | ||
throw err; | ||
} | ||
/** | ||
* Check if the first word in `keywords` is a keyword. | ||
* | ||
* @private | ||
* @param {Object} mode - Sub-mode. | ||
* @param {Array.<string>} keywords - Words. | ||
* @return {boolean} - Whether the first word in | ||
* `keywords` is a keyword. | ||
*/ | ||
function keywordMatch(mode, keywords) { | ||
var keyword = keywords[0]; | ||
return {relevance: 0, value: addText(value, [])}; | ||
} | ||
if (language[KEY_INSENSITIVE]) { | ||
keyword = keyword.toLowerCase(); | ||
} | ||
/* Process a lexeme. Returns next position. */ | ||
function processLexeme(buffer, lexeme) { | ||
var newMode; | ||
var endMode; | ||
var origin; | ||
return mode.keywords.hasOwnProperty(keyword) && | ||
mode.keywords[keyword]; | ||
} | ||
modeBuffer += buffer; | ||
/** | ||
* Build a span. | ||
* | ||
* @private | ||
* @param {string} name - Class-name. | ||
* @param {string} contents - Value inside the span. | ||
* @param {boolean?} [noPrefix] - Don’t prefix class. | ||
* @return {HASTNode} - HAST Element node. | ||
*/ | ||
function build(name, contents, noPrefix) { | ||
return { | ||
'type': T_ELEMENT, | ||
'tagName': T_SPAN, | ||
'properties': { | ||
'className': [(noPrefix ? EMPTY : prefix) + name] | ||
}, | ||
'children': contents | ||
}; | ||
} | ||
if (lexeme === undefined) { | ||
addSiblings(processBuffer(), currentChildren); | ||
/** | ||
* Build a text. | ||
* | ||
* @private | ||
* @param {string} value - Content. | ||
* @return {HASTNode} - HAST Text node. | ||
*/ | ||
function buildText(value) { | ||
return { | ||
'type': T_TEXT, | ||
'value': value | ||
}; | ||
return 0; | ||
} | ||
/** | ||
* Add a text. | ||
* | ||
* @private | ||
* @param {string} value - Content. | ||
* @param {Array.<Node>} [nodes] - Nodes to add to, | ||
* defaults to `currentChildren`. | ||
*/ | ||
function addText(value, nodes) { | ||
var tail; | ||
newMode = subMode(lexeme, top); | ||
if (value) { | ||
tail = nodes[nodes.length - 1]; | ||
if (newMode) { | ||
addSiblings(processBuffer(), currentChildren); | ||
if (tail && tail.type === T_TEXT) { | ||
tail.value += value; | ||
} else { | ||
nodes.push(buildText(value)); | ||
} | ||
} | ||
startNewMode(newMode, lexeme); | ||
return nodes; | ||
return newMode.returnBegin ? 0 : lexeme.length; | ||
} | ||
/** | ||
* Add a text. | ||
* | ||
* @private | ||
* @param {Array.<Node>} siblings - Nodes to add. | ||
* @param {Array.<Node>} [nodes] - Nodes to add to. | ||
*/ | ||
function addSiblings(siblings, nodes) { | ||
var length = siblings.length; | ||
var index = -1; | ||
var sibling; | ||
endMode = endOfMode(top, lexeme); | ||
while (++index < length) { | ||
sibling = siblings[index]; | ||
if (endMode) { | ||
origin = top; | ||
if (sibling.type === T_TEXT) { | ||
addText(sibling.value, nodes); | ||
} else { | ||
nodes.push(sibling); | ||
} | ||
} | ||
} | ||
if (!(origin.returnEnd || origin.excludeEnd)) { | ||
modeBuffer += lexeme; | ||
} | ||
/** | ||
* Process keywords. | ||
* | ||
* @private | ||
* @return {Array.<Node>} - Nodes. | ||
*/ | ||
function processKeywords() { | ||
var nodes = []; | ||
var lastIndex; | ||
var keyword; | ||
var node; | ||
var submatch; | ||
addSiblings(processBuffer(), currentChildren); | ||
if (!top.keywords) { | ||
return addText(modeBuffer, nodes); | ||
/* Close open modes. */ | ||
do { | ||
if (top.className) { | ||
pop(); | ||
} | ||
lastIndex = 0; | ||
relevance += top.relevance; | ||
top = top.parent; | ||
} while (top !== endMode.parent); | ||
top.lexemesRe.lastIndex = 0; | ||
if (origin.excludeEnd) { | ||
addText(lexeme, currentChildren); | ||
} | ||
keyword = top.lexemesRe.exec(modeBuffer); | ||
modeBuffer = EMPTY; | ||
while (keyword) { | ||
addText(modeBuffer.substr( | ||
lastIndex, keyword.index - lastIndex | ||
), nodes); | ||
if (endMode.starts) { | ||
startNewMode(endMode.starts, EMPTY); | ||
} | ||
submatch = keywordMatch(top, keyword); | ||
return origin.returnEnd ? 0 : lexeme.length; | ||
} | ||
if (submatch) { | ||
relevance += submatch[1]; | ||
if (isIllegal(lexeme, top)) { | ||
throw new Error( | ||
'Illegal lexeme "' + lexeme + '" for mode "' + | ||
(top.className || '<unnamed>') + '"' | ||
); | ||
} | ||
node = build(submatch[0], []); | ||
/* Parser should not reach this point as all | ||
* types of lexemes should be caught earlier, | ||
* but if it does due to some bug make sure it | ||
* advances at least one character forward to | ||
* prevent infinite looping. */ | ||
modeBuffer += lexeme; | ||
nodes.push(node); | ||
return lexeme.length || /* istanbul ignore next */ 1; | ||
} | ||
addText(keyword[0], node.children); | ||
} else { | ||
addText(keyword[0], nodes); | ||
} | ||
/* Start a new mode with a `lexeme` to process. */ | ||
function startNewMode(mode, lexeme) { | ||
var node; | ||
lastIndex = top.lexemesRe.lastIndex; | ||
keyword = top.lexemesRe.exec(modeBuffer); | ||
} | ||
if (mode.className) { | ||
node = build(mode.className, []); | ||
} | ||
addText(modeBuffer.substr(lastIndex), nodes); | ||
if (mode.returnBegin) { | ||
modeBuffer = EMPTY; | ||
} else if (mode.excludeBegin) { | ||
addText(lexeme, currentChildren); | ||
return nodes; | ||
modeBuffer = EMPTY; | ||
} else { | ||
modeBuffer = lexeme; | ||
} | ||
/** | ||
* Process a sublanguage. | ||
* | ||
* @private | ||
* @return {Array.<Node>} - Nodes. | ||
*/ | ||
function processSubLanguage() { | ||
var explicit = typeof top.subLanguage == 'string'; | ||
var subvalue; | ||
/* Enter a new mode. */ | ||
if (node) { | ||
currentChildren.push(node); | ||
stack.push(currentChildren); | ||
currentChildren = node.children; | ||
} | ||
/* istanbul ignore if - support non-loaded sublanguages */ | ||
if (explicit && !languages[top.subLanguage]) { | ||
return addText(modeBuffer, []); | ||
} | ||
top = Object.create(mode, {parent: {value: top}}); | ||
} | ||
if (explicit) { | ||
subvalue = coreHighlight( | ||
top.subLanguage, | ||
modeBuffer, | ||
true, | ||
prefix, | ||
continuations[top.subLanguage] | ||
); | ||
} else { | ||
subvalue = autoHighlight(modeBuffer, { | ||
'subset': top.subLanguage.length ? | ||
top.subLanguage : undefined, | ||
'prefix': prefix | ||
}); | ||
} | ||
/* Process the buffer. */ | ||
function processBuffer() { | ||
var result = top.subLanguage === undefined ? processKeywords() : processSubLanguage(); | ||
modeBuffer = EMPTY; | ||
return result; | ||
} | ||
/* | ||
* Counting embedded language score towards the | ||
* host language may be disabled with zeroing the | ||
* containing mode relevance. Usecase in point is | ||
* Markdown that allows XML everywhere and makes | ||
* every XML snippet to have a much larger Markdown | ||
* score. | ||
*/ | ||
/* Process a sublanguage (returns a list of nodes). */ | ||
function processSubLanguage() { | ||
var explicit = typeof top.subLanguage === 'string'; | ||
var subvalue; | ||
if (top.relevance > 0) { | ||
relevance += subvalue.relevance; | ||
} | ||
/* istanbul ignore if - support non-loaded sublanguages */ | ||
if (explicit && !languages[top.subLanguage]) { | ||
return addText(modeBuffer, []); | ||
} | ||
if (explicit) { | ||
continuations[top.subLanguage] = subvalue.top; | ||
} | ||
if (explicit) { | ||
subvalue = coreHighlight( | ||
top.subLanguage, | ||
modeBuffer, | ||
true, | ||
prefix, | ||
continuations[top.subLanguage] | ||
); | ||
} else { | ||
subvalue = autoHighlight(modeBuffer, { | ||
subset: top.subLanguage.length ? top.subLanguage : undefined, | ||
prefix: prefix | ||
}); | ||
} | ||
return [build(subvalue.language, subvalue.value, true)]; | ||
/* Counting embedded language score towards the | ||
* host language may be disabled with zeroing the | ||
* containing mode relevance. Usecase in point is | ||
* Markdown that allows XML everywhere and makes | ||
* every XML snippet to have a much larger Markdown | ||
* score. */ | ||
if (top.relevance > 0) { | ||
relevance += subvalue.relevance; | ||
} | ||
/** | ||
* Process the buffer. | ||
* | ||
* @private | ||
* @return {string} - The processed buffer. | ||
*/ | ||
function processBuffer() { | ||
var result = top.subLanguage !== undefined ? | ||
processSubLanguage() : | ||
processKeywords(); | ||
if (explicit) { | ||
continuations[top.subLanguage] = subvalue.top; | ||
} | ||
modeBuffer = EMPTY; | ||
return [build(subvalue.language, subvalue.value, true)]; | ||
} | ||
return result; | ||
/* Process keywords. Returns nodes. */ | ||
function processKeywords() { | ||
var nodes = []; | ||
var lastIndex; | ||
var keyword; | ||
var node; | ||
var submatch; | ||
if (!top.keywords) { | ||
return addText(modeBuffer, nodes); | ||
} | ||
/** | ||
* Start a new mode. | ||
* | ||
* @private | ||
* @param {Object} mode - Mode to use. | ||
* @param {string} lexeme - Lexeme to process.. | ||
*/ | ||
function startNewMode(mode, lexeme) { | ||
var node; | ||
lastIndex = 0; | ||
if (mode.className) { | ||
node = build(mode.className, []); | ||
} | ||
top.lexemesRe.lastIndex = 0; | ||
if (mode.returnBegin) { | ||
modeBuffer = EMPTY; | ||
} else if (mode.excludeBegin) { | ||
addText(lexeme, currentChildren); | ||
keyword = top.lexemesRe.exec(modeBuffer); | ||
modeBuffer = EMPTY; | ||
} else { | ||
modeBuffer = lexeme; | ||
} | ||
while (keyword) { | ||
addText(modeBuffer.substr(lastIndex, keyword.index - lastIndex), nodes); | ||
/* | ||
* Enter a new mode. | ||
*/ | ||
submatch = keywordMatch(top, keyword); | ||
if (node) { | ||
currentChildren.push(node); | ||
stack.push(currentChildren); | ||
currentChildren = node.children; | ||
} | ||
if (submatch) { | ||
relevance += submatch[1]; | ||
top = Object.create(mode, { | ||
'parent': { | ||
'value': top | ||
} | ||
}); | ||
node = build(submatch[0], []); | ||
nodes.push(node); | ||
addText(keyword[0], node.children); | ||
} else { | ||
addText(keyword[0], nodes); | ||
} | ||
lastIndex = top.lexemesRe.lastIndex; | ||
keyword = top.lexemesRe.exec(modeBuffer); | ||
} | ||
/** | ||
* Process a lexeme. | ||
* | ||
* @private | ||
* @param {string} buffer - Current buffer. | ||
* @param {string} lexeme - Current lexeme. | ||
* @return {number} - Next position. | ||
*/ | ||
function processLexeme(buffer, lexeme) { | ||
var newMode; | ||
var endMode; | ||
var origin; | ||
addText(modeBuffer.substr(lastIndex), nodes); | ||
modeBuffer += buffer; | ||
return nodes; | ||
} | ||
if (lexeme === undefined) { | ||
addSiblings(processBuffer(), currentChildren); | ||
/* Add siblings. */ | ||
function addSiblings(siblings, nodes) { | ||
var length = siblings.length; | ||
var index = -1; | ||
var sibling; | ||
return 0; | ||
} | ||
while (++index < length) { | ||
sibling = siblings[index]; | ||
newMode = subMode(lexeme, top); | ||
if (sibling.type === T_TEXT) { | ||
addText(sibling.value, nodes); | ||
} else { | ||
nodes.push(sibling); | ||
} | ||
} | ||
} | ||
if (newMode) { | ||
addSiblings(processBuffer(), currentChildren); | ||
/* Add a text. */ | ||
function addText(value, nodes) { | ||
var tail; | ||
startNewMode(newMode, lexeme); | ||
if (value) { | ||
tail = nodes[nodes.length - 1]; | ||
return newMode.returnBegin ? 0 : lexeme.length; | ||
} | ||
if (tail && tail.type === T_TEXT) { | ||
tail.value += value; | ||
} else { | ||
nodes.push(buildText(value)); | ||
} | ||
} | ||
endMode = endOfMode(top, lexeme); | ||
return nodes; | ||
} | ||
if (endMode) { | ||
origin = top; | ||
/* Build a text. */ | ||
function buildText(value) { | ||
return {type: T_TEXT, value: value}; | ||
} | ||
if (!(origin.returnEnd || origin.excludeEnd)) { | ||
modeBuffer += lexeme; | ||
} | ||
/* Build a span. */ | ||
function build(name, contents, noPrefix) { | ||
return { | ||
type: T_ELEMENT, | ||
tagName: T_SPAN, | ||
properties: { | ||
className: [(noPrefix ? EMPTY : prefix) + name] | ||
}, | ||
children: contents | ||
}; | ||
} | ||
addSiblings(processBuffer(), currentChildren); | ||
/* Check if the first word in `keywords` is a keyword. */ | ||
function keywordMatch(mode, keywords) { | ||
var keyword = keywords[0]; | ||
/* | ||
* Close open modes. | ||
*/ | ||
if (language[KEY_INSENSITIVE]) { | ||
keyword = keyword.toLowerCase(); | ||
} | ||
do { | ||
if (top.className) { | ||
pop(); | ||
} | ||
return own.call(mode.keywords, keyword) && mode.keywords[keyword]; | ||
} | ||
relevance += top.relevance; | ||
top = top.parent; | ||
} while (top !== endMode.parent); | ||
/* Check if `lexeme` is illegal according to `mode`. */ | ||
function isIllegal(lexeme, mode) { | ||
return !ignore && test(mode.illegalRe, lexeme); | ||
} | ||
if (origin.excludeEnd) { | ||
addText(lexeme, currentChildren); | ||
} | ||
/* Check if `lexeme` ends `mode`. */ | ||
function endOfMode(mode, lexeme) { | ||
if (test(mode.endRe, lexeme)) { | ||
while (mode.endsParent && mode.parent) { | ||
mode = mode.parent; | ||
} | ||
return mode; | ||
} | ||
modeBuffer = EMPTY; | ||
if (mode.endsWithParent) { | ||
return endOfMode(mode.parent, lexeme); | ||
} | ||
} | ||
if (endMode.starts) { | ||
startNewMode(endMode.starts, EMPTY); | ||
} | ||
/* Check a sub-mode. */ | ||
function subMode(lexeme, mode) { | ||
var values = mode.contains; | ||
var length = values.length; | ||
var index = -1; | ||
return origin.returnEnd ? 0 : lexeme.length; | ||
} | ||
while (++index < length) { | ||
if (test(values[index].beginRe, lexeme)) { | ||
return values[index]; | ||
} | ||
} | ||
} | ||
if (isIllegal(lexeme, top)) { | ||
throw new Error( | ||
'Illegal lexeme "' + lexeme + '" for mode "' + | ||
(top.className || '<unnamed>') + '"' | ||
); | ||
} | ||
/* Exit the current context. */ | ||
function pop() { | ||
/* istanbul ignore next - removed in hljs 9.3 */ | ||
currentChildren = stack.pop() || children; | ||
} | ||
} | ||
/* | ||
* Parser should not reach this point as all | ||
* types of lexemes should be caught earlier, | ||
* but if it does due to some bug make sure it | ||
* advances at least one character forward to | ||
* prevent infinite looping. | ||
*/ | ||
/* Compile a language. */ | ||
function compileLanguage(language) { | ||
compileMode(language); | ||
return; | ||
modeBuffer += lexeme; | ||
/* Compile a language mode, optionally with a parent. */ | ||
function compileMode(mode, parent) { | ||
var compiledKeywords = {}; | ||
var expandedContains = []; | ||
var terminators; | ||
return lexeme.length || /* istanbul ignore next */ 1; | ||
if (mode.compiled) { | ||
return; | ||
} | ||
try { | ||
offset = top.terminators.lastIndex = 0; | ||
match = top.terminators.exec(value); | ||
mode.compiled = true; | ||
while (match) { | ||
count = processLexeme( | ||
value.substr(offset, match.index - offset), | ||
match[0] | ||
); | ||
mode.keywords = mode.keywords || mode.beginKeywords; | ||
offset = top.terminators.lastIndex = match.index + count; | ||
match = top.terminators.exec(value); | ||
} | ||
if (mode.keywords) { | ||
if (typeof mode.keywords === 'string') { | ||
flatten('keyword', mode.keywords); | ||
} else { | ||
Object.keys(mode.keywords).forEach(function (className) { | ||
flatten(className, mode.keywords[className]); | ||
}); | ||
} | ||
processLexeme(value.substr(offset)); | ||
current = top; | ||
mode.keywords = compiledKeywords; | ||
} | ||
while (current.parent) { | ||
if (current.className) { | ||
pop(); | ||
} | ||
mode.lexemesRe = langRe(mode.lexemes || /\w+/, true); | ||
current = current.parent; | ||
} | ||
if (parent) { | ||
if (mode.beginKeywords) { | ||
mode.begin = '\\b(' + mode.beginKeywords.split(C_SPACE).join(C_PIPE) + ')\\b'; | ||
} | ||
return { | ||
'relevance': relevance, | ||
'value': currentChildren, | ||
'language': name, | ||
'top': top | ||
}; | ||
} catch (e) { | ||
/* istanbul ignore else - Catch-all */ | ||
if (e.message.indexOf('Illegal') !== -1) { | ||
return { | ||
'relevance': 0, | ||
'value': addText(value, []) | ||
}; | ||
} else { | ||
throw e; | ||
} | ||
} | ||
} | ||
if (!mode.begin) { | ||
mode.begin = /\B|\b/; | ||
} | ||
/** | ||
* Highlighting with language detection. Accepts a string | ||
* with the code to highlight. Returns an object with the | ||
* following properties: | ||
* | ||
* - language (detected language) | ||
* - relevance (int) | ||
* - value (an HTML string with highlighting markup) | ||
* - secondBest (object with the same structure for | ||
* second-best heuristically detected language, may | ||
* be absent). | ||
* | ||
* @param {string} value - Source to highlight. | ||
* @param {Object?} [options={}] - Configuration. | ||
* @param {string} [options.prefix='hljs-'] - Highlight | ||
* prefix. | ||
* @param {Array.<string>} [options.subset] - List of | ||
* allowed languages. | ||
* @return {Object} - Highlighted `value`. | ||
*/ | ||
function autoHighlight(value, options) { | ||
var settings = options || {}; | ||
var prefix = settings.prefix || DEFAULT_PREFIX; | ||
var subset = settings.subset || languageNames; | ||
var result; | ||
var secondBest; | ||
var index; | ||
var length; | ||
var current; | ||
var name; | ||
mode.beginRe = langRe(mode.begin); | ||
length = subset.length; | ||
index = -1; | ||
if (!mode.end && !mode.endsWithParent) { | ||
mode.end = /\B|\b/; | ||
} | ||
if (typeof value !== 'string') { | ||
throw new Error('Expected `string` for value, got `' + value + '`'); | ||
if (mode.end) { | ||
mode.endRe = langRe(mode.end); | ||
} | ||
mode.terminatorEnd = source(mode.end) || EMPTY; | ||
if (mode.endsWithParent && parent.terminatorEnd) { | ||
mode.terminatorEnd += (mode.end ? C_PIPE : EMPTY) + parent.terminatorEnd; | ||
} | ||
} | ||
secondBest = normalize({}); | ||
result = normalize({}); | ||
if (mode.illegal) { | ||
mode.illegalRe = langRe(mode.illegal); | ||
} | ||
while (++index < length) { | ||
name = subset[index]; | ||
if (mode.relevance === undefined) { | ||
mode.relevance = 1; | ||
} | ||
if (!getLanguage(name)) { | ||
continue; | ||
} | ||
if (!mode.contains) { | ||
mode.contains = []; | ||
} | ||
current = normalize(coreHighlight(name, value, false, prefix)); | ||
mode.contains.forEach(function (c) { | ||
if (c.variants) { | ||
c.variants.forEach(function (v) { | ||
expandedContains.push(inherit(c, v)); | ||
}); | ||
} else { | ||
expandedContains.push(c === 'self' ? mode : c); | ||
} | ||
}); | ||
current.language = name; | ||
mode.contains = expandedContains; | ||
if (current.relevance > secondBest.relevance) { | ||
secondBest = current; | ||
} | ||
mode.contains.forEach(function (c) { | ||
compileMode(c, mode); | ||
}); | ||
if (current.relevance > result.relevance) { | ||
secondBest = result; | ||
result = current; | ||
} | ||
if (mode.starts) { | ||
compileMode(mode.starts, parent); | ||
} | ||
if (secondBest.language) { | ||
result.secondBest = secondBest; | ||
} | ||
terminators = | ||
mode.contains.map(function (c) { | ||
return c.beginKeywords ? '\\.?(' + c.begin + ')\\.?' : c.begin; | ||
}) | ||
.concat([mode.terminatorEnd, mode.illegal]) | ||
.map(source) | ||
.filter(Boolean); | ||
return result; | ||
} | ||
mode.terminators = terminators.length ? | ||
langRe(terminators.join(C_PIPE), true) : | ||
{exec: execNoop}; | ||
/** | ||
* Highlighting `value` in the language `language`. | ||
* | ||
* @private | ||
* @param {string} language - Language name. | ||
* @param {string} value - Source to highlight. | ||
* @param {Object?} [options={}] - Configuration. | ||
* @param {string} [options.prefix='hljs-'] - Highlight | ||
* prefix. | ||
* @return {Object} - Highlighted `value`. | ||
*/ | ||
function highlight(language, value, options) { | ||
var settings = options || {}; | ||
var prefix = settings.prefix || DEFAULT_PREFIX; | ||
return; | ||
return normalize(coreHighlight(language, value, true, prefix)); | ||
} | ||
/* Flatten a classname. */ | ||
function flatten(className, value) { | ||
var pairs; | ||
var pair; | ||
var index; | ||
var length; | ||
/* | ||
* The lowlight interface, which has to be compatible | ||
* with highlight.js, as this object is passed to | ||
* highlight.js syntaxes. | ||
*/ | ||
if (language[KEY_INSENSITIVE]) { | ||
value = value.toLowerCase(); | ||
} | ||
/** High constructor. */ | ||
function High() {} | ||
pairs = value.split(C_SPACE); | ||
length = pairs.length; | ||
index = -1; | ||
High.prototype = high; | ||
while (++index < length) { | ||
pair = pairs[index].split(C_PIPE); | ||
low = new High(); // Ha! | ||
compiledKeywords[pair[0]] = [ | ||
className, | ||
pair[1] ? Number(pair[1]) : 1 | ||
]; | ||
} | ||
} | ||
} | ||
low.highlight = highlight; | ||
low.highlightAuto = autoHighlight; | ||
low.registerLanguage = registerLanguage; | ||
low.getLanguage = getLanguage; | ||
/* Create a regex for `value`. */ | ||
function langRe(value, global) { | ||
return new RegExp( | ||
source(value), | ||
'm' + (language[KEY_INSENSITIVE] ? 'i' : '') + | ||
(global ? 'g' : '') | ||
); | ||
} | ||
/* | ||
* Expose. | ||
*/ | ||
/* Get the source of an expression or string. */ | ||
function source(re) { | ||
return (re && re.source) || re; | ||
} | ||
} | ||
module.exports = low; | ||
/* Normalize a syntax result. */ | ||
function normalize(result) { | ||
return { | ||
relevance: result.relevance || 0, | ||
language: result.language || null, | ||
value: result.value || [] | ||
}; | ||
} | ||
/* Check if `expression` matches `lexeme`. */ | ||
function test(expression, lexeme) { | ||
var match = expression && expression.exec(lexeme); | ||
return match && match.index === 0; | ||
} | ||
/* No-op exec. */ | ||
function execNoop() { | ||
return null; | ||
} | ||
/* Get a language by `name`. */ | ||
function getLanguage(name) { | ||
name = name.toLowerCase(); | ||
return languages[name] || languages[aliases[name]]; | ||
} |
{ | ||
"name": "lowlight", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "Virtual syntax highlighting for virtual DOMs and non-HTML things", | ||
@@ -19,6 +19,3 @@ "license": "MIT", | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/wooorm/lowlight.git" | ||
}, | ||
"repository": "https://github.com/wooorm/lowlight", | ||
"bugs": "https://github.com/wooorm/lowlight/issues", | ||
@@ -31,3 +28,3 @@ "author": "Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)", | ||
"dependencies": { | ||
"highlight.js": "~9.4.0" | ||
"highlight.js": "~9.6.0" | ||
}, | ||
@@ -37,16 +34,10 @@ "devDependencies": { | ||
"chalk": "^1.1.1", | ||
"eslint": "^2.0.0", | ||
"esmangle": "^1.0.0", | ||
"hast": "0.0.2", | ||
"istanbul": "^0.4.0", | ||
"jscs": "^3.0.0", | ||
"jscs-jsdoc": "^2.0.0", | ||
"remark": "^4.0.0", | ||
"remark-comment-config": "^3.0.0", | ||
"remark-github": "^4.0.1", | ||
"remark-lint": "^3.0.0", | ||
"remark-usage": "^3.0.0", | ||
"remark-validate-links": "^3.0.0", | ||
"rehype": "2.0.0", | ||
"nyc": "^8.1.0", | ||
"remark-cli": "^2.0.0", | ||
"remark-preset-wooorm": "^1.0.0", | ||
"tape": "^4.0.0", | ||
"unist-util-visit": "^1.0.0" | ||
"unist-util-visit": "^1.0.0", | ||
"xo": "^0.16.0" | ||
}, | ||
@@ -59,9 +50,27 @@ "scripts": { | ||
"build": "npm run build-registry && npm run build-md && npm run build-bundle && npm run build-mangle", | ||
"lint-api": "eslint .", | ||
"lint-style": "jscs --reporter inline .", | ||
"lint": "npm run lint-api && npm run lint-style", | ||
"lint": "xo", | ||
"test-api": "node test/index.js", | ||
"test-coverage": "istanbul cover test/index.js", | ||
"test-coverage": "nyc --reporter lcov tape test/index.js", | ||
"test": "npm run build && npm run lint && npm run test-coverage" | ||
}, | ||
"nyc": { | ||
"check-coverage": true, | ||
"lines": 100, | ||
"functions": 100, | ||
"branches": 100 | ||
}, | ||
"xo": { | ||
"space": true, | ||
"rules": { | ||
"max-lines": "off", | ||
"max-params": "off" | ||
}, | ||
"ignores": [ | ||
"lowlight.js" | ||
] | ||
}, | ||
"remarkConfig": { | ||
"output": true, | ||
"presets": "wooorm" | ||
} | ||
} |
133
readme.md
@@ -8,7 +8,19 @@ # lowlight [![Build Status][travis-badge]][travis] [![Coverage Status][codecov-badge]][codecov] | ||
Lowlight is built to work with all syntaxes supported by [highlight.js][], | ||
that’s [155 languages][names] (and all 73 themes). | ||
that’s [166 languages][names] (and all 73 themes). | ||
## Table of Contents | ||
* [Installation](#installation) | ||
* [Usage](#usage) | ||
* [API](#api) | ||
* [low.highlight(language, value\[, options\])](#lowhighlightlanguage-value-options) | ||
* [low.highlightAuto(value\[, options\])](#lowhighlightautovalue-options) | ||
* [low.registerLanguage(name, syntax)](#lowregisterlanguagename-syntax) | ||
* [Browser](#browser) | ||
* [Projects](#projects) | ||
* [License](#license) | ||
## Installation | ||
[npm][npm-install]: | ||
[npm][]: | ||
@@ -19,56 +31,39 @@ ```bash | ||
**lowlight** is also available for [duo][duo-install], and as an AMD, | ||
CommonJS, and globals module, [uncompressed and compressed][releases]. | ||
Read more about [usage in the browser][browser]. | ||
[Usage in the browser »][browser] | ||
## Usage | ||
Dependencies: | ||
Highlight: | ||
```javascript | ||
var hast = require('hast'); | ||
var low = require('lowlight'); | ||
var ast = low.highlight('js', '"use strict";').value; | ||
``` | ||
Compile: | ||
Yields: | ||
```javascript | ||
var ast = low.highlight('js', '"use strict";').value; | ||
var html = hast.stringify({'type': 'root', 'children': ast}); | ||
```js | ||
[ { type: 'element', | ||
tagName: 'span', | ||
properties: { className: [ 'hljs-meta' ] }, | ||
children: [ { type: 'text', value: '"use strict"' } ] }, | ||
{ type: 'text', value: ';' } ] | ||
``` | ||
Yields: | ||
Or, stringified with [rehype][]: | ||
```json | ||
[ | ||
{ | ||
"type": "element", | ||
"tagName": "span", | ||
"properties": { | ||
"className": [ | ||
"hljs-meta" | ||
] | ||
}, | ||
"children": [ | ||
{ | ||
"type": "text", | ||
"value": "\"use strict\"" | ||
} | ||
] | ||
}, | ||
{ | ||
"type": "text", | ||
"value": ";" | ||
} | ||
] | ||
```js | ||
var rehype = require('rehype'); | ||
var html = rehype().stringify({type: 'root', children: ast})); | ||
``` | ||
Or, stringified with [hast][]: | ||
Yields: | ||
```html | ||
<span class="hljs-meta">"use strict"</span>; | ||
<span class="hljs-meta">"use strict"</span>; | ||
``` | ||
> **Tip**: Use [`hast-to-hyperscript`][to-hyperscript] to transform | ||
> to other virtual DOMs, or DIY. | ||
## API | ||
@@ -80,14 +75,12 @@ | ||
**Parameters** | ||
##### Parameters | ||
* `name` (`string`) — See list of [names and aliases][names]; | ||
* `value` (`string`) — Source to highlight; | ||
* `options` (`Object?`, optional): | ||
* `prefix` (`string?`, default: `'hljs-'`) — Class prefix. | ||
* `prefix` (`string?`, default: `'hljs-'`) | ||
— Class prefix. | ||
###### Returns | ||
**Returns**: `Object`: | ||
`Object`: | ||
@@ -97,5 +90,3 @@ * `relevance` (`number`) | ||
the given language; | ||
* `language` (`string`) — The given `language`; | ||
* `value` ([`Array.<Node>`][hast-node]) — [Hast nodes][hast-node] | ||
@@ -108,11 +99,8 @@ representing the highlighted given code. | ||
**Parameters** | ||
###### Parameters | ||
* `value` (`string`) — Source to highlight; | ||
* `options` (`Object?`, optional): | ||
* `prefix` (`string?`, default: `'hljs-'`) | ||
— Class prefix; | ||
* `subset` (`Array.<string>?`, optional, defaults to | ||
@@ -122,13 +110,12 @@ all registered languages.) | ||
**Returns**: `Object`: | ||
###### Returns | ||
`Object`: | ||
* `relevance` (`number`) | ||
— Integer representing how sure **low** is the given code | ||
is in the detected language; | ||
* `language` (`string`) — The given `language`; | ||
* `value` ([`Array.<Node>`][hast-node]) — [Hast nodes][hast-node] | ||
representing the highlighted given code. | ||
* `secondBest` (`Object?`) | ||
@@ -141,15 +128,14 @@ — Object with the same structure as the top returned object, but | ||
Register a syntax. Useful in the browser or with custom grammars. | ||
Register a syntax. Useful in the browser or with custom grammars. | ||
**Parameters** | ||
###### Parameters | ||
* `name` (`string`) — Name of language; | ||
* `syntax` (`Function`) — Syntax highlighter, see | ||
[`highlight.js`s docs][syntax] for more information. | ||
## Lowlight in the browser | ||
## Browser | ||
I do not suggest using the pre-built files or requiring `lowlight` in | ||
the browser as that would include a 530kb (170kb with GZIP) file. | ||
the browser as that would include a 530kb (170kb GZipped) file. | ||
@@ -169,5 +155,18 @@ Instead, require `lowlight/lib/core`, and include only the used | ||
...When using browserify, minifying, and GZIP that results in just | ||
17kb of code (7kb with GZIP). | ||
...When using browserify, minifying this results in just 17kb of code | ||
(7kb with GZip). | ||
## Projects | ||
* [`emphasize`](https://github.com/wooorm/emphasize) | ||
— Syntax highlighting in ANSI, for the terminal; | ||
* [`react-lowlight`](https://github.com/rexxars/react-lowlight) | ||
— Syntax highlighter for [React][]; | ||
* [`react-syntax-highlighter`](https://github.com/conorhastings/react-syntax-highlighter) | ||
— [React][] component for syntax highlighting. | ||
* [`rehype-highlight`](https://github.com/wooorm/rehype-highlight) | ||
— Syntax highlighting in [**rehype**](https://github.com/wooorm/rehype); | ||
* [`remark-highlight.js`](https://github.com/ben-eb/remark-highlight.js) | ||
— Syntax highlighting in [**remark**](https://github.com/wooorm/remark); | ||
## License | ||
@@ -187,8 +186,4 @@ | ||
[npm-install]: https://docs.npmjs.com/cli/install | ||
[npm]: https://docs.npmjs.com/cli/install | ||
[duo-install]: http://duojs.org/#getting-started | ||
[releases]: https://github.com/wooorm/lowlight/releases | ||
[license]: LICENSE | ||
@@ -198,3 +193,3 @@ | ||
[hast]: https://github.com/wooorm/hast | ||
[rehype]: https://github.com/wooorm/rehype | ||
@@ -213,2 +208,4 @@ [hast-node]: https://github.com/wooorm/hast#nodes | ||
[browser]: #lowlight-in-the-browser | ||
[to-hyperscript]: https://github.com/wooorm/hast-to-hyperscript | ||
[browser]: #browser |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
10
36778
4
745
201
1
1
+ Addedhighlight.js@9.6.0(transitive)
- Removedhighlight.js@9.4.0(transitive)
Updatedhighlight.js@~9.6.0