diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ab2d78f..983711c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,13 @@ ## [Unreleased] +### Added + +- `highlightjs` getter, to access the generated highlight.js instance per Marp Core instances ([#350](https://github.com/marp-team/marp-core/pull/350)) + ### Changed +- Marp Core instance is no longer using the shared highlight.js instance ([#350](https://github.com/marp-team/marp-core/pull/350)) - Upgrade Node.js and dependent packages to the latest version ([#351](https://github.com/marp-team/marp-core/pull/351)) ## v3.7.0 - 2023-06-09 diff --git a/marp.config.mjs b/marp.config.mjs index 37fdf73a..27aee53b 100644 --- a/marp.config.mjs +++ b/marp.config.mjs @@ -1,11 +1,12 @@ import path from 'node:path' -const dirname = path.dirname(new URL(import.meta.url).pathname) - export default { - engine: path.join(dirname, './lib/marp.js'), + engine: './lib/marp.js', server: true, - inputDir: path.join(dirname, './sandbox'), + inputDir: path.join( + path.dirname(new URL(import.meta.url).pathname), + './sandbox', + ), html: true, options: { minifyCSS: false, diff --git a/src/highlightjs.ts b/src/highlightjs.ts new file mode 100644 index 00000000..6da0818d --- /dev/null +++ b/src/highlightjs.ts @@ -0,0 +1,386 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +import hljsCore from 'highlight.js/lib/core' + +export const generateHighlightJSInstance = () => { + // Create a new instance to avoid polluation to global highlight.js module by users. + const hljs = hljsCore.newInstance() + + // Ported from highlight.js/lib/index.js + hljs.registerLanguage('1c', require('highlight.js/lib/languages/1c')) + hljs.registerLanguage('abnf', require('highlight.js/lib/languages/abnf')) + hljs.registerLanguage( + 'accesslog', + require('highlight.js/lib/languages/accesslog'), + ) + hljs.registerLanguage( + 'actionscript', + require('highlight.js/lib/languages/actionscript'), + ) + hljs.registerLanguage('ada', require('highlight.js/lib/languages/ada')) + hljs.registerLanguage( + 'angelscript', + require('highlight.js/lib/languages/angelscript'), + ) + hljs.registerLanguage('apache', require('highlight.js/lib/languages/apache')) + hljs.registerLanguage( + 'applescript', + require('highlight.js/lib/languages/applescript'), + ) + hljs.registerLanguage('arcade', require('highlight.js/lib/languages/arcade')) + hljs.registerLanguage( + 'arduino', + require('highlight.js/lib/languages/arduino'), + ) + hljs.registerLanguage('armasm', require('highlight.js/lib/languages/armasm')) + hljs.registerLanguage('xml', require('highlight.js/lib/languages/xml')) + hljs.registerLanguage( + 'asciidoc', + require('highlight.js/lib/languages/asciidoc'), + ) + hljs.registerLanguage( + 'aspectj', + require('highlight.js/lib/languages/aspectj'), + ) + hljs.registerLanguage( + 'autohotkey', + require('highlight.js/lib/languages/autohotkey'), + ) + hljs.registerLanguage('autoit', require('highlight.js/lib/languages/autoit')) + hljs.registerLanguage('avrasm', require('highlight.js/lib/languages/avrasm')) + hljs.registerLanguage('awk', require('highlight.js/lib/languages/awk')) + hljs.registerLanguage('axapta', require('highlight.js/lib/languages/axapta')) + hljs.registerLanguage('bash', require('highlight.js/lib/languages/bash')) + hljs.registerLanguage('basic', require('highlight.js/lib/languages/basic')) + hljs.registerLanguage('bnf', require('highlight.js/lib/languages/bnf')) + hljs.registerLanguage( + 'brainfuck', + require('highlight.js/lib/languages/brainfuck'), + ) + hljs.registerLanguage('c', require('highlight.js/lib/languages/c')) + hljs.registerLanguage('cal', require('highlight.js/lib/languages/cal')) + hljs.registerLanguage( + 'capnproto', + require('highlight.js/lib/languages/capnproto'), + ) + hljs.registerLanguage('ceylon', require('highlight.js/lib/languages/ceylon')) + hljs.registerLanguage('clean', require('highlight.js/lib/languages/clean')) + hljs.registerLanguage( + 'clojure', + require('highlight.js/lib/languages/clojure'), + ) + hljs.registerLanguage( + 'clojure-repl', + require('highlight.js/lib/languages/clojure-repl'), + ) + hljs.registerLanguage('cmake', require('highlight.js/lib/languages/cmake')) + hljs.registerLanguage( + 'coffeescript', + require('highlight.js/lib/languages/coffeescript'), + ) + hljs.registerLanguage('coq', require('highlight.js/lib/languages/coq')) + hljs.registerLanguage('cos', require('highlight.js/lib/languages/cos')) + hljs.registerLanguage('cpp', require('highlight.js/lib/languages/cpp')) + hljs.registerLanguage('crmsh', require('highlight.js/lib/languages/crmsh')) + hljs.registerLanguage( + 'crystal', + require('highlight.js/lib/languages/crystal'), + ) + hljs.registerLanguage('csharp', require('highlight.js/lib/languages/csharp')) + hljs.registerLanguage('csp', require('highlight.js/lib/languages/csp')) + hljs.registerLanguage('css', require('highlight.js/lib/languages/css')) + hljs.registerLanguage('d', require('highlight.js/lib/languages/d')) + hljs.registerLanguage( + 'markdown', + require('highlight.js/lib/languages/markdown'), + ) + hljs.registerLanguage('dart', require('highlight.js/lib/languages/dart')) + hljs.registerLanguage('delphi', require('highlight.js/lib/languages/delphi')) + hljs.registerLanguage('diff', require('highlight.js/lib/languages/diff')) + hljs.registerLanguage('django', require('highlight.js/lib/languages/django')) + hljs.registerLanguage('dns', require('highlight.js/lib/languages/dns')) + hljs.registerLanguage( + 'dockerfile', + require('highlight.js/lib/languages/dockerfile'), + ) + hljs.registerLanguage('dos', require('highlight.js/lib/languages/dos')) + hljs.registerLanguage( + 'dsconfig', + require('highlight.js/lib/languages/dsconfig'), + ) + hljs.registerLanguage('dts', require('highlight.js/lib/languages/dts')) + hljs.registerLanguage('dust', require('highlight.js/lib/languages/dust')) + hljs.registerLanguage('ebnf', require('highlight.js/lib/languages/ebnf')) + hljs.registerLanguage('elixir', require('highlight.js/lib/languages/elixir')) + hljs.registerLanguage('elm', require('highlight.js/lib/languages/elm')) + hljs.registerLanguage('ruby', require('highlight.js/lib/languages/ruby')) + hljs.registerLanguage('erb', require('highlight.js/lib/languages/erb')) + hljs.registerLanguage( + 'erlang-repl', + require('highlight.js/lib/languages/erlang-repl'), + ) + hljs.registerLanguage('erlang', require('highlight.js/lib/languages/erlang')) + hljs.registerLanguage('excel', require('highlight.js/lib/languages/excel')) + hljs.registerLanguage('fix', require('highlight.js/lib/languages/fix')) + hljs.registerLanguage('flix', require('highlight.js/lib/languages/flix')) + hljs.registerLanguage( + 'fortran', + require('highlight.js/lib/languages/fortran'), + ) + hljs.registerLanguage('fsharp', require('highlight.js/lib/languages/fsharp')) + hljs.registerLanguage('gams', require('highlight.js/lib/languages/gams')) + hljs.registerLanguage('gauss', require('highlight.js/lib/languages/gauss')) + hljs.registerLanguage('gcode', require('highlight.js/lib/languages/gcode')) + hljs.registerLanguage( + 'gherkin', + require('highlight.js/lib/languages/gherkin'), + ) + hljs.registerLanguage('glsl', require('highlight.js/lib/languages/glsl')) + hljs.registerLanguage('gml', require('highlight.js/lib/languages/gml')) + hljs.registerLanguage('go', require('highlight.js/lib/languages/go')) + hljs.registerLanguage('golo', require('highlight.js/lib/languages/golo')) + hljs.registerLanguage('gradle', require('highlight.js/lib/languages/gradle')) + hljs.registerLanguage( + 'graphql', + require('highlight.js/lib/languages/graphql'), + ) + hljs.registerLanguage('groovy', require('highlight.js/lib/languages/groovy')) + hljs.registerLanguage('haml', require('highlight.js/lib/languages/haml')) + hljs.registerLanguage( + 'handlebars', + require('highlight.js/lib/languages/handlebars'), + ) + hljs.registerLanguage( + 'haskell', + require('highlight.js/lib/languages/haskell'), + ) + hljs.registerLanguage('haxe', require('highlight.js/lib/languages/haxe')) + hljs.registerLanguage('hsp', require('highlight.js/lib/languages/hsp')) + hljs.registerLanguage('http', require('highlight.js/lib/languages/http')) + hljs.registerLanguage('hy', require('highlight.js/lib/languages/hy')) + hljs.registerLanguage( + 'inform7', + require('highlight.js/lib/languages/inform7'), + ) + hljs.registerLanguage('ini', require('highlight.js/lib/languages/ini')) + hljs.registerLanguage('irpf90', require('highlight.js/lib/languages/irpf90')) + hljs.registerLanguage('isbl', require('highlight.js/lib/languages/isbl')) + hljs.registerLanguage('java', require('highlight.js/lib/languages/java')) + hljs.registerLanguage( + 'javascript', + require('highlight.js/lib/languages/javascript'), + ) + hljs.registerLanguage( + 'jboss-cli', + require('highlight.js/lib/languages/jboss-cli'), + ) + hljs.registerLanguage('json', require('highlight.js/lib/languages/json')) + hljs.registerLanguage('julia', require('highlight.js/lib/languages/julia')) + hljs.registerLanguage( + 'julia-repl', + require('highlight.js/lib/languages/julia-repl'), + ) + hljs.registerLanguage('kotlin', require('highlight.js/lib/languages/kotlin')) + hljs.registerLanguage('lasso', require('highlight.js/lib/languages/lasso')) + hljs.registerLanguage('latex', require('highlight.js/lib/languages/latex')) + hljs.registerLanguage('ldif', require('highlight.js/lib/languages/ldif')) + hljs.registerLanguage('leaf', require('highlight.js/lib/languages/leaf')) + hljs.registerLanguage('less', require('highlight.js/lib/languages/less')) + hljs.registerLanguage('lisp', require('highlight.js/lib/languages/lisp')) + hljs.registerLanguage( + 'livecodeserver', + require('highlight.js/lib/languages/livecodeserver'), + ) + hljs.registerLanguage( + 'livescript', + require('highlight.js/lib/languages/livescript'), + ) + hljs.registerLanguage('llvm', require('highlight.js/lib/languages/llvm')) + hljs.registerLanguage('lsl', require('highlight.js/lib/languages/lsl')) + hljs.registerLanguage('lua', require('highlight.js/lib/languages/lua')) + hljs.registerLanguage( + 'makefile', + require('highlight.js/lib/languages/makefile'), + ) + hljs.registerLanguage( + 'mathematica', + require('highlight.js/lib/languages/mathematica'), + ) + hljs.registerLanguage('matlab', require('highlight.js/lib/languages/matlab')) + hljs.registerLanguage('maxima', require('highlight.js/lib/languages/maxima')) + hljs.registerLanguage('mel', require('highlight.js/lib/languages/mel')) + hljs.registerLanguage( + 'mercury', + require('highlight.js/lib/languages/mercury'), + ) + hljs.registerLanguage( + 'mipsasm', + require('highlight.js/lib/languages/mipsasm'), + ) + hljs.registerLanguage('mizar', require('highlight.js/lib/languages/mizar')) + hljs.registerLanguage('perl', require('highlight.js/lib/languages/perl')) + hljs.registerLanguage( + 'mojolicious', + require('highlight.js/lib/languages/mojolicious'), + ) + hljs.registerLanguage('monkey', require('highlight.js/lib/languages/monkey')) + hljs.registerLanguage( + 'moonscript', + require('highlight.js/lib/languages/moonscript'), + ) + hljs.registerLanguage('n1ql', require('highlight.js/lib/languages/n1ql')) + hljs.registerLanguage( + 'nestedtext', + require('highlight.js/lib/languages/nestedtext'), + ) + hljs.registerLanguage('nginx', require('highlight.js/lib/languages/nginx')) + hljs.registerLanguage('nim', require('highlight.js/lib/languages/nim')) + hljs.registerLanguage('nix', require('highlight.js/lib/languages/nix')) + hljs.registerLanguage( + 'node-repl', + require('highlight.js/lib/languages/node-repl'), + ) + hljs.registerLanguage('nsis', require('highlight.js/lib/languages/nsis')) + hljs.registerLanguage( + 'objectivec', + require('highlight.js/lib/languages/objectivec'), + ) + hljs.registerLanguage('ocaml', require('highlight.js/lib/languages/ocaml')) + hljs.registerLanguage( + 'openscad', + require('highlight.js/lib/languages/openscad'), + ) + hljs.registerLanguage( + 'oxygene', + require('highlight.js/lib/languages/oxygene'), + ) + hljs.registerLanguage( + 'parser3', + require('highlight.js/lib/languages/parser3'), + ) + hljs.registerLanguage('pf', require('highlight.js/lib/languages/pf')) + hljs.registerLanguage('pgsql', require('highlight.js/lib/languages/pgsql')) + hljs.registerLanguage('php', require('highlight.js/lib/languages/php')) + hljs.registerLanguage( + 'php-template', + require('highlight.js/lib/languages/php-template'), + ) + hljs.registerLanguage( + 'plaintext', + require('highlight.js/lib/languages/plaintext'), + ) + hljs.registerLanguage('pony', require('highlight.js/lib/languages/pony')) + hljs.registerLanguage( + 'powershell', + require('highlight.js/lib/languages/powershell'), + ) + hljs.registerLanguage( + 'processing', + require('highlight.js/lib/languages/processing'), + ) + hljs.registerLanguage( + 'profile', + require('highlight.js/lib/languages/profile'), + ) + hljs.registerLanguage('prolog', require('highlight.js/lib/languages/prolog')) + hljs.registerLanguage( + 'properties', + require('highlight.js/lib/languages/properties'), + ) + hljs.registerLanguage( + 'protobuf', + require('highlight.js/lib/languages/protobuf'), + ) + hljs.registerLanguage('puppet', require('highlight.js/lib/languages/puppet')) + hljs.registerLanguage( + 'purebasic', + require('highlight.js/lib/languages/purebasic'), + ) + hljs.registerLanguage('python', require('highlight.js/lib/languages/python')) + hljs.registerLanguage( + 'python-repl', + require('highlight.js/lib/languages/python-repl'), + ) + hljs.registerLanguage('q', require('highlight.js/lib/languages/q')) + hljs.registerLanguage('qml', require('highlight.js/lib/languages/qml')) + hljs.registerLanguage('r', require('highlight.js/lib/languages/r')) + hljs.registerLanguage( + 'reasonml', + require('highlight.js/lib/languages/reasonml'), + ) + hljs.registerLanguage('rib', require('highlight.js/lib/languages/rib')) + hljs.registerLanguage( + 'roboconf', + require('highlight.js/lib/languages/roboconf'), + ) + hljs.registerLanguage( + 'routeros', + require('highlight.js/lib/languages/routeros'), + ) + hljs.registerLanguage('rsl', require('highlight.js/lib/languages/rsl')) + hljs.registerLanguage( + 'ruleslanguage', + require('highlight.js/lib/languages/ruleslanguage'), + ) + hljs.registerLanguage('rust', require('highlight.js/lib/languages/rust')) + hljs.registerLanguage('sas', require('highlight.js/lib/languages/sas')) + hljs.registerLanguage('scala', require('highlight.js/lib/languages/scala')) + hljs.registerLanguage('scheme', require('highlight.js/lib/languages/scheme')) + hljs.registerLanguage('scilab', require('highlight.js/lib/languages/scilab')) + hljs.registerLanguage('scss', require('highlight.js/lib/languages/scss')) + hljs.registerLanguage('shell', require('highlight.js/lib/languages/shell')) + hljs.registerLanguage('smali', require('highlight.js/lib/languages/smali')) + hljs.registerLanguage( + 'smalltalk', + require('highlight.js/lib/languages/smalltalk'), + ) + hljs.registerLanguage('sml', require('highlight.js/lib/languages/sml')) + hljs.registerLanguage('sqf', require('highlight.js/lib/languages/sqf')) + hljs.registerLanguage('sql', require('highlight.js/lib/languages/sql')) + hljs.registerLanguage('stan', require('highlight.js/lib/languages/stan')) + hljs.registerLanguage('stata', require('highlight.js/lib/languages/stata')) + hljs.registerLanguage('step21', require('highlight.js/lib/languages/step21')) + hljs.registerLanguage('stylus', require('highlight.js/lib/languages/stylus')) + hljs.registerLanguage( + 'subunit', + require('highlight.js/lib/languages/subunit'), + ) + hljs.registerLanguage('swift', require('highlight.js/lib/languages/swift')) + hljs.registerLanguage( + 'taggerscript', + require('highlight.js/lib/languages/taggerscript'), + ) + hljs.registerLanguage('yaml', require('highlight.js/lib/languages/yaml')) + hljs.registerLanguage('tap', require('highlight.js/lib/languages/tap')) + hljs.registerLanguage('tcl', require('highlight.js/lib/languages/tcl')) + hljs.registerLanguage('thrift', require('highlight.js/lib/languages/thrift')) + hljs.registerLanguage('tp', require('highlight.js/lib/languages/tp')) + hljs.registerLanguage('twig', require('highlight.js/lib/languages/twig')) + hljs.registerLanguage( + 'typescript', + require('highlight.js/lib/languages/typescript'), + ) + hljs.registerLanguage('vala', require('highlight.js/lib/languages/vala')) + hljs.registerLanguage('vbnet', require('highlight.js/lib/languages/vbnet')) + hljs.registerLanguage( + 'vbscript', + require('highlight.js/lib/languages/vbscript'), + ) + hljs.registerLanguage( + 'vbscript-html', + require('highlight.js/lib/languages/vbscript-html'), + ) + hljs.registerLanguage( + 'verilog', + require('highlight.js/lib/languages/verilog'), + ) + hljs.registerLanguage('vhdl', require('highlight.js/lib/languages/vhdl')) + hljs.registerLanguage('vim', require('highlight.js/lib/languages/vim')) + hljs.registerLanguage('wasm', require('highlight.js/lib/languages/wasm')) + hljs.registerLanguage('wren', require('highlight.js/lib/languages/wren')) + hljs.registerLanguage('x86asm', require('highlight.js/lib/languages/x86asm')) + hljs.registerLanguage('xl', require('highlight.js/lib/languages/xl')) + hljs.registerLanguage('xquery', require('highlight.js/lib/languages/xquery')) + hljs.registerLanguage('zephir', require('highlight.js/lib/languages/zephir')) + + return hljs +} diff --git a/src/marp.ts b/src/marp.ts index a3c62ce5..66046163 100644 --- a/src/marp.ts +++ b/src/marp.ts @@ -1,5 +1,5 @@ import { Marpit, Options, ThemeSetPackOptions } from '@marp-team/marpit' -import highlightjs from 'highlight.js' +import type { HLJSApi } from 'highlight.js' import postcss, { AcceptedPlugin } from 'postcss' import defaultTheme from '../themes/default.scss' import gaiaTheme from '../themes/gaia.scss' @@ -7,6 +7,7 @@ import uncoverTheme from '../themes/uncover.scss' import * as autoScalingPlugin from './auto-scaling' import * as customElements from './custom-elements' import * as emojiPlugin from './emoji/emoji' +import { generateHighlightJSInstance } from './highlightjs' import * as htmlPlugin from './html/html' import * as mathPlugin from './math/math' import minifyPlugins from './prebundles/postcss-minify-plugins' @@ -33,6 +34,8 @@ export interface MarpOptions extends Options { export class Marp extends Marpit { readonly options!: Required + private _highlightjs: HLJSApi | undefined + static readonly html = { br: [] } constructor(opts: MarpOptions = {}) { @@ -90,10 +93,17 @@ export class Marp extends Marpit { .use(slugPlugin.markdown) } + get highlightjs() { + if (!this._highlightjs) { + this._highlightjs = generateHighlightJSInstance() + } + return this._highlightjs + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars highlighter(code: string, lang: string, attrs: string): string { - if (lang && highlightjs.getLanguage(lang)) { - return highlightjs.highlight(code, { + if (lang && this.highlightjs.getLanguage(lang)) { + return this.highlightjs.highlight(code, { language: lang, ignoreIllegals: true, }).value diff --git a/test/marp.ts b/test/marp.ts index ed04c1e5..5dab6f19 100644 --- a/test/marp.ts +++ b/test/marp.ts @@ -1,5 +1,6 @@ import { Marpit } from '@marp-team/marpit' import { load, CheerioOptions } from 'cheerio' +import highlightjs from 'highlight.js' import postcss, { Rule } from 'postcss' import { elements } from '../src/custom-elements/definitions' import { EmojiOptions } from '../src/emoji/emoji' @@ -1103,6 +1104,23 @@ function matchwo(a,b) expect(instance.markdown.render('```\ntest\n```')).toContain('CUSTOM')) }) + describe('with customized highlightjs instance', () => { + const instance = marp() + + instance.highlightjs.registerAliases('marp-test', { + languageName: 'javascript', + }) + + const $ = load( + instance.markdown.render('```marp-test\nconst a = 1;\n```'), + ) + + it('highlights code with customized highlightjs', () => { + expect($('code.language-marp-test')).toHaveLength(1) + expect($('code .hljs-keyword')).toHaveLength(1) + }) + }) + describe('with overriden #highlighter', () => { const instance = marp() @@ -1120,4 +1138,38 @@ function matchwo(a,b) expect($('code .customized')).toHaveLength(1)) }) }) + + describe('get #highlightjs', () => { + it('returns highlight.js instance', () => { + const instance = marp() + + expect(instance.highlightjs.highlight).toBeInstanceOf(Function) + expect(instance.highlightjs.versionString).toMatchInlineSnapshot( + `"11.8.0"`, + ) + }) + + it('has registered all highlight languages as same as highlight.js module', () => { + const instance = marp() + + const moduleLanguages = highlightjs.listLanguages() + const languages = instance.highlightjs.listLanguages() + + expect(languages).toHaveLength(moduleLanguages.length) + moduleLanguages.forEach((lang) => expect(languages).toContain(lang)) + }) + + it('does not pollute global highlight.js instance even if extended an instance of highlight.js resolved by #highlightjs', () => { + const instance = marp() + + instance.highlightjs.registerAliases('marp-test', { + languageName: 'javascript', + }) + + expect(instance.highlightjs.getLanguage('marp-test')?.name).toBe( + 'JavaScript', + ) + expect(highlightjs.getLanguage('marp-test')?.name).toBeUndefined() + }) + }) })