@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
package/src/footnote.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export class Footnote {
|
|
2
|
+
constructor(index, id, text) {
|
|
3
|
+
this.index = index
|
|
4
|
+
this.id = id ?? null
|
|
5
|
+
this.text = text
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @returns {number} the index of this footnote.
|
|
10
|
+
*/
|
|
11
|
+
getIndex() {
|
|
12
|
+
return this.index
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @returns {string|null} the id of this footnote, or null if not set.
|
|
17
|
+
*/
|
|
18
|
+
getId() {
|
|
19
|
+
return this.id
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @returns {string} the text of this footnote.
|
|
24
|
+
*/
|
|
25
|
+
getText() {
|
|
26
|
+
return this.text
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/helpers.js
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
// ESM conversion of helpers.rb
|
|
2
|
+
// Internal helper functions used by the Asciidoctor parser.
|
|
3
|
+
//
|
|
4
|
+
// Ruby-to-JavaScript notes:
|
|
5
|
+
// - require_library / require_open_uri have no JS equivalent and are omitted.
|
|
6
|
+
// - resolve_class / class_for_name are Ruby-specific and are omitted.
|
|
7
|
+
// - BOM detection uses the Unicode BOM codepoint U+FEFF instead of raw bytes,
|
|
8
|
+
// since JS strings are always UTF-16 and never carry an encoding tag.
|
|
9
|
+
// - File.basename / File.extname are reimplemented without the Node `path` module
|
|
10
|
+
// so this module works in browser (Opal) and Node environments alike.
|
|
11
|
+
// - mkdir_p delegates to Node's fs.mkdirSync with { recursive: true }.
|
|
12
|
+
// - String#succ (nextval) is implemented for the ASCII alphanumeric subset
|
|
13
|
+
// used by Asciidoctor list-numbering sequences.
|
|
14
|
+
|
|
15
|
+
import { UriSniffRx } from './rx.js'
|
|
16
|
+
|
|
17
|
+
// ── BOM ──────────────────────────────────────────────────────────────────────
|
|
18
|
+
// Unicode byte-order mark (U+FEFF). In a JS string (already decoded to UTF-16)
|
|
19
|
+
// this is the single character that corresponds to all three BOM byte patterns:
|
|
20
|
+
// UTF-8 BOM 0xEF 0xBB 0xBF → U+FEFF
|
|
21
|
+
// UTF-16 LE 0xFF 0xFE → U+FEFF
|
|
22
|
+
// UTF-16 BE 0xFE 0xFF → U+FEFF
|
|
23
|
+
const BOM = ''
|
|
24
|
+
|
|
25
|
+
/** Trim trailing ASCII whitespace only (not Unicode line separators U+2028/U+2029). */
|
|
26
|
+
const rstrip = (line) => line.replace(/[ \t\r\n\f\v]+$/, '')
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Prepare the source data Array for parsing.
|
|
30
|
+
*
|
|
31
|
+
* Strips a leading BOM from the first element if present, then trims trailing
|
|
32
|
+
* whitespace (trimEnd = true) or only the trailing newline (trimEnd = false)
|
|
33
|
+
* from every line.
|
|
34
|
+
*
|
|
35
|
+
* @param {string[]} data - the source data Array to prepare (no null/undefined entries allowed)
|
|
36
|
+
* @param {boolean} [trimEnd=true] - whether to strip all trailing whitespace (true) or only \n (false)
|
|
37
|
+
* @returns {string[]} Array of prepared lines
|
|
38
|
+
*/
|
|
39
|
+
export function prepareSourceArray(data, trimEnd = true) {
|
|
40
|
+
if (!data.length) return []
|
|
41
|
+
if (data[0].startsWith(BOM)) data[0] = data[0].slice(1)
|
|
42
|
+
// Strip trailing \r to normalize Windows CRLF line endings (lines were split on \n, leaving \r).
|
|
43
|
+
return trimEnd
|
|
44
|
+
? data.map(rstrip)
|
|
45
|
+
: data.map((line) => line.replace(/\r?\n$/, '').replace(/\r$/, ''))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Prepare the source data String for parsing.
|
|
50
|
+
*
|
|
51
|
+
* Strips a leading BOM if present, splits into an array, and trims trailing
|
|
52
|
+
* whitespace (trimEnd = true) or only the trailing newline (trimEnd = false)
|
|
53
|
+
* from every line.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} data - the source data String to prepare
|
|
56
|
+
* @param {boolean} [trimEnd=true] - whether to strip all trailing whitespace (true) or only \n (false)
|
|
57
|
+
* @returns {string[]} Array of prepared lines
|
|
58
|
+
*/
|
|
59
|
+
export function prepareSourceString(data, trimEnd = true) {
|
|
60
|
+
if (!data) return []
|
|
61
|
+
if (data.startsWith(BOM)) data = data.slice(1)
|
|
62
|
+
// Normalize Windows CRLF to LF so that split('\n') does not leave trailing \r on each line.
|
|
63
|
+
if (data.includes('\r'))
|
|
64
|
+
data = data.replace(/\r\n/g, '\n').replace(/\r/g, '\n')
|
|
65
|
+
// Ruby's each_line does not produce an empty trailing element when the string
|
|
66
|
+
// ends with \n, but JS split('\n') does. Remove the trailing empty element
|
|
67
|
+
// to match Ruby behaviour.
|
|
68
|
+
if (data.endsWith('\n')) data = data.slice(0, -1)
|
|
69
|
+
const lines = data.split('\n')
|
|
70
|
+
return trimEnd ? lines.map(rstrip) : lines
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Efficiently check whether the specified String resembles a URI.
|
|
75
|
+
*
|
|
76
|
+
* Uses UriSniffRx to check whether the String begins with a URI prefix (e.g.
|
|
77
|
+
* http://). No validation of the URI is performed.
|
|
78
|
+
*
|
|
79
|
+
* @param {string} str - the String to check
|
|
80
|
+
* @returns {boolean} true if the String resembles a URI, false otherwise
|
|
81
|
+
*/
|
|
82
|
+
export function isUriish(str) {
|
|
83
|
+
return str.includes(':') && UriSniffRx.test(str)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Encode a URI component String for safe inclusion in a URI.
|
|
88
|
+
*
|
|
89
|
+
* Encodes all characters that are not unreserved per RFC-3986. Specifically,
|
|
90
|
+
* encodeURIComponent leaves !, ', (, ), and * unencoded; this function encodes
|
|
91
|
+
* those as well so the result matches CGI.escapeURIComponent (Ruby ≥ 3.2) /
|
|
92
|
+
* CGI.escape + gsub('+', '%20').
|
|
93
|
+
*
|
|
94
|
+
* @param {string} str - the URI component String to encode
|
|
95
|
+
* @returns {string} the encoded String
|
|
96
|
+
*/
|
|
97
|
+
export function encodeUriComponent(str) {
|
|
98
|
+
return encodeURIComponent(str).replace(
|
|
99
|
+
/[!'()*]/g,
|
|
100
|
+
(m) => `%${m.charCodeAt(0).toString(16)}`
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Replace spaces with %20 in a URI path.
|
|
106
|
+
*
|
|
107
|
+
* @param {string} str - the String to encode
|
|
108
|
+
* @returns {string} the String with all spaces replaced with %20
|
|
109
|
+
*/
|
|
110
|
+
export function encodeSpacesInUri(str) {
|
|
111
|
+
return str.includes(' ') ? str.replaceAll(' ', '%20') : str
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Remove the file extension from a filename and return the result.
|
|
116
|
+
*
|
|
117
|
+
* The filename is expected to be a POSIX path. The extension is only stripped
|
|
118
|
+
* when no path separator follows the last dot, so paths like
|
|
119
|
+
* "dir.with.dots/file" are returned unchanged.
|
|
120
|
+
*
|
|
121
|
+
* @param {string} filename - the String file name to process
|
|
122
|
+
* @returns {string} the String filename with the file extension removed
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* rootname('part1/chapter1.adoc')
|
|
126
|
+
* // => "part1/chapter1"
|
|
127
|
+
*/
|
|
128
|
+
export function rootname(filename) {
|
|
129
|
+
const lastDotIdx = filename.lastIndexOf('.')
|
|
130
|
+
if (lastDotIdx < 0) return filename
|
|
131
|
+
return filename.indexOf('/', lastDotIdx) >= 0
|
|
132
|
+
? filename
|
|
133
|
+
: filename.slice(0, lastDotIdx)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Retrieve the basename of a filename, optionally removing the extension.
|
|
138
|
+
*
|
|
139
|
+
* @param {string} filename - the String file name to process
|
|
140
|
+
* @param {boolean|string|null} [dropExt=null] - a Boolean flag or an explicit String extension to drop
|
|
141
|
+
* @returns {string} the String filename with leading directories removed and, optionally, the extension removed
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* basename('images/tiger.png', true)
|
|
145
|
+
* // => "tiger"
|
|
146
|
+
*
|
|
147
|
+
* basename('images/tiger.png', '.png')
|
|
148
|
+
* // => "tiger"
|
|
149
|
+
*/
|
|
150
|
+
export function basename(filename, dropExt = null) {
|
|
151
|
+
// Split on both POSIX and Windows separators, take the last non-empty segment.
|
|
152
|
+
const base =
|
|
153
|
+
filename
|
|
154
|
+
.replace(/[/\\]+$/, '')
|
|
155
|
+
.split(/[/\\]/)
|
|
156
|
+
.pop() ?? filename
|
|
157
|
+
if (!dropExt) return base
|
|
158
|
+
const ext = dropExt === true ? extname(base) : dropExt
|
|
159
|
+
return ext && base.endsWith(ext) ? base.slice(0, -ext.length) : base
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Return whether this path has a file extension.
|
|
164
|
+
*
|
|
165
|
+
* @param {string} path - the path String to check (expects a POSIX path)
|
|
166
|
+
* @returns {boolean} true if the path has a file extension, false otherwise
|
|
167
|
+
*/
|
|
168
|
+
export function isExtname(path) {
|
|
169
|
+
const lastDotIdx = path.lastIndexOf('.')
|
|
170
|
+
return lastDotIdx >= 0 && path.indexOf('/', lastDotIdx) < 0
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Retrieve the file extension of the specified path.
|
|
175
|
+
*
|
|
176
|
+
* The file extension is the portion of the last path segment starting from
|
|
177
|
+
* the last period. Differs from Node's path.extname in that the fallback value
|
|
178
|
+
* is configurable.
|
|
179
|
+
*
|
|
180
|
+
* @param {string} path - the path String in which to look for a file extension
|
|
181
|
+
* @param {string} [fallback=''] - the fallback String to return if no file extension is present
|
|
182
|
+
* @returns {string} the String file extension (with the leading dot) or fallback
|
|
183
|
+
*/
|
|
184
|
+
export function extname(path, fallback = '') {
|
|
185
|
+
const lastDotIdx = path.lastIndexOf('.')
|
|
186
|
+
if (lastDotIdx < 0) return fallback
|
|
187
|
+
// treat both '/' and '\\' as path separators (Windows support)
|
|
188
|
+
if (path.indexOf('/', lastDotIdx) >= 0 || path.indexOf('\\', lastDotIdx) >= 0)
|
|
189
|
+
return fallback
|
|
190
|
+
return path.slice(lastDotIdx)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Async-aware string replacement using matchAll.
|
|
195
|
+
*
|
|
196
|
+
* The replacer may return a string or a Promise<string>.
|
|
197
|
+
* The regex is treated as global regardless of its flags.
|
|
198
|
+
*
|
|
199
|
+
* @param {string} str - the String to perform replacements on
|
|
200
|
+
* @param {RegExp} regex - the RegExp pattern to match
|
|
201
|
+
* @param {Function} replacer - an async function receiving the same arguments as String#replace callbacks
|
|
202
|
+
* @returns {Promise<string>} the String with all matches replaced
|
|
203
|
+
*/
|
|
204
|
+
export async function asyncReplace(str, regex, replacer) {
|
|
205
|
+
const gRegex = regex.flags.includes('g')
|
|
206
|
+
? regex
|
|
207
|
+
: new RegExp(regex.source, `${regex.flags}g`)
|
|
208
|
+
const matches = [...str.matchAll(gRegex)]
|
|
209
|
+
if (matches.length === 0) return str
|
|
210
|
+
const parts = []
|
|
211
|
+
let lastIndex = 0
|
|
212
|
+
for (const match of matches) {
|
|
213
|
+
parts.push(str.slice(lastIndex, match.index))
|
|
214
|
+
// Process replacements sequentially so state mutations (e.g. footnote registration)
|
|
215
|
+
// are visible to subsequent replacements in the same string.
|
|
216
|
+
parts.push(await replacer(...match, match.index, str))
|
|
217
|
+
lastIndex = match.index + match[0].length
|
|
218
|
+
}
|
|
219
|
+
parts.push(str.slice(lastIndex))
|
|
220
|
+
return parts.join('')
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Make a directory, creating all missing parent directories.
|
|
225
|
+
*
|
|
226
|
+
* @param {string} dir - the String path of the directory to create
|
|
227
|
+
* @returns {Promise<void>} Throws if the path cannot be created
|
|
228
|
+
*/
|
|
229
|
+
export async function mkdirP(dir) {
|
|
230
|
+
const { mkdir } = await import('node:fs/promises')
|
|
231
|
+
await mkdir(dir, { recursive: true })
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ── Roman numeral helpers ─────────────────────────────────────────────────────
|
|
235
|
+
|
|
236
|
+
const ROMAN_NUMERALS_WITH_REDUCERS = [
|
|
237
|
+
['M', 1000],
|
|
238
|
+
['CM', 900],
|
|
239
|
+
['D', 500],
|
|
240
|
+
['CD', 400],
|
|
241
|
+
['C', 100],
|
|
242
|
+
['XC', 90],
|
|
243
|
+
['L', 50],
|
|
244
|
+
['XL', 40],
|
|
245
|
+
['X', 10],
|
|
246
|
+
['IX', 9],
|
|
247
|
+
['V', 5],
|
|
248
|
+
['IV', 4],
|
|
249
|
+
['I', 1],
|
|
250
|
+
]
|
|
251
|
+
|
|
252
|
+
const ROMAN_NUMERALS = { I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000 }
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Convert an integer to a Roman numeral.
|
|
256
|
+
*
|
|
257
|
+
* @param {number} val - the integer value to convert
|
|
258
|
+
* @returns {string} the String Roman numeral
|
|
259
|
+
*/
|
|
260
|
+
export function intToRoman(val) {
|
|
261
|
+
let result = ''
|
|
262
|
+
for (const [l, i] of ROMAN_NUMERALS_WITH_REDUCERS) {
|
|
263
|
+
const repeat = Math.floor(val / i)
|
|
264
|
+
val %= i
|
|
265
|
+
result += l.repeat(repeat)
|
|
266
|
+
}
|
|
267
|
+
return result
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Convert an uppercase Roman numeral to an integer.
|
|
272
|
+
*
|
|
273
|
+
* @param {string} val - the String Roman numeral in uppercase to convert
|
|
274
|
+
* @returns {number} the integer value
|
|
275
|
+
*/
|
|
276
|
+
export function romanToInt(val) {
|
|
277
|
+
const valmap = [...val].map((c) => ROMAN_NUMERALS[c])
|
|
278
|
+
let result = 0
|
|
279
|
+
for (let idx = 0; idx < valmap.length; idx++) {
|
|
280
|
+
const v = valmap[idx]
|
|
281
|
+
const succ = valmap[idx + 1]
|
|
282
|
+
result += succ && succ > v ? -v : v
|
|
283
|
+
}
|
|
284
|
+
return result
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Get the next value in a sequence.
|
|
289
|
+
*
|
|
290
|
+
* Handles integer sequences (numeric increment) and alphabetic sequences
|
|
291
|
+
* (ASCII letter increment with carry, matching Ruby's String#succ for the
|
|
292
|
+
* alphanumeric subset used by Asciidoctor list labels).
|
|
293
|
+
*
|
|
294
|
+
* @param {string|number} current - the value to increment
|
|
295
|
+
* @returns {string|number} the next value in the sequence
|
|
296
|
+
*/
|
|
297
|
+
export function nextval(current) {
|
|
298
|
+
if (typeof current === 'number') return current + 1
|
|
299
|
+
const intval = parseInt(current, 10)
|
|
300
|
+
if (String(intval) === String(current)) return intval + 1
|
|
301
|
+
// Mirrors Ruby's String#succ for single- and multi-character strings.
|
|
302
|
+
// Strategy: find the rightmost ASCII-alphanumeric character and increment it
|
|
303
|
+
// with carry. If NO alphanumeric character exists, increment the rightmost
|
|
304
|
+
// character's Unicode code point instead.
|
|
305
|
+
const chars = [...current] // split by Unicode code point (handles surrogate pairs)
|
|
306
|
+
let hasAlnum = false
|
|
307
|
+
for (let i = chars.length - 1; i >= 0; i--) {
|
|
308
|
+
const code = chars[i].codePointAt(0)
|
|
309
|
+
const isLower = code >= 97 && code <= 122
|
|
310
|
+
const isUpper = code >= 65 && code <= 90
|
|
311
|
+
const isDigit = code >= 48 && code <= 57
|
|
312
|
+
if (!isLower && !isUpper && !isDigit) continue
|
|
313
|
+
hasAlnum = true
|
|
314
|
+
const atEnd =
|
|
315
|
+
(isLower && code === 122) ||
|
|
316
|
+
(isUpper && code === 90) ||
|
|
317
|
+
(isDigit && code === 57)
|
|
318
|
+
if (!atEnd) {
|
|
319
|
+
chars[i] = String.fromCodePoint(code + 1)
|
|
320
|
+
return chars.join('')
|
|
321
|
+
}
|
|
322
|
+
// Carry: wrap this char and continue to the next alphanumeric to the left.
|
|
323
|
+
chars[i] = isLower ? 'a' : isUpper ? 'A' : '0'
|
|
324
|
+
// Find next alphanumeric to carry into.
|
|
325
|
+
let carried = false
|
|
326
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
327
|
+
const c2 = chars[j].codePointAt(0)
|
|
328
|
+
const l2 = c2 >= 97 && c2 <= 122
|
|
329
|
+
const u2 = c2 >= 65 && c2 <= 90
|
|
330
|
+
const d2 = c2 >= 48 && c2 <= 57
|
|
331
|
+
if (!l2 && !u2 && !d2) continue
|
|
332
|
+
const end2 = (l2 && c2 === 122) || (u2 && c2 === 90) || (d2 && c2 === 57)
|
|
333
|
+
if (!end2) {
|
|
334
|
+
chars[j] = String.fromCodePoint(c2 + 1)
|
|
335
|
+
carried = true
|
|
336
|
+
break
|
|
337
|
+
}
|
|
338
|
+
chars[j] = l2 ? 'a' : u2 ? 'A' : '0'
|
|
339
|
+
}
|
|
340
|
+
if (!carried) {
|
|
341
|
+
// All alphanumeric characters wrapped — prepend carry character.
|
|
342
|
+
const carry = isLower ? 'a' : isUpper ? 'A' : '1'
|
|
343
|
+
return carry + chars.join('')
|
|
344
|
+
}
|
|
345
|
+
return chars.join('')
|
|
346
|
+
}
|
|
347
|
+
if (!hasAlnum) {
|
|
348
|
+
// No alphanumeric chars: increment the rightmost character's code point.
|
|
349
|
+
const last = chars.length - 1
|
|
350
|
+
const code = chars[last].codePointAt(0)
|
|
351
|
+
chars[last] = String.fromCodePoint(code + 1)
|
|
352
|
+
return chars.join('')
|
|
353
|
+
}
|
|
354
|
+
return current
|
|
355
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import packageJson from '../package.json' with { type: 'json' }
|
|
2
|
+
import { load as _load, loadFile as _loadFile } from './load.js'
|
|
3
|
+
import { convert, convertFile } from './convert.js'
|
|
4
|
+
import {
|
|
5
|
+
Document,
|
|
6
|
+
DocumentTitle,
|
|
7
|
+
Author,
|
|
8
|
+
Footnote,
|
|
9
|
+
ImageReference,
|
|
10
|
+
RevisionInfo,
|
|
11
|
+
} from './document.js'
|
|
12
|
+
import { Logger, LoggerManager, MemoryLogger, NullLogger } from './logging.js'
|
|
13
|
+
import { SafeMode, ContentModel } from './constants.js'
|
|
14
|
+
import { Timings } from './timings.js'
|
|
15
|
+
import { AbstractNode } from './abstract_node.js'
|
|
16
|
+
import { AbstractBlock } from './abstract_block.js'
|
|
17
|
+
import {
|
|
18
|
+
Registry,
|
|
19
|
+
Processor,
|
|
20
|
+
ProcessorExtension,
|
|
21
|
+
Preprocessor,
|
|
22
|
+
TreeProcessor,
|
|
23
|
+
Postprocessor,
|
|
24
|
+
IncludeProcessor,
|
|
25
|
+
DocinfoProcessor,
|
|
26
|
+
BlockProcessor,
|
|
27
|
+
InlineMacroProcessor,
|
|
28
|
+
BlockMacroProcessor,
|
|
29
|
+
Extensions,
|
|
30
|
+
} from './extensions.js'
|
|
31
|
+
import {
|
|
32
|
+
Converter,
|
|
33
|
+
ConverterBase,
|
|
34
|
+
CustomFactory,
|
|
35
|
+
DefaultFactory as DefaultConverterFactory,
|
|
36
|
+
deriveBackendTraits,
|
|
37
|
+
} from './converter.js'
|
|
38
|
+
import { Inline } from './inline.js'
|
|
39
|
+
import { Block } from './block.js'
|
|
40
|
+
import { List, ListItem } from './list.js'
|
|
41
|
+
import { Section } from './section.js'
|
|
42
|
+
import { Cursor, Reader } from './reader.js'
|
|
43
|
+
import Html5Converter from './converter/html5.js'
|
|
44
|
+
import {
|
|
45
|
+
SyntaxHighlighter,
|
|
46
|
+
SyntaxHighlighterBase,
|
|
47
|
+
DefaultFactory as DefaultSyntaxHighlighterFactory,
|
|
48
|
+
} from './syntax_highlighter.js'
|
|
49
|
+
|
|
50
|
+
const ASCIIDOCTOR_CORE_VERSION = '2.0.26'
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get the version of Asciidoctor.js.
|
|
54
|
+
*
|
|
55
|
+
* @returns {string} - the version of Asciidoctor.js
|
|
56
|
+
*/
|
|
57
|
+
export function getVersion() {
|
|
58
|
+
return packageJson.version
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get Asciidoctor core version number.
|
|
63
|
+
*
|
|
64
|
+
* @returns {string} - the version of Asciidoctor core (Ruby)
|
|
65
|
+
*/
|
|
66
|
+
export function getCoreVersion() {
|
|
67
|
+
return ASCIIDOCTOR_CORE_VERSION
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Parse the AsciiDoc source input into a Document.
|
|
72
|
+
*
|
|
73
|
+
* @param {string|string[]|Buffer} input - the AsciiDoc source as a String, String Array, or Buffer
|
|
74
|
+
* @param {Object} [options={}] - a plain object of options to control processing
|
|
75
|
+
* @returns {Promise<Document>} - the parsed Document
|
|
76
|
+
*/
|
|
77
|
+
export async function load(input, options = {}) {
|
|
78
|
+
return _load(input, options)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Parse the contents of the AsciiDoc source file into a Document.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} filename - the path to the AsciiDoc source file
|
|
85
|
+
* @param {Object} [options={}] - a plain object of options to control processing
|
|
86
|
+
* @returns {Promise<Document>} - the parsed Document
|
|
87
|
+
*/
|
|
88
|
+
export async function loadFile(filename, options = {}) {
|
|
89
|
+
return _loadFile(filename, options)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export { convert, convertFile }
|
|
93
|
+
|
|
94
|
+
export {
|
|
95
|
+
Document,
|
|
96
|
+
DocumentTitle,
|
|
97
|
+
Author,
|
|
98
|
+
Footnote,
|
|
99
|
+
ImageReference,
|
|
100
|
+
RevisionInfo,
|
|
101
|
+
Logger,
|
|
102
|
+
AbstractNode,
|
|
103
|
+
AbstractBlock,
|
|
104
|
+
Inline,
|
|
105
|
+
Block,
|
|
106
|
+
List,
|
|
107
|
+
ListItem,
|
|
108
|
+
Section,
|
|
109
|
+
Reader,
|
|
110
|
+
SyntaxHighlighterBase,
|
|
111
|
+
LoggerManager,
|
|
112
|
+
MemoryLogger,
|
|
113
|
+
NullLogger,
|
|
114
|
+
SafeMode,
|
|
115
|
+
ContentModel,
|
|
116
|
+
Timings,
|
|
117
|
+
Registry,
|
|
118
|
+
Processor,
|
|
119
|
+
ProcessorExtension,
|
|
120
|
+
Preprocessor,
|
|
121
|
+
TreeProcessor,
|
|
122
|
+
Postprocessor,
|
|
123
|
+
IncludeProcessor,
|
|
124
|
+
DocinfoProcessor,
|
|
125
|
+
BlockProcessor,
|
|
126
|
+
InlineMacroProcessor,
|
|
127
|
+
BlockMacroProcessor,
|
|
128
|
+
Extensions,
|
|
129
|
+
Cursor,
|
|
130
|
+
Converter as ConverterFactory,
|
|
131
|
+
ConverterBase,
|
|
132
|
+
CustomFactory as ConverterCustomFactory,
|
|
133
|
+
DefaultConverterFactory,
|
|
134
|
+
deriveBackendTraits,
|
|
135
|
+
DefaultSyntaxHighlighterFactory,
|
|
136
|
+
Html5Converter,
|
|
137
|
+
SyntaxHighlighter,
|
|
138
|
+
}
|
package/src/inline.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// ESM conversion of inline.rb
|
|
2
|
+
|
|
3
|
+
import { AbstractNode } from './abstract_node.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Represents an inline element in an AsciiDoc document.
|
|
7
|
+
*/
|
|
8
|
+
export class Inline extends AbstractNode {
|
|
9
|
+
/** @type {string|null} */
|
|
10
|
+
id
|
|
11
|
+
/** @type {string|null} */
|
|
12
|
+
type
|
|
13
|
+
/** @type {string|null} */
|
|
14
|
+
target
|
|
15
|
+
/** @type {string|null} */
|
|
16
|
+
text
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {AbstractNode} parent
|
|
20
|
+
* @param {string} context
|
|
21
|
+
* @param {string|null} [text=null] - The String text of this inline element.
|
|
22
|
+
* @param {Object} [opts={}] - A plain object of options:
|
|
23
|
+
* id - The String id of this inline element.
|
|
24
|
+
* type - The String type qualifier (e.g. 'ref', 'bibref').
|
|
25
|
+
* target - The String target (e.g. a URI).
|
|
26
|
+
*/
|
|
27
|
+
constructor(parent, context, text = null, opts = {}) {
|
|
28
|
+
super(parent, context, opts)
|
|
29
|
+
this.nodeName = `inline_${context}`
|
|
30
|
+
this.text = text
|
|
31
|
+
this.id = opts.id ?? null
|
|
32
|
+
this.type = opts.type ?? null
|
|
33
|
+
this.target = opts.target ?? null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
isBlock() {
|
|
37
|
+
return false
|
|
38
|
+
}
|
|
39
|
+
isInline() {
|
|
40
|
+
return true
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Convert this inline element using the document's converter.
|
|
45
|
+
* @returns {Promise<string>}
|
|
46
|
+
*/
|
|
47
|
+
async convert() {
|
|
48
|
+
return this.converter.convert(this)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @deprecated Use convert() instead. */
|
|
52
|
+
render() {
|
|
53
|
+
return this.convert()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get the converted content (alias for text).
|
|
58
|
+
* @returns {string|null}
|
|
59
|
+
*/
|
|
60
|
+
content() {
|
|
61
|
+
return this.text
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Alias for {@link getAlt}.
|
|
66
|
+
* @see {getAlt}
|
|
67
|
+
*/
|
|
68
|
+
get alt() {
|
|
69
|
+
return this.getAttribute('alt') || ''
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check whether this inline node has reftext.
|
|
74
|
+
* For ref and bibref nodes the text acts as the reftext.
|
|
75
|
+
* @returns {boolean}
|
|
76
|
+
*/
|
|
77
|
+
hasReftext() {
|
|
78
|
+
return !!(this.text && (this.type === 'ref' || this.type === 'bibref'))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get the reftext for this inline node with substitutions applied.
|
|
83
|
+
* The result is pre-computed during Document.parse() via precomputeReftext().
|
|
84
|
+
* Falls back to the raw text if precomputeReftext() has not been called yet.
|
|
85
|
+
* @returns {string|null}
|
|
86
|
+
*/
|
|
87
|
+
get reftext() {
|
|
88
|
+
if (this._convertedReftext !== undefined) return this._convertedReftext
|
|
89
|
+
return this.text ?? null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @internal
|
|
94
|
+
* Pre-compute the reftext with substitutions applied asynchronously.
|
|
95
|
+
* Called during Document.parse() so the synchronous getter works during conversion.
|
|
96
|
+
* @returns {Promise<void>}
|
|
97
|
+
*/
|
|
98
|
+
async precomputeReftext() {
|
|
99
|
+
const val = this.text
|
|
100
|
+
this._convertedReftext =
|
|
101
|
+
val != null ? await this.applyReftextSubs(val) : null
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Generate cross-reference text (xreftext) that can be used to refer to this inline node.
|
|
106
|
+
*
|
|
107
|
+
* Uses the explicit reftext for this inline node, if specified, retrieved by calling the
|
|
108
|
+
* reftext method. Otherwise, returns null.
|
|
109
|
+
*
|
|
110
|
+
* @param {string|null} [_xrefstyle=null] - Not currently used.
|
|
111
|
+
* @returns {string|null} the reftext to refer to this inline node, or null if no reftext is defined.
|
|
112
|
+
*/
|
|
113
|
+
xreftext(_xrefstyle = null) {
|
|
114
|
+
return this.reftext
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── JavaScript-style accessors ────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Return the text of this inline node.
|
|
121
|
+
* @returns {string|null}
|
|
122
|
+
*/
|
|
123
|
+
getText() {
|
|
124
|
+
return this.text
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Return the type qualifier of this inline node (e.g. 'ref', 'bibref').
|
|
129
|
+
* @returns {string|null}
|
|
130
|
+
*/
|
|
131
|
+
getType() {
|
|
132
|
+
return this.type
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Return the target (e.g. URI or anchor) of this inline node.
|
|
137
|
+
* @returns {string|null}
|
|
138
|
+
*/
|
|
139
|
+
getTarget() {
|
|
140
|
+
return this.target
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get the alt text for this inline image.
|
|
145
|
+
* @returns {string} the value of the alt attribute, or ''.
|
|
146
|
+
*/
|
|
147
|
+
getAlt() {
|
|
148
|
+
return this.alt
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get the reftext for this inline node with substitutions applied.
|
|
153
|
+
* @returns {string|null}
|
|
154
|
+
*/
|
|
155
|
+
getReftext() {
|
|
156
|
+
return this.reftext
|
|
157
|
+
}
|
|
158
|
+
}
|