From 28f81b41e3ed644847c4dfc7d6e9accccf5616a5 Mon Sep 17 00:00:00 2001 From: "Marcus R. Brown" Date: Thu, 3 Dec 2020 19:42:50 -0700 Subject: [PATCH] Add an option to specify the Highlight.js instance and provide a core plugin --- README.md | 29 +++++++++++++++++++- core.js | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++ index.js | 76 +++++----------------------------------------------- package.json | 1 + test.js | 16 +++++++++++ 5 files changed, 128 insertions(+), 70 deletions(-) create mode 100644 core.js diff --git a/README.md b/README.md index 5284951..747e557 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# markdown-it-highlightjs [![npm version](http://img.shields.io/npm/v/markdown-it-highlightjs.svg?style=flat-square)](https://www.npmjs.org/package/markdown-it-highlightjs) +# markdown-it-highlightjs [![npm version](https://img.shields.io/npm/v/markdown-it-highlightjs.svg?style=flat-square)](https://www.npmjs.org/package/markdown-it-highlightjs) > Preset to use [highlight.js] with [markdown-it]. @@ -23,6 +23,7 @@ Name | Type | Description `code` | boolean | Whether to add the `hljs` class to raw code blocks (not fenced blocks). | `true` `register` | object | Register other languages which are not included in the standard pack. | `null` `inline` | boolean | Whether to highlight inline code. | `false` +`hljs` | object | Provide the instance of [highlight.js] to use for highlighting | `require('highlight.js')` ### Register languages @@ -57,3 +58,29 @@ or [Kramdown IAL syntax](https://kramdown.gettalong.org/syntax.html#inline-attri ``` If you do not specify a language, then highlight.js will attempt to guess the language if `auto` is true (which it is by default). + +### Provide the [highlight.js] instance + +You can specify the `hljs` option to override the default [highlight.js] instance with your own: + +```js +const hljs = require('highlight.js/lib/core') + +hljs.registerLanguage( + 'javascript', + require('highlight.js/lib/languages/javascript') +) + +const md = require('markdown-it')() + .use(require('markdown-it-highlightjs'), { hljs }) +``` + +### Core plugin + +You may import the core `markdown-it-highlightjs` plugin directly, without any default options. You must specify an instance of [highlight.js] for the `hljs` option. + +```js +const hljs = require('highlight.js/lib/core') +const md = require('markdown-it')() + .use(require('markdown-it-highlightjs/core'), { hljs }) +``` diff --git a/core.js b/core.js new file mode 100644 index 0000000..fcad487 --- /dev/null +++ b/core.js @@ -0,0 +1,76 @@ +function maybe (f) { + try { + return f() + } catch (e) { + return false + } +} + +let hljs + +// Allow registration of other languages. +const registerLangs = (register) => register && + Object.entries(register).map(([lang, pack]) => { hljs.registerLanguage(lang, pack) }) + +// Highlight with given language. +const highlight = (code, lang) => + maybe(() => hljs.highlight(lang || 'plaintext', code, true).value) || '' + +// Highlight with given language or automatically. +const highlightAuto = (code, lang) => + lang + ? highlight(code, lang) + : maybe(() => hljs.highlightAuto(code).value) || '' + +// Wrap a render function to add `hljs` class to code blocks. +const wrap = render => + function (...args) { + return render.apply(this, args) + .replace('', '') + } + +function inlineCodeRenderer (md, tokens, idx, options) { + const code = tokens[idx] + const next = tokens[idx + 1] + let lang + + if (next && next.type === 'text') { + // Match kramdown- or pandoc-style language specifier. + // e.g. `code`{:.ruby} or `code`{.haskell} + const match = /^{:?\.([^}]+)}/.exec(next.content) + + if (match) { + lang = match[1] + + // Remove the language specification from text following the code. + next.content = next.content.slice(match[0].length) + } + } + + const highlighted = options.highlight(code.content, lang) + const cls = lang ? ` class="${options.langPrefix}${md.utils.escapeHtml(lang)}"` : '' + + return `${highlighted}` +} + +module.exports = (md, opts) => { + opts = Object.assign({}, opts) + hljs = opts.hljs + if (!hljs) { + throw new Error('A hljs instance is required.') + } + + registerLangs(opts.register) + + md.options.highlight = opts.auto ? highlightAuto : highlight + md.renderer.rules.fence = wrap(md.renderer.rules.fence) + + if (opts.code) { + md.renderer.rules.code_block = wrap(md.renderer.rules.code_block) + } + + if (opts.inline) { + md.renderer.rules.code_inline = inlineCodeRenderer.bind(null, md) + } +} diff --git a/index.js b/index.js index 3cec226..04c5fee 100644 --- a/index.js +++ b/index.js @@ -1,73 +1,11 @@ -const hljs = require('highlight.js') - -function maybe (f) { - try { - return f() - } catch (e) { - return false - } -} - -// Allow registration of other languages. -const registerLangs = (register) => register && - Object.entries(register).map(([lang, pack]) => { hljs.registerLanguage(lang, pack) }) - -// Highlight with given language. -const highlight = (code, lang) => - maybe(() => hljs.highlight(lang || 'plaintext', code, true).value) || '' - -// Highlight with given language or automatically. -const highlightAuto = (code, lang) => - lang - ? highlight(code, lang) - : maybe(() => hljs.highlightAuto(code).value) || '' - -// Wrap a render function to add `hljs` class to code blocks. -const wrap = render => - function (...args) { - return render.apply(this, args) - .replace('', '') - } - -function inlineCodeRenderer (md, tokens, idx, options) { - const code = tokens[idx] - const next = tokens[idx + 1] - let lang - - if (next && next.type === 'text') { - // Match kramdown- or pandoc-style language specifier. - // e.g. `code`{:.ruby} or `code`{.haskell} - const match = /^{:?\.([^}]+)}/.exec(next.content) - - if (match) { - lang = match[1] - - // Remove the language specification from text following the code. - next.content = next.content.slice(match[0].length) - } - } - - const highlighted = options.highlight(code.content, lang) - const cls = lang ? ` class="${options.langPrefix}${md.utils.escapeHtml(lang)}"` : '' - - return `${highlighted}` -} - const highlightjs = (md, opts) => { - opts = Object.assign({}, highlightjs.defaults, opts) - registerLangs(opts.register) - - md.options.highlight = opts.auto ? highlightAuto : highlight - md.renderer.rules.fence = wrap(md.renderer.rules.fence) - - if (opts.code) { - md.renderer.rules.code_block = wrap(md.renderer.rules.code_block) - } - - if (opts.inline) { - md.renderer.rules.code_inline = inlineCodeRenderer.bind(null, md) - } + opts = Object.assign( + {}, + highlightjs.defaults, + { hljs: (opts && opts.hljs) || require('highlight.js') }, + opts + ) + return require('./core')(md, opts) } highlightjs.defaults = { diff --git a/package.json b/package.json index c4a773c..3165b5b 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "files": [ "README.md", "UNLICENSE", + "core.js", "index.js" ], "repository": { diff --git a/test.js b/test.js index 2352d3d..fd100bd 100644 --- a/test.js +++ b/test.js @@ -2,6 +2,22 @@ const { strictEqual: equal } = require('assert') const md = require('markdown-it') const highlightjs = require('./') +equal( + (() => { + try { + return md().use(require('./core')) + } catch (ex) { + return ex.message + } + })(), + 'A hljs instance is required.' +) + +equal( + md().use(highlightjs, { hljs: require('highlight.js/lib/core') }).render('```js\nconsole.log(42)\n```'), + `
console.log(42)\n
+`) + equal( md().use(highlightjs).render('```js\nconsole.log(42)\n```'), `
console.log(42)