@asciidoctor/core 3.0.3 → 4.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -10
- package/build/browser/index.js +24154 -0
- package/build/node/index.cjs +24735 -0
- package/{dist/css/asciidoctor.css → data/asciidoctor-default.css} +54 -53
- package/package.json +53 -100
- package/src/abstract_block.js +849 -0
- package/src/abstract_node.js +954 -0
- package/src/attribute_entry.js +12 -0
- package/src/attribute_list.js +380 -0
- package/src/block.js +168 -0
- package/src/browser/asset.js +22 -0
- package/src/browser/reader.js +138 -0
- package/src/browser.js +121 -0
- package/src/callouts.js +85 -0
- package/src/compliance.js +54 -0
- package/src/constants.js +665 -0
- package/src/convert.js +370 -0
- package/src/converter/composite.js +83 -0
- package/src/converter/docbook5.js +1031 -0
- package/src/converter/html5.js +1899 -0
- package/src/converter/manpage.js +935 -0
- package/src/converter/template.js +459 -0
- package/src/converter.js +478 -0
- package/src/data/stylesheet-data.js +2 -0
- package/src/document.js +2134 -0
- package/src/extensions.js +1952 -0
- package/src/footnote.js +28 -0
- package/src/helpers.js +355 -0
- package/src/index.js +138 -0
- package/src/inline.js +158 -0
- package/src/list.js +240 -0
- package/src/load.js +276 -0
- package/src/logging.js +526 -0
- package/src/parser.js +3661 -0
- package/src/path_resolver.js +472 -0
- package/src/reader.js +1755 -0
- package/src/rx.js +829 -0
- package/src/section.js +354 -0
- package/src/stylesheets.js +30 -0
- package/src/substitutors.js +2241 -0
- package/src/syntaxHighlighter/highlightjs.js +90 -0
- package/src/syntaxHighlighter/html_pipeline.js +33 -0
- package/src/syntax_highlighter.js +304 -0
- package/src/table.js +952 -0
- package/src/timings.js +78 -0
- package/types/abstract_block.d.ts +343 -0
- package/types/abstract_node.d.ts +471 -0
- package/types/attribute_entry.d.ts +7 -0
- package/types/attribute_list.d.ts +52 -0
- package/types/block.d.ts +55 -0
- package/types/browser/asset.d.ts +7 -0
- package/types/browser/reader.d.ts +29 -0
- package/types/callouts.d.ts +36 -0
- package/types/compliance.d.ts +23 -0
- package/types/constants.d.ts +268 -0
- package/types/convert.d.ts +34 -0
- package/types/converter/composite.d.ts +20 -0
- package/types/converter/docbook5.d.ts +41 -0
- package/types/converter/html5.d.ts +51 -0
- package/types/converter/manpage.d.ts +59 -0
- package/types/converter/template.d.ts +83 -0
- package/types/converter.d.ts +150 -0
- package/types/data/stylesheet-data.d.ts +2 -0
- package/types/document.d.ts +495 -0
- package/types/extensions.d.ts +876 -0
- package/types/footnote.d.ts +18 -0
- package/types/helpers.d.ts +146 -0
- package/types/index.d.ts +73 -3731
- package/types/inline.d.ts +69 -0
- package/types/list.d.ts +114 -0
- package/types/load.d.ts +39 -0
- package/types/logging.d.ts +187 -0
- package/types/parser.d.ts +114 -0
- package/types/path_resolver.d.ts +103 -0
- package/types/reader.d.ts +184 -0
- package/types/rx.d.ts +513 -0
- package/types/section.d.ts +122 -0
- package/types/stylesheets.d.ts +10 -0
- package/types/substitutors.d.ts +208 -0
- package/types/syntaxHighlighter/highlightjs.d.ts +33 -0
- package/types/syntaxHighlighter/html_pipeline.d.ts +16 -0
- package/types/syntax_highlighter.d.ts +167 -0
- package/types/table.d.ts +231 -0
- package/types/timings.d.ts +25 -0
- package/types/tsconfig.json +9 -0
- package/LICENSE +0 -21
- package/dist/browser/asciidoctor.js +0 -47654
- package/dist/browser/asciidoctor.min.js +0 -1452
- package/dist/graalvm/asciidoctor.js +0 -47402
- package/dist/node/asciidoctor.cjs +0 -21567
- package/dist/node/asciidoctor.js +0 -23037
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// ESM conversion of syntax_highlighter/highlightjs.rb
|
|
2
|
+
//
|
|
3
|
+
// Ruby-to-JavaScript notes:
|
|
4
|
+
// - Ruby class SyntaxHighlighter::HighlightJsAdapter → HighlightJsAdapter extends SyntaxHighlighterBase.
|
|
5
|
+
// - register_for 'highlightjs', 'highlight.js' → handled by the parent SyntaxHighlighter factory.
|
|
6
|
+
// - HIGHLIGHT_JS_VERSION constant imported from constants.js.
|
|
7
|
+
// - Ruby doc.getAttribute(name, default) → doc.getAttribute(name) with fallback using ?? operator.
|
|
8
|
+
// - Ruby doc.attr? 'name' → doc.hasAttribute('name').
|
|
9
|
+
// - Ruby string interpolation / multiline heredocs → template literals.
|
|
10
|
+
// - Ruby :head / :footer symbols → plain strings 'head' / 'footer'.
|
|
11
|
+
|
|
12
|
+
import { SyntaxHighlighterBase } from '../syntax_highlighter.js'
|
|
13
|
+
import { HIGHLIGHT_JS_VERSION } from '../constants.js'
|
|
14
|
+
|
|
15
|
+
export class HighlightJsAdapter extends SyntaxHighlighterBase {
|
|
16
|
+
constructor(...args) {
|
|
17
|
+
super(...args)
|
|
18
|
+
this.name = 'highlightjs'
|
|
19
|
+
this._preClass = 'highlightjs'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Wrap the source block in `<pre><code>` with highlight.js CSS classes.
|
|
24
|
+
*
|
|
25
|
+
* Adds `language-<lang>` and `hljs` to the `<code>` class attribute, and strips
|
|
26
|
+
* the `highlight` class from `<pre>` when the `nohighlight-option` attribute is set.
|
|
27
|
+
* @param {object} node - the source Block being processed
|
|
28
|
+
* @param {string|null} lang - the source language string, or falsy if none
|
|
29
|
+
* @param {object} opts - options passed to the base format()
|
|
30
|
+
* @returns {Promise<string>}
|
|
31
|
+
*/
|
|
32
|
+
async format(node, lang, opts) {
|
|
33
|
+
const transform = (pre, code) => {
|
|
34
|
+
if (node.hasAttribute('nohighlight-option')) {
|
|
35
|
+
pre.class = pre.class.replace(' highlight', '')
|
|
36
|
+
}
|
|
37
|
+
code.class = `language-${lang || 'none'} hljs`
|
|
38
|
+
}
|
|
39
|
+
return super.format(node, lang, { ...opts, transform })
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Always returns true — highlight.js injects markup into the document.
|
|
44
|
+
* @param {string} location - 'head' or 'footer'
|
|
45
|
+
* @returns {true}
|
|
46
|
+
*/
|
|
47
|
+
hasDocinfo(location) {
|
|
48
|
+
return true
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Returns the CSS `<link>` tag (head) or the `<script>` tags (footer).
|
|
53
|
+
* @param {string} location - 'head' or 'footer'
|
|
54
|
+
* @param {object} doc - the Document being converted
|
|
55
|
+
* @param {{ cdn_base_url: string, self_closing_tag_slash: string }} opts
|
|
56
|
+
* @returns {string}
|
|
57
|
+
*/
|
|
58
|
+
docinfo(location, doc, opts) {
|
|
59
|
+
const baseUrl =
|
|
60
|
+
doc.getAttribute('highlightjsdir') ??
|
|
61
|
+
`${opts.cdn_base_url}/highlight.js/${HIGHLIGHT_JS_VERSION}`
|
|
62
|
+
|
|
63
|
+
if (location === 'head') {
|
|
64
|
+
const theme = doc.getAttribute('highlightjs-theme') ?? 'github'
|
|
65
|
+
return `<link rel="stylesheet" href="${baseUrl}/styles/${theme}.min.css"${opts.self_closing_tag_slash ?? ''}>`
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// footer
|
|
69
|
+
const langScripts = doc.getAttribute('highlightjs-languages')
|
|
70
|
+
? doc
|
|
71
|
+
.getAttribute('highlightjs-languages')
|
|
72
|
+
.split(',')
|
|
73
|
+
.map(
|
|
74
|
+
(lang) =>
|
|
75
|
+
`<script src="${baseUrl}/languages/${lang.trimStart()}.min.js"></script>\n`
|
|
76
|
+
)
|
|
77
|
+
.join('')
|
|
78
|
+
: ''
|
|
79
|
+
|
|
80
|
+
return `<script src="${baseUrl}/highlight.min.js"></script>
|
|
81
|
+
${langScripts}<script>
|
|
82
|
+
if (!hljs.initHighlighting.called) {
|
|
83
|
+
hljs.initHighlighting.called = true
|
|
84
|
+
;[].slice.call(document.querySelectorAll('pre.highlight > code[data-lang]')).forEach(function (el) { hljs.highlightBlock(el) })
|
|
85
|
+
}
|
|
86
|
+
</script>`
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default HighlightJsAdapter
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// ESM conversion of syntax_highlighter/html_pipeline.rb
|
|
2
|
+
//
|
|
3
|
+
// Ruby-to-JavaScript notes:
|
|
4
|
+
// - Ruby class SyntaxHighlighter::HtmlPipelineAdapter → HtmlPipelineAdapter extends SyntaxHighlighterBase.
|
|
5
|
+
// - register_for 'html-pipeline' → registration delegated to load.js for consistency.
|
|
6
|
+
// - format() overrides the base class to emit <pre lang="..."><code>…</code></pre> without
|
|
7
|
+
// highlight CSS classes — the html-pipeline gem processes the markup downstream.
|
|
8
|
+
// - Ruby string interpolation → template literals.
|
|
9
|
+
|
|
10
|
+
import { SyntaxHighlighterBase } from '../syntax_highlighter.js'
|
|
11
|
+
|
|
12
|
+
export class HtmlPipelineAdapter extends SyntaxHighlighterBase {
|
|
13
|
+
constructor(...args) {
|
|
14
|
+
super(...args)
|
|
15
|
+
this.name = 'html-pipeline'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Wrap the source block in `<pre><code>` without highlight classes.
|
|
20
|
+
*
|
|
21
|
+
* The html-pipeline gem processes the markup downstream, so only a bare
|
|
22
|
+
* `<pre lang="<lang>"><code>` wrapper is emitted (no CSS classes, no data-lang).
|
|
23
|
+
* @param {object} node - the source Block being processed
|
|
24
|
+
* @param {string|null} lang - the source language string, or falsy if none
|
|
25
|
+
* @param {object} opts - options (unused by this adapter)
|
|
26
|
+
* @returns {Promise<string>} the wrapped source string
|
|
27
|
+
*/
|
|
28
|
+
async format(node, lang, opts) {
|
|
29
|
+
return `<pre${lang ? ` lang="${lang}"` : ''}><code>${await node.content()}</code></pre>`
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default HtmlPipelineAdapter
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/** @import { Block } from './block.js' */
|
|
2
|
+
|
|
3
|
+
// ESM conversion of syntax_highlighter.rb
|
|
4
|
+
//
|
|
5
|
+
// Ruby-to-JavaScript notes:
|
|
6
|
+
// - Ruby module SyntaxHighlighter used as mixin → SyntaxHighlighterBase class.
|
|
7
|
+
// - Ruby module Factory → mixed into CustomFactory and DefaultFactory classes.
|
|
8
|
+
// - Ruby @@registry class var → module-level _defaultRegistry Map for DefaultFactory.
|
|
9
|
+
// - Ruby Mutex thread-safety → not needed in single-threaded JS.
|
|
10
|
+
// - Ruby lazy require (PROVIDED map) → async dynamic import() in DefaultFactory.
|
|
11
|
+
// - Ruby DefaultFactoryProxy (overrides #for with custom-first lookup) → DefaultFactory
|
|
12
|
+
// already handles this with _registry (custom) checked before _defaultRegistry (built-in).
|
|
13
|
+
// - Ruby module Config / register_for static helper → static registerFor() on each subclass.
|
|
14
|
+
// - Ruby :symbol keys → plain strings throughout.
|
|
15
|
+
// - highlightjs is always registered; coderay/pygments/rouge are Ruby-only (not ported).
|
|
16
|
+
|
|
17
|
+
// ── SyntaxHighlighterBase ─────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Base class for syntax highlighter adapters.
|
|
21
|
+
*
|
|
22
|
+
* Subclasses should override the methods they need. Two usage patterns:
|
|
23
|
+
* 1. Server-side highlighting: override `handlesHighlighting()` → true and `highlight()`.
|
|
24
|
+
* 2. Client-side highlighting: override `hasDocinfo()` → true and `docinfo()`.
|
|
25
|
+
*
|
|
26
|
+
* Both patterns may also override `format()`.
|
|
27
|
+
*/
|
|
28
|
+
export class SyntaxHighlighterBase {
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} name - the name identifying this adapter
|
|
31
|
+
* @param {string} [backend='html5'] - the backend name
|
|
32
|
+
* @param {Object} [opts={}] - options
|
|
33
|
+
*/
|
|
34
|
+
constructor(name, backend = 'html5', opts = {}) {
|
|
35
|
+
this.name = name
|
|
36
|
+
this._preClass = name
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Indicates whether this highlighter has docinfo markup to insert at the specified location.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} location - the location slot ('head' or 'footer')
|
|
43
|
+
* @returns {boolean} false by default; subclasses return true to enable {@link docinfo}
|
|
44
|
+
*/
|
|
45
|
+
hasDocinfo(location) {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Generates docinfo markup to insert at the specified location in the output document.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} location - the location slot ('head' or 'footer')
|
|
53
|
+
* @param {Document} doc - the Document in which this highlighter is used
|
|
54
|
+
* @param {Object} opts - options
|
|
55
|
+
* @param {boolean} [opts.linkcss] - link stylesheet instead of embedding
|
|
56
|
+
* @param {string} [opts.cdn_base_url] - base URL for CDN assets
|
|
57
|
+
* @param {string} [opts.self_closing_tag_slash] - '/' for self-closing tags
|
|
58
|
+
* @returns {string} the markup to insert
|
|
59
|
+
*/
|
|
60
|
+
docinfo(location, doc, opts) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`${this.constructor.name} must implement docinfo() since hasDocinfo() returns true`
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Indicates whether highlighting is handled server-side by this highlighter.
|
|
68
|
+
*
|
|
69
|
+
* @returns {boolean} false by default; subclasses return true to enable {@link highlight}
|
|
70
|
+
*/
|
|
71
|
+
handlesHighlighting() {
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Highlights the specified source when this source block is being converted.
|
|
77
|
+
*
|
|
78
|
+
* If the source contains callout marks, the caller assumes the source remains on the same
|
|
79
|
+
* lines and no closing tags are added to the end of each line. If the source gets shifted
|
|
80
|
+
* by one or more lines, return a tuple of the highlighted source and the line offset.
|
|
81
|
+
*
|
|
82
|
+
* @param {Block} node - the source Block to highlight
|
|
83
|
+
* @param {string} source - the raw source text
|
|
84
|
+
* @param {string} lang - the source language (e.g. 'ruby')
|
|
85
|
+
* @param {Object} opts - options
|
|
86
|
+
* @param {Object} [opts.callouts] - callouts indexed by line number
|
|
87
|
+
* @param {string} [opts.css_mode] - CSS mode ('class' or 'inline')
|
|
88
|
+
* @param {number[]} [opts.highlight_lines] - 1-based line numbers to emphasize
|
|
89
|
+
* @param {string} [opts.number_lines] - 'table' or 'inline' if lines should be numbered
|
|
90
|
+
* @param {number} [opts.start_line_number] - starting line number (default: 1)
|
|
91
|
+
* @param {string} [opts.style] - theme name
|
|
92
|
+
* @returns {string|[string, number]} the highlighted source, or a tuple with a line offset
|
|
93
|
+
*/
|
|
94
|
+
highlight(node, source, lang, opts) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`${this.constructor.name} must implement highlight() since handlesHighlighting() returns true`
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Formats the highlighted source for inclusion in an HTML document.
|
|
102
|
+
*
|
|
103
|
+
* @param {Block} node - the source Block being processed
|
|
104
|
+
* @param {string} lang - the source language (e.g. 'ruby')
|
|
105
|
+
* @param {Object} opts - options
|
|
106
|
+
* @param {boolean} [opts.nowrap] - disable line wrapping
|
|
107
|
+
* @param {Function} [opts.transform] - called with (pre, code) attribute objects before building tags
|
|
108
|
+
* @returns {Promise<string>|string} the highlighted source wrapped in <pre><code> tags.
|
|
109
|
+
* Subclasses may return a plain `string` — the caller always `await`s the result.
|
|
110
|
+
*/
|
|
111
|
+
format(node, lang, opts) {
|
|
112
|
+
const classAttrVal = opts.nowrap
|
|
113
|
+
? `${this._preClass} highlight nowrap`
|
|
114
|
+
: `${this._preClass} highlight`
|
|
115
|
+
return node.content().then((content) => {
|
|
116
|
+
const transform = opts.transform
|
|
117
|
+
if (transform) {
|
|
118
|
+
const pre = { class: classAttrVal }
|
|
119
|
+
const code = lang ? { 'data-lang': lang } : {}
|
|
120
|
+
transform(pre, code)
|
|
121
|
+
// NOTE keep data-lang as the last attribute on <code> to match Ruby 1.5.x behaviour
|
|
122
|
+
const dataLang = code['data-lang']
|
|
123
|
+
delete code['data-lang']
|
|
124
|
+
if (dataLang) code['data-lang'] = dataLang
|
|
125
|
+
const preAttrs = Object.entries(pre)
|
|
126
|
+
.map(([k, v]) => ` ${k}="${v}"`)
|
|
127
|
+
.join('')
|
|
128
|
+
const codeAttrs = Object.entries(code)
|
|
129
|
+
.map(([k, v]) => ` ${k}="${v}"`)
|
|
130
|
+
.join('')
|
|
131
|
+
return `<pre${preAttrs}><code${codeAttrs}>${content}</code></pre>`
|
|
132
|
+
}
|
|
133
|
+
return `<pre class="${classAttrVal}"><code${lang ? ` data-lang="${lang}"` : ''}>${content}</code></pre>`
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Indicates whether this highlighter wants to write a stylesheet to disk.
|
|
139
|
+
*
|
|
140
|
+
* @param {Document} doc - the Document in which this highlighter is being used
|
|
141
|
+
* @returns {boolean} false by default; subclasses return true to enable {@link writeStylesheetToDisk}
|
|
142
|
+
*/
|
|
143
|
+
writeStylesheet(doc) {
|
|
144
|
+
return false
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Writes the stylesheet to disk.
|
|
149
|
+
*
|
|
150
|
+
* @param {Document} doc - the Document in which this highlighter is used
|
|
151
|
+
* @param {string} toDir - the absolute path of the output directory
|
|
152
|
+
*/
|
|
153
|
+
writeStylesheetToDisk(doc, toDir) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`${this.constructor.name} must implement writeStylesheetToDisk() since writeStylesheet() returns true`
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── CustomFactory ─────────────────────────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* A syntax highlighter factory backed by a caller-supplied registry.
|
|
164
|
+
*/
|
|
165
|
+
export class CustomFactory {
|
|
166
|
+
/**
|
|
167
|
+
* @param {Object|null} [seedRegistry=null] - initial registry entries
|
|
168
|
+
*/
|
|
169
|
+
constructor(seedRegistry = null) {
|
|
170
|
+
this._registry = seedRegistry ? { ...seedRegistry } : {}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Associates a syntax highlighter class or instance with one or more names.
|
|
175
|
+
*
|
|
176
|
+
* @param {Function|SyntaxHighlighterBase} syntaxHighlighter - the class or instance to register
|
|
177
|
+
* @param {...string} names - one or more names to associate
|
|
178
|
+
*/
|
|
179
|
+
register(syntaxHighlighter, ...names) {
|
|
180
|
+
for (const name of names) {
|
|
181
|
+
this._registry[name] = syntaxHighlighter
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Retrieves the syntax highlighter class or instance registered for the given name.
|
|
187
|
+
*
|
|
188
|
+
* @param {string} name - the name to look up
|
|
189
|
+
* @returns {Function|SyntaxHighlighterBase|null} the registered class or instance, or null
|
|
190
|
+
*/
|
|
191
|
+
for(name) {
|
|
192
|
+
return this._registry[name] ?? null
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Resolves a name to a syntax highlighter instance.
|
|
197
|
+
*
|
|
198
|
+
* @param {string} name - the name of the syntax highlighter
|
|
199
|
+
* @param {string} [backend='html5'] - the backend name
|
|
200
|
+
* @param {Object} [opts={}] - options passed to the constructor
|
|
201
|
+
* @returns {SyntaxHighlighterBase|null} a highlighter instance, or null if not registered
|
|
202
|
+
*/
|
|
203
|
+
create(name, backend = 'html5', opts = {}) {
|
|
204
|
+
let syntaxHl = this.for(name)
|
|
205
|
+
if (!syntaxHl) return null
|
|
206
|
+
if (typeof syntaxHl === 'function' && syntaxHl.prototype) {
|
|
207
|
+
syntaxHl = new syntaxHl(name, backend, opts)
|
|
208
|
+
}
|
|
209
|
+
if (!syntaxHl.name) {
|
|
210
|
+
throw new Error(
|
|
211
|
+
`${syntaxHl.constructor.name} must specify a value for 'name'`
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
return syntaxHl
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ── DefaultFactoryProxy ───────────────────────────────────────────────────────
|
|
219
|
+
|
|
220
|
+
// Wraps a `syntax_highlighters` hash (per-load overrides) and falls back to a
|
|
221
|
+
// delegate factory (typically the global SyntaxHighlighter singleton) for names
|
|
222
|
+
// not present in the overrides. Setting a name to null disables that highlighter.
|
|
223
|
+
|
|
224
|
+
export class DefaultFactoryProxy extends CustomFactory {
|
|
225
|
+
/**
|
|
226
|
+
* @param {Object} overrides - map of name → class/instance/null
|
|
227
|
+
* @param {CustomFactory} fallback - factory to delegate to when name is not overridden
|
|
228
|
+
*/
|
|
229
|
+
constructor(overrides, fallback) {
|
|
230
|
+
super(overrides)
|
|
231
|
+
this._fallback = fallback
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
for(name) {
|
|
235
|
+
// Use hasOwnProperty so that null (disabled) is returned as-is
|
|
236
|
+
if (Object.hasOwn(this._registry, name)) {
|
|
237
|
+
return this._registry[name]
|
|
238
|
+
}
|
|
239
|
+
return this._fallback.for(name)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ── DefaultFactory ────────────────────────────────────────────────────────────
|
|
244
|
+
|
|
245
|
+
// Global registry that distinguishes built-in adapters (registered by the
|
|
246
|
+
// adapters themselves via self-registration) from custom adapters (registered
|
|
247
|
+
// by user code). unregisterAll() clears only the custom layer so built-ins
|
|
248
|
+
// remain available after a reset, mirroring Ruby's DefaultFactory behaviour.
|
|
249
|
+
|
|
250
|
+
export class DefaultFactory extends CustomFactory {
|
|
251
|
+
constructor() {
|
|
252
|
+
super()
|
|
253
|
+
// _registry (inherited) → custom registrations
|
|
254
|
+
// _defaultRegistry → built-in registrations (populated by adapters)
|
|
255
|
+
this._defaultRegistry = {}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Register into the built-in layer (called by built-in adapters).
|
|
259
|
+
register(syntaxHighlighter, ...names) {
|
|
260
|
+
for (const name of names) {
|
|
261
|
+
this._defaultRegistry[name] = syntaxHighlighter
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Custom registrations shadow built-ins.
|
|
266
|
+
for(name) {
|
|
267
|
+
return this._registry[name] ?? this._defaultRegistry[name] ?? null
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Retrieves the syntax highlighter class or instance registered for the given name.
|
|
272
|
+
*
|
|
273
|
+
* @param {string} name - the name of the syntax highlighter to retrieve
|
|
274
|
+
* @returns {Function|SyntaxHighlighterBase|undefined} the registered class or instance, or undefined
|
|
275
|
+
*/
|
|
276
|
+
get(name) {
|
|
277
|
+
return this.for(name) ?? undefined
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
create(name, backend = 'html5', opts = {}) {
|
|
281
|
+
let syntaxHl = this.for(name)
|
|
282
|
+
if (!syntaxHl) return null
|
|
283
|
+
if (typeof syntaxHl === 'function' && syntaxHl.prototype) {
|
|
284
|
+
syntaxHl = new syntaxHl(name, backend, opts)
|
|
285
|
+
}
|
|
286
|
+
if (!syntaxHl.name) {
|
|
287
|
+
throw new Error(
|
|
288
|
+
`${syntaxHl.constructor.name} must specify a value for 'name'`
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
return syntaxHl
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Clears all custom (user) registrations; built-in adapters are preserved.
|
|
296
|
+
*/
|
|
297
|
+
unregisterAll() {
|
|
298
|
+
this._registry = {}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ── The global SyntaxHighlighter registry ─────────────────────────────────────
|
|
303
|
+
|
|
304
|
+
export const SyntaxHighlighter = new DefaultFactory()
|