asciidoctor-katex 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8fe4e14368b676f0b85542bf052faaac55308d0a53229551034b0343f5efbdaf
4
+ data.tar.gz: acc3c63000b2f03af463b8e2b3129d4374aa6add7f4eef24ad1dabcd8d9597ed
5
+ SHA512:
6
+ metadata.gz: 0a0544bff51d8b8cc3db4cf041feab4c1c64a326517063d39c33e81e99b57a5883fe53634e6f8f82837862b5ff68d63635297c16231422cb836a38fa2280fb1f
7
+ data.tar.gz: acb95d9a74f1e2f02b7ea296e05e77f9825e41007c405cc258bb4cd51c0af6f8803295d08877068121ce27f65dafd7defbc230682834b0312e4d7a8022fba05a
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright 2018 Jakub Jirutka <jakub@jirutka.cz>.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.adoc ADDED
@@ -0,0 +1,139 @@
1
+ = Asciidoctor KaTeX
2
+ :source-language: shell
3
+ // custom
4
+ :gem-name: asciidoctor-katex
5
+ :gh-name: jirutka/{gem-name}
6
+ :gh-branch: master
7
+ :codacy-id: 58f6aa6e3ef04c8aad1ecd276a8a2c35
8
+
9
+ ifdef::env-github[]
10
+ image:https://travis-ci.com/{gh-name}.svg?branch={gh-branch}[Build Status, link="https://travis-ci.com/{gh-name}"]
11
+ image:https://api.codacy.com/project/badge/Coverage/{codacy-id}["Test Coverage", link="https://www.codacy.com/app/{gh-name}"]
12
+ image:https://api.codacy.com/project/badge/Grade/{codacy-id}["Codacy Code quality", link="https://www.codacy.com/app/{gh-name}"]
13
+ image:https://img.shields.io/gem/v/{gem-name}.svg?style=flat[Gem Version, link="https://rubygems.org/gems/{gem-name}"]
14
+ image:https://img.shields.io/npm/v/{gem-name}.svg?style=flat[npm Version, link="https://www.npmjs.org/package/{gem-name}"]
15
+ image:https://img.shields.io/badge/yard-docs-blue.svg[Yard Docs, link="http://www.rubydoc.info/github/{gh-name}/{gh-branch}"]
16
+ endif::env-github[]
17
+
18
+
19
+ This project provides an http://asciidoctor.org/[Asciidoctor] extension for converting block and inline https://asciidoctor.org/docs/user-manual/#activating-stem-support[STEM] in TeX notation (latexmath) to HTML using https://khan.github.io/KaTeX[KaTeX] library right during document conversion (instead of in browser on client-side).
20
+
21
+
22
+ == Requirements
23
+
24
+ === Ruby
25
+
26
+ * https://www.ruby-lang.org/[Ruby] 2.3+ or http://jruby.org/[JRuby] 9.1+
27
+ * https://rubygems.org/gems/asciidoctor/[Asciidoctor] 1.5.6+
28
+ * https://rubygems.org/gems/katex[katex (gem)] 0.4.3+
29
+ * JavaScript engine supported by https://github.com/rails/execjs#execjs[ExecJS]
30
+ * https://rubygems.org/gems/thread_safe/[thread_safe] (not required, but recommended for Ruby MRI)
31
+
32
+
33
+ === Node.js
34
+
35
+ * https://nodejs.org/[Node.js] 8+
36
+ * https://www.npmjs.com/package/asciidoctor.js[asciidoctor.js] 1.5.6
37
+ * https://www.npmjs.com/package/katex[katex (module)] 0.9.0+
38
+
39
+
40
+ == Installation
41
+
42
+ === Ruby
43
+
44
+ Install {gem-name} from Rubygems:
45
+
46
+ [source, subs="+attributes"]
47
+ gem install {gem-name}
48
+
49
+ or to install the latest development version:
50
+
51
+ [source, subs="+attributes"]
52
+ gem install {gem-name} --pre
53
+
54
+
55
+ === Node.js
56
+
57
+ Install {gem-name} from npmjs.com:
58
+
59
+ [source, sh, subs="+attributes"]
60
+ npm install --save {gem-name}
61
+
62
+
63
+ == Usage
64
+
65
+ === CLI
66
+
67
+ If you invoke Asciidoctor from command-line, use option `-r` to load the extension:
68
+
69
+ [source, subs="+attributes"]
70
+ asciidoctor -r {gem-name} README.adoc
71
+
72
+
73
+ === Ruby
74
+
75
+ Just `require '{gem-name}'`.
76
+ However, if you don’t want the extension to be automatically registered in Asciidoctor, `require 'asciidoctor/katex/treeprocessor'` instead.
77
+
78
+ IMPORTANT: Bundler automatically _requires_ all the specified gems.
79
+ To prevent it, use +
80
+ `gem '{gem-name}', require: false`.
81
+
82
+
83
+ === Node.js
84
+
85
+ [source, js, subs="+attributes"]
86
+ ----
87
+ // Load asciidoctor.js and {gem-name}.
88
+ const asciidoctor = require('asciidoctor.js')()
89
+ const asciidoctorKatex = require('{gem-name}')
90
+
91
+ // See documentation at the end of this section.
92
+ const options = {
93
+ katexOptions: {
94
+ macros: {
95
+ "\\RR": "\\mathbb{R}",
96
+ },
97
+ },
98
+ }
99
+
100
+ // Configure the extension and register it into global registry.
101
+ asciidoctorKatex.register(asciidoctor.Extensions, options)
102
+
103
+ // Convert the content to HTML.
104
+ const content = `
105
+ :stem: latexmath
106
+
107
+ Do some math: stem:[E = mc^2]
108
+ `
109
+ const html = asciidoctor.convert(content)
110
+ console.log(html)
111
+ ----
112
+
113
+
114
+ You may also register the extension into a custom extensions registry:
115
+
116
+ [source, js]
117
+ const registry = asciidoctor.Extensions.create()
118
+ asciidoctorKatex.register(registry, options)
119
+
120
+ .*Options:*
121
+ katex::
122
+ The katex object to use for rendering.
123
+ Defaults to `require('katex')`.
124
+
125
+ requireStemAttr::
126
+ Whether to require `stem` attribute to be defined (Asciidoctor’s standard behaviour).
127
+ Set to `false` to process latexmath even when `stem` attribute is not defined.
128
+ Default is `true`. +
129
+ Note that the default stem type hard-coded by Asciidoctor is `asciimath` (not `latexmath`), so `[stem]` block and `++stem:[...]++` macro will not be rendered anyway (only `[latexmath]` block and `++latexmath:[...]++` macro).
130
+
131
+ katexOptions::
132
+ The default options for `katex.render()`.
133
+ Defaults to empty object.
134
+
135
+
136
+ == License
137
+
138
+ This project is licensed under http://opensource.org/licenses/MIT/[MIT License].
139
+ For the full text of the license, see the link:LICENSE[LICENSE] file.
@@ -0,0 +1,27 @@
1
+ require File.expand_path('../lib/asciidoctor/katex/version', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'asciidoctor-katex'
5
+ s.version = Asciidoctor::Katex::VERSION
6
+ s.author = 'Jakub Jirutka'
7
+ s.email = 'jakub@jirutka.cz'
8
+ s.homepage = 'https://github.com/jirutka/asciidoctor-katex/'
9
+ s.license = 'MIT'
10
+
11
+ s.summary = 'Asciidoctor extension that converts latexmath to HTML using KaTeX at build-time'
12
+
13
+ s.files = Dir['lib/**/*', '*.gemspec', 'LICENSE*', 'README*']
14
+
15
+ s.required_ruby_version = '>= 2.3'
16
+
17
+ s.add_runtime_dependency 'asciidoctor', '~> 1.5.6'
18
+ s.add_runtime_dependency 'katex', '~> 0.4.3'
19
+
20
+ s.add_development_dependency 'kramdown', '~> 1.17'
21
+ s.add_development_dependency 'rake', '~> 12.0'
22
+ s.add_development_dependency 'rspec', '~> 3.7'
23
+ s.add_development_dependency 'rspec-html-matchers', '~> 0.9.1'
24
+ s.add_development_dependency 'rubocop', '~> 0.51.0'
25
+ s.add_development_dependency 'simplecov', '~> 0.16'
26
+ s.add_development_dependency 'yard', '~> 0.9'
27
+ end
@@ -0,0 +1,2 @@
1
+ # frozen_string_literal: true
2
+ require 'asciidoctor/katex'
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ require 'asciidoctor' unless RUBY_PLATFORM == 'opal'
3
+ require 'asciidoctor/extensions' unless RUBY_PLATFORM == 'opal'
4
+ require 'asciidoctor/katex/version'
5
+ require 'asciidoctor/katex/treeprocessor'
6
+
7
+ unless RUBY_PLATFORM == 'opal'
8
+ Asciidoctor::Extensions.register do
9
+ treeprocessor Asciidoctor::Katex::Treeprocessor
10
+ end
11
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+ require 'asciidoctor/katex/version'
3
+ require 'katex' unless RUBY_PLATFORM == 'opal'
4
+
5
+ module Asciidoctor::Katex
6
+ # Adapter for KaTeX library supporting both Ruby and Opal environment.
7
+ class KatexAdapter
8
+
9
+ # @param default_options [Hash] the default options for the KaTeX renderer.
10
+ # @param katex_object the katex object to use under Opal (defaults to
11
+ # global variable `katex`).
12
+ def initialize(default_options = {}, katex_object = nil)
13
+ @default_options = default_options
14
+ @katex_object = katex_object || `katex` if RUBY_PLATFORM == 'opal'
15
+ end
16
+
17
+ # @return [Boolean] whether is KaTeX library installed and loaded.
18
+ # This is useful only under Opal.
19
+ def available?
20
+ if RUBY_PLATFORM == 'opal'
21
+ `#{@katex_object} != 'undefined' && #{@katex_object}.renderToString != undefined`
22
+ else
23
+ defined? ::Katex
24
+ end
25
+ end
26
+
27
+ # Renders the given math expression to HTML using KaTeX.
28
+ #
29
+ # @param math [String] the math (LaTeX) expression.
30
+ # @param options [Hash] options for `katex.renderToString`.
31
+ # Keys in under_score notation will be converted to camelCase.
32
+ # See <https://github.com/Khan/KaTeX#rendering-options>.
33
+ # @return [String] a rendered HTML fragment.
34
+ def render(math, options = {})
35
+ options = hash_camelize(options || {})
36
+
37
+ if RUBY_PLATFORM == 'opal'
38
+ `#{@katex_object}.renderToString(#{math}, #{options}.$$smap)`
39
+ else
40
+ ::Katex.render(math, options)
41
+ end
42
+ end
43
+
44
+ alias call render
45
+
46
+ # @return [String] version of the KaTeX library.
47
+ def version
48
+ if RUBY_PLATFORM == 'opal'
49
+ '0.9.0' # TODO: replace with `katex.version` after update to 0.10+.
50
+ else
51
+ ::Katex::KATEX_VERSION.sub(/^v/, '')
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ # @param hash [Hash] the hash to transform.
58
+ # @return [Hash] a copy of the *hash* with under_score keys transformed to camelCase.
59
+ def hash_camelize(hash)
60
+ hash.map { |k, v|
61
+ [k.to_s.gsub(/_\w/) { |s| s[1].upcase }.to_sym, v]
62
+ }.to_h
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+ require 'asciidoctor/katex/version'
3
+ require 'delegate'
4
+
5
+ module Asciidoctor::Katex
6
+ # The converter decorator that renders delimited math expressions
7
+ # in block and inline latexmath stem nodes after.
8
+ class StemConverterDecorator < ::SimpleDelegator
9
+
10
+ # @param converter [Asciidoctor::Converter] the decorated converter to
11
+ # delegate all method calls to.
12
+ # @param math_renderer [#call] callable that accepts a math expression
13
+ # [String] and options [Hash], and returns a rendered expression [String].
14
+ def initialize(converter, math_renderer)
15
+ super(converter)
16
+ @math_renderer = math_renderer
17
+ @block_re = regexp_from_delimiters(::Asciidoctor::BLOCK_MATH_DELIMITERS[:latexmath])
18
+ @inline_re = regexp_from_delimiters(::Asciidoctor::INLINE_MATH_DELIMITERS[:latexmath])
19
+ end
20
+
21
+ # @param node [Asciidoctor::AbstractNode] the node to convert.
22
+ # @param transform [String, nil] the conversion method to call.
23
+ # @param opts [Hash] options to pass to the converter.
24
+ # @return [String] output of the converter.
25
+ def convert(node, transform = nil, opts = {})
26
+ # Call the underlying converter.
27
+ output = __getobj__.convert(node, transform, opts)
28
+
29
+ if latexmath? node
30
+ render_latexmath(output, node)
31
+ else
32
+ output
33
+ end
34
+ end
35
+
36
+ protected
37
+
38
+ # @param output [String] the converted *node* with delimited math expression.
39
+ # @param node [Asciidoctor::AbstractNode] the AST node.
40
+ # @return [String] a copy of *output* with math expression rendered.
41
+ def render_latexmath(output, node)
42
+ @throw_on_error ||= node.document.attr('katex-throw-on-error', false)
43
+ isblock = node.block?
44
+
45
+ output.sub(isblock ? @block_re : @inline_re) do
46
+ @math_renderer.call(::Regexp.last_match[1].strip,
47
+ display_mode: isblock,
48
+ throw_on_error: @throw_on_error)
49
+ end
50
+ end
51
+
52
+ # @param node [Asciidoctor::AbstractNode] the AST node to test.
53
+ # @return [Boolean] `true` if the given *node* is a block or inline latexmath,
54
+ # `false` otherwise.
55
+ def latexmath?(node)
56
+ case node.node_name
57
+ when 'stem'
58
+ return true if node.style == 'latexmath'
59
+ when 'inline_quoted'
60
+ return true if node.type == :latexmath
61
+ end
62
+ false
63
+ end
64
+
65
+ private
66
+
67
+ # @param delimiters [Array<String>] tuple with open and close delimiter.
68
+ # @return [Regexp]
69
+ def regexp_from_delimiters(delimiters)
70
+ open, close = delimiters.map { |s| ::Regexp.escape(s) }
71
+ /#{open}(.+)#{close}/m
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+ require 'asciidoctor' unless RUBY_PLATFORM == 'opal'
3
+ require 'asciidoctor/extensions' unless RUBY_PLATFORM == 'opal'
4
+ require 'asciidoctor/katex/version'
5
+ require 'asciidoctor/katex/katex_adapter'
6
+ require 'asciidoctor/katex/stem_converter_decorator'
7
+
8
+ module Asciidoctor::Katex
9
+ # Asciidoctor processor that renders delimited latexmath expressions using
10
+ # the KaTeX library.
11
+ class Treeprocessor < ::Asciidoctor::Extensions::Treeprocessor
12
+
13
+ # @param katex_renderer [#call] callable that accepts a math expression
14
+ # [String] and options [Hash], and returns a rendered expression [String].
15
+ # Default is an instance of KatexAdapter.
16
+ # @param require_stem_attr [Boolean] `true` to skip when `stem` attribute
17
+ # is not declared, `false` to process anyway.
18
+ def initialize(katex_renderer: KatexAdapter.new,
19
+ require_stem_attr: true,
20
+ **)
21
+ super
22
+ @katex_renderer = katex_renderer
23
+ @require_stem_attr = require_stem_attr
24
+ end
25
+
26
+ # @param document [Asciidoctor::Document] the document to process.
27
+ def process(document)
28
+ return if skip? document
29
+
30
+ converter = document.instance_variable_get(:@converter)
31
+ decorator = StemConverterDecorator.new(converter, @katex_renderer)
32
+ document.instance_variable_set(:@converter, decorator)
33
+ nil
34
+ end
35
+
36
+ protected
37
+
38
+ # @param document [Asciidoctor::Document] the document to process.
39
+ # @return [Boolean] whether to skip processing of the *document*.
40
+ def skip?(document)
41
+ @require_stem_attr && !document.attr?('stem')
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor
4
+ module Katex
5
+ # Version of the asciidoctor-katex gem.
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: asciidoctor-katex
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jakub Jirutka
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-08-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: asciidoctor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.5.6
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.5.6
27
+ - !ruby/object:Gem::Dependency
28
+ name: katex
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.4.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: kramdown
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.17'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.17'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '12.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '12.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec-html-matchers
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.9.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.9.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.51.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.51.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.16'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.16'
125
+ - !ruby/object:Gem::Dependency
126
+ name: yard
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.9'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.9'
139
+ description:
140
+ email: jakub@jirutka.cz
141
+ executables: []
142
+ extensions: []
143
+ extra_rdoc_files: []
144
+ files:
145
+ - LICENSE
146
+ - README.adoc
147
+ - asciidoctor-katex.gemspec
148
+ - lib/asciidoctor-katex.rb
149
+ - lib/asciidoctor/katex.rb
150
+ - lib/asciidoctor/katex/katex_adapter.rb
151
+ - lib/asciidoctor/katex/stem_converter_decorator.rb
152
+ - lib/asciidoctor/katex/treeprocessor.rb
153
+ - lib/asciidoctor/katex/version.rb
154
+ homepage: https://github.com/jirutka/asciidoctor-katex/
155
+ licenses:
156
+ - MIT
157
+ metadata: {}
158
+ post_install_message:
159
+ rdoc_options: []
160
+ require_paths:
161
+ - lib
162
+ required_ruby_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '2.3'
167
+ required_rubygems_version: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ requirements: []
173
+ rubyforge_project:
174
+ rubygems_version: 2.7.7
175
+ signing_key:
176
+ specification_version: 4
177
+ summary: Asciidoctor extension that converts latexmath to HTML using KaTeX at build-time
178
+ test_files: []