@asciidoctor/core 3.0.4 → 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/logging.js
ADDED
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
// ESM conversion of logging.rb
|
|
2
|
+
//
|
|
3
|
+
// Ruby-to-JavaScript notes:
|
|
4
|
+
// - Ruby's Logger hierarchy (Logger, MemoryLogger, NullLogger) is reimplemented
|
|
5
|
+
// without inheriting from a stdlib Logger class.
|
|
6
|
+
// - Severity levels mirror Ruby's Logger::Severity constants.
|
|
7
|
+
// - Logger.BasicFormatter formats messages as "asciidoctor: SEVERITY: text\n".
|
|
8
|
+
// - Logger.AutoFormattingMessage is an interface for objects that carry both
|
|
9
|
+
// text and source_location; in JS it is a plain object with a custom
|
|
10
|
+
// toString / inspect method attached.
|
|
11
|
+
// - LoggerManager is a module-level singleton object (not a class instance).
|
|
12
|
+
// - The Logging mixin is applied via applyLogging(prototype) which installs
|
|
13
|
+
// `logger` and `messageWithContext` on the target prototype.
|
|
14
|
+
// - In JS there is no $stderr; the default pipe is console.error.
|
|
15
|
+
|
|
16
|
+
// ── Severity levels (mirrors Ruby Logger::Severity) ──────────────────────────
|
|
17
|
+
export const Severity = {
|
|
18
|
+
DEBUG: 0,
|
|
19
|
+
INFO: 1,
|
|
20
|
+
WARN: 2,
|
|
21
|
+
ERROR: 3,
|
|
22
|
+
FATAL: 4,
|
|
23
|
+
UNKNOWN: 5,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const SEVERITY_LABEL = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL', 'ANY']
|
|
27
|
+
const SEVERITY_LABEL_SUBSTITUTES = { WARN: 'WARNING', FATAL: 'FAILED' }
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Convert a string or nullable severity value to a numeric Severity constant.
|
|
31
|
+
* @param {number|string|null|undefined} severity
|
|
32
|
+
* @returns {number}
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
function resolveSeverity(severity) {
|
|
36
|
+
if (typeof severity === 'number') return severity
|
|
37
|
+
if (typeof severity === 'string')
|
|
38
|
+
return Severity[severity.toUpperCase()] ?? Severity.UNKNOWN
|
|
39
|
+
return severity ?? Severity.UNKNOWN
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ── Logger ────────────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
/** Standard logger that writes formatted messages to stderr or a custom pipe. */
|
|
45
|
+
export class Logger {
|
|
46
|
+
constructor(opts = {}) {
|
|
47
|
+
this.progname = opts.progname ?? 'asciidoctor'
|
|
48
|
+
this.level = opts.level ?? Severity.WARN
|
|
49
|
+
/** @internal */
|
|
50
|
+
this._maxSeverity = null
|
|
51
|
+
/** @internal */
|
|
52
|
+
this._formatter = opts.formatter ?? new Logger.BasicFormatter()
|
|
53
|
+
/** @internal */
|
|
54
|
+
this._pipe = opts.pipe ?? null // null → write via _writeln
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** getter/setter so custom logger impls can access this.formatter */
|
|
58
|
+
get formatter() {
|
|
59
|
+
return this._formatter
|
|
60
|
+
}
|
|
61
|
+
set formatter(f) {
|
|
62
|
+
this._formatter = f
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @returns {number|null} The highest severity level logged so far.
|
|
67
|
+
*/
|
|
68
|
+
get maxSeverity() {
|
|
69
|
+
return this._maxSeverity
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Fluent getters/setters (used by the public API consumed by tests)
|
|
73
|
+
getLevel() {
|
|
74
|
+
return this.level
|
|
75
|
+
}
|
|
76
|
+
setLevel(n) {
|
|
77
|
+
this.level = n
|
|
78
|
+
}
|
|
79
|
+
getFormatter() {
|
|
80
|
+
return this._formatter
|
|
81
|
+
}
|
|
82
|
+
setFormatter(f) {
|
|
83
|
+
this._formatter = f
|
|
84
|
+
}
|
|
85
|
+
getProgramName() {
|
|
86
|
+
return this.progname
|
|
87
|
+
}
|
|
88
|
+
setProgramName(n) {
|
|
89
|
+
this.progname = n
|
|
90
|
+
}
|
|
91
|
+
getMaxSeverity() {
|
|
92
|
+
return this._maxSeverity
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @returns {boolean} Whether DEBUG-level messages will be logged.
|
|
97
|
+
*/
|
|
98
|
+
isDebugEnabled() {
|
|
99
|
+
return this.level <= Severity.DEBUG
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @returns {boolean} Whether INFO-level messages will be logged.
|
|
104
|
+
*/
|
|
105
|
+
isInfoEnabled() {
|
|
106
|
+
return this.level <= Severity.INFO
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @returns {boolean} Whether WARN-level messages will be logged.
|
|
111
|
+
*/
|
|
112
|
+
isWarnEnabled() {
|
|
113
|
+
return this.level <= Severity.WARN
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @returns {boolean} Whether ERROR-level messages will be logged.
|
|
118
|
+
*/
|
|
119
|
+
isErrorEnabled() {
|
|
120
|
+
return this.level <= Severity.ERROR
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @returns {boolean} Whether FATAL-level messages will be logged.
|
|
125
|
+
*/
|
|
126
|
+
isFatalEnabled() {
|
|
127
|
+
return this.level <= Severity.FATAL
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Kept for internal compatibility
|
|
131
|
+
isDebug() {
|
|
132
|
+
return this.level <= Severity.DEBUG
|
|
133
|
+
}
|
|
134
|
+
isInfo() {
|
|
135
|
+
return this.level <= Severity.INFO
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Log a message at the given severity level.
|
|
140
|
+
* @param {number|string} severity - Severity level (numeric constant or string name).
|
|
141
|
+
* @param {string|{inspect?(): string}|null} [message=null] - The message to log.
|
|
142
|
+
* @param {string|Function|null} [progname=null] - Program name or message supplier function.
|
|
143
|
+
* @returns {boolean}
|
|
144
|
+
*/
|
|
145
|
+
add(severity, message = null, progname = null) {
|
|
146
|
+
severity = resolveSeverity(severity)
|
|
147
|
+
if (this._maxSeverity === null || severity > this._maxSeverity) {
|
|
148
|
+
this._maxSeverity = severity
|
|
149
|
+
}
|
|
150
|
+
if (severity < this.level) return true
|
|
151
|
+
const text =
|
|
152
|
+
message ?? (typeof progname === 'function' ? progname() : progname)
|
|
153
|
+
const label = SEVERITY_LABEL[severity] ?? 'ANY'
|
|
154
|
+
const line = this._formatter.call(label, null, this.progname, text)
|
|
155
|
+
this._writeln(line)
|
|
156
|
+
return true
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Alias for {@link add} (Ruby Logger API). */
|
|
160
|
+
log(severity, message, progname) {
|
|
161
|
+
return this.add(severity, message, progname)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
debug(msg, progname) {
|
|
165
|
+
return this.add(Severity.DEBUG, msg, progname)
|
|
166
|
+
}
|
|
167
|
+
info(msg, progname) {
|
|
168
|
+
return this.add(Severity.INFO, msg, progname)
|
|
169
|
+
}
|
|
170
|
+
warn(msg, progname) {
|
|
171
|
+
return this.add(Severity.WARN, msg, progname)
|
|
172
|
+
}
|
|
173
|
+
error(msg, progname) {
|
|
174
|
+
return this.add(Severity.ERROR, msg, progname)
|
|
175
|
+
}
|
|
176
|
+
fatal(msg, progname) {
|
|
177
|
+
return this.add(Severity.FATAL, msg, progname)
|
|
178
|
+
}
|
|
179
|
+
unknown(msg, progname) {
|
|
180
|
+
return this.add(Severity.UNKNOWN, msg, progname)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Write a formatted line to stderr or console.error.
|
|
185
|
+
* @param {string} line
|
|
186
|
+
* @internal
|
|
187
|
+
*/
|
|
188
|
+
_writeln(line) {
|
|
189
|
+
if (typeof process !== 'undefined' && process.stderr?.write) {
|
|
190
|
+
process.stderr.write(line)
|
|
191
|
+
} else {
|
|
192
|
+
console.error(line.replace(/\n$/, ''))
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
Logger.BasicFormatter = class {
|
|
198
|
+
/**
|
|
199
|
+
* Format a log entry as "progname: SEVERITY: message\n".
|
|
200
|
+
* @param {number|string} severity
|
|
201
|
+
* @param {null} _time
|
|
202
|
+
* @param {string} progname
|
|
203
|
+
* @param {string|{inspect?(): string}} msg
|
|
204
|
+
* @returns {string}
|
|
205
|
+
*/
|
|
206
|
+
call(severity, _time, progname, msg) {
|
|
207
|
+
// severity may be numeric (from newLogger impls) or a string label
|
|
208
|
+
const label =
|
|
209
|
+
typeof severity === 'number'
|
|
210
|
+
? (SEVERITY_LABEL[severity] ?? 'ANY')
|
|
211
|
+
: severity
|
|
212
|
+
const substituted = SEVERITY_LABEL_SUBSTITUTES[label] ?? label
|
|
213
|
+
const text =
|
|
214
|
+
typeof msg === 'string' ? msg : (msg?.inspect?.() ?? String(msg))
|
|
215
|
+
return `${progname}: ${substituted}: ${text}\n`
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
Logger.AutoFormattingMessage = {
|
|
220
|
+
/**
|
|
221
|
+
* Attach auto-formatting to any plain object carrying { text, source_location }.
|
|
222
|
+
* @param {{text: string, source_location?: string}} obj
|
|
223
|
+
* @returns {typeof obj} The same object with inspect() and toString() added.
|
|
224
|
+
*/
|
|
225
|
+
attach(obj) {
|
|
226
|
+
obj.inspect = function () {
|
|
227
|
+
const sloc = this.source_location
|
|
228
|
+
return sloc ? `${sloc}: ${this.text}` : this.text
|
|
229
|
+
}
|
|
230
|
+
obj.toString = obj.inspect
|
|
231
|
+
return obj
|
|
232
|
+
},
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ── LogMessage ────────────────────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
/** Wrapper stored by MemoryLogger; provides getSeverity/getText/getSourceLocation. */
|
|
238
|
+
class LogMessage {
|
|
239
|
+
constructor(severity, message) {
|
|
240
|
+
this.message = message
|
|
241
|
+
this.severity = severity // string label, e.g. 'ERROR'
|
|
242
|
+
// AutoFormattingMessage objects carry { text, source_location }
|
|
243
|
+
if (message !== null && typeof message === 'object' && 'text' in message) {
|
|
244
|
+
this._text = message.text
|
|
245
|
+
this._sourceLocation = message.source_location ?? null
|
|
246
|
+
} else {
|
|
247
|
+
this._text = message != null ? String(message) : ''
|
|
248
|
+
this._sourceLocation = null
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
getSeverity() {
|
|
253
|
+
return this.severity
|
|
254
|
+
}
|
|
255
|
+
getText() {
|
|
256
|
+
return this._text
|
|
257
|
+
}
|
|
258
|
+
getSourceLocation() {
|
|
259
|
+
return this._sourceLocation ?? undefined
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ── MemoryLogger ──────────────────────────────────────────────────────────────
|
|
264
|
+
|
|
265
|
+
/** In-memory logger that stores all log messages for later inspection. */
|
|
266
|
+
export class MemoryLogger {
|
|
267
|
+
constructor() {
|
|
268
|
+
// Default level is UNKNOWN (highest) so isDebug() returns false by default,
|
|
269
|
+
// matching Ruby's MemoryLogger (level: UNKNOWN). The add() method stores all
|
|
270
|
+
// messages unconditionally — level is only used by the isDebug() guard.
|
|
271
|
+
this.level = Severity.UNKNOWN
|
|
272
|
+
this.messages = []
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
static create() {
|
|
276
|
+
return new MemoryLogger()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
getMessages() {
|
|
280
|
+
return this.messages
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
getMaxSeverity() {
|
|
284
|
+
if (this.messages.length === 0) return null
|
|
285
|
+
return Math.max(
|
|
286
|
+
...this.messages.map((m) => Severity[m.getSeverity()] ?? Severity.UNKNOWN)
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
add(severity, message = null, progname = null) {
|
|
291
|
+
const sev = resolveSeverity(severity)
|
|
292
|
+
const msg =
|
|
293
|
+
message ?? (typeof progname === 'function' ? progname() : progname)
|
|
294
|
+
const severityName = SEVERITY_LABEL[sev] ?? 'UNKNOWN'
|
|
295
|
+
this.messages.push(new LogMessage(severityName, msg))
|
|
296
|
+
return true
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
debug(msg, pn) {
|
|
300
|
+
return this.add(Severity.DEBUG, msg, pn)
|
|
301
|
+
}
|
|
302
|
+
info(msg, pn) {
|
|
303
|
+
return this.add(Severity.INFO, msg, pn)
|
|
304
|
+
}
|
|
305
|
+
warn(msg, pn) {
|
|
306
|
+
return this.add(Severity.WARN, msg, pn)
|
|
307
|
+
}
|
|
308
|
+
error(msg, pn) {
|
|
309
|
+
return this.add(Severity.ERROR, msg, pn)
|
|
310
|
+
}
|
|
311
|
+
fatal(msg, pn) {
|
|
312
|
+
return this.add(Severity.FATAL, msg, pn)
|
|
313
|
+
}
|
|
314
|
+
unknown(msg, pn) {
|
|
315
|
+
return this.add(Severity.UNKNOWN, msg, pn)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
log(severity, message, progname) {
|
|
319
|
+
return this.add(severity, message, progname)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
isDebug() {
|
|
323
|
+
return this.level <= Severity.DEBUG
|
|
324
|
+
}
|
|
325
|
+
isInfo() {
|
|
326
|
+
return this.level <= Severity.INFO
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Write a string at INFO level (trailing newline stripped).
|
|
331
|
+
* Allows MemoryLogger to be used with Timings.printReport().
|
|
332
|
+
* @param {string} s
|
|
333
|
+
* @returns {boolean}
|
|
334
|
+
*/
|
|
335
|
+
write(s) {
|
|
336
|
+
return this.info(s.replace(/\n$/, ''))
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
clear() {
|
|
340
|
+
this.messages.length = 0
|
|
341
|
+
}
|
|
342
|
+
empty() {
|
|
343
|
+
return this.messages.length === 0
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ── NullLogger ────────────────────────────────────────────────────────────────
|
|
348
|
+
|
|
349
|
+
/** Logger that discards all messages but still tracks the maximum severity. */
|
|
350
|
+
export class NullLogger {
|
|
351
|
+
constructor() {
|
|
352
|
+
this.level = Severity.UNKNOWN
|
|
353
|
+
this._maxSeverity = null
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
static create() {
|
|
357
|
+
return new NullLogger()
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
get maxSeverity() {
|
|
361
|
+
return this._maxSeverity
|
|
362
|
+
}
|
|
363
|
+
getMaxSeverity() {
|
|
364
|
+
return this._maxSeverity
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
add(severity) {
|
|
368
|
+
const sev = resolveSeverity(severity)
|
|
369
|
+
if (this._maxSeverity === null || sev > this._maxSeverity)
|
|
370
|
+
this._maxSeverity = sev
|
|
371
|
+
return true
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
log(severity) {
|
|
375
|
+
return this.add(severity)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
debug() {
|
|
379
|
+
return this.add(Severity.DEBUG)
|
|
380
|
+
}
|
|
381
|
+
info() {
|
|
382
|
+
return this.add(Severity.INFO)
|
|
383
|
+
}
|
|
384
|
+
warn() {
|
|
385
|
+
return this.add(Severity.WARN)
|
|
386
|
+
}
|
|
387
|
+
error() {
|
|
388
|
+
return this.add(Severity.ERROR)
|
|
389
|
+
}
|
|
390
|
+
fatal() {
|
|
391
|
+
return this.add(Severity.FATAL)
|
|
392
|
+
}
|
|
393
|
+
unknown() {
|
|
394
|
+
return this.add(Severity.UNKNOWN)
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// ── LoggerManager ─────────────────────────────────────────────────────────────
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Module-level singleton — the active logger is stored here and can be
|
|
402
|
+
* replaced by callers (e.g. the load function).
|
|
403
|
+
*/
|
|
404
|
+
export const LoggerManager = (() => {
|
|
405
|
+
let _loggerClass = Logger
|
|
406
|
+
let _logger = null
|
|
407
|
+
|
|
408
|
+
return {
|
|
409
|
+
get loggerClass() {
|
|
410
|
+
return _loggerClass
|
|
411
|
+
},
|
|
412
|
+
set loggerClass(cls) {
|
|
413
|
+
_loggerClass = cls
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
get logger() {
|
|
417
|
+
if (!_logger) _logger = new _loggerClass()
|
|
418
|
+
return _logger
|
|
419
|
+
},
|
|
420
|
+
set logger(newLogger) {
|
|
421
|
+
_logger = newLogger ?? new _loggerClass()
|
|
422
|
+
},
|
|
423
|
+
|
|
424
|
+
// Public API (mirrors Ruby LoggerManager)
|
|
425
|
+
getLogger() {
|
|
426
|
+
return this.logger
|
|
427
|
+
},
|
|
428
|
+
setLogger(newLogger) {
|
|
429
|
+
this.logger = newLogger
|
|
430
|
+
},
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Create a new formatter whose call() delegates to the provided impl.
|
|
434
|
+
* @param {string} _name
|
|
435
|
+
* @param {{call: Function}} impl
|
|
436
|
+
* @returns {{call: Function}}
|
|
437
|
+
*/
|
|
438
|
+
newFormatter(_name, impl) {
|
|
439
|
+
return { call: impl.call.bind(impl) }
|
|
440
|
+
},
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Create a new Logger instance with custom behaviour supplied via impl.
|
|
444
|
+
* @param {string} _name
|
|
445
|
+
* @param {{add?: (severity: number, message: any, progname: any) => boolean, postConstruct?: (this: Logger) => void}} impl
|
|
446
|
+
* - `add(severity, message, progname)` — overrides the default add method; severity is always numeric.
|
|
447
|
+
* - `postConstruct()` — called once after the instance is created (`this` is the logger instance).
|
|
448
|
+
* @returns {Logger}
|
|
449
|
+
*/
|
|
450
|
+
newLogger(_name, impl) {
|
|
451
|
+
const inst = new Logger()
|
|
452
|
+
if (impl.add) {
|
|
453
|
+
const customAdd = impl.add
|
|
454
|
+
inst.add = function (severity, message = null, progname = null) {
|
|
455
|
+
const sev = resolveSeverity(severity)
|
|
456
|
+
if (this._maxSeverity === null || sev > this._maxSeverity) {
|
|
457
|
+
this._maxSeverity = sev
|
|
458
|
+
}
|
|
459
|
+
return customAdd.call(this, sev, message, progname)
|
|
460
|
+
}
|
|
461
|
+
// Re-bind shorthand methods so they resolve through the custom add
|
|
462
|
+
for (const [meth, sev] of [
|
|
463
|
+
['debug', Severity.DEBUG],
|
|
464
|
+
['info', Severity.INFO],
|
|
465
|
+
['warn', Severity.WARN],
|
|
466
|
+
['error', Severity.ERROR],
|
|
467
|
+
['fatal', Severity.FATAL],
|
|
468
|
+
['unknown', Severity.UNKNOWN],
|
|
469
|
+
]) {
|
|
470
|
+
inst[meth] = (msg, pn) => inst.add(sev, msg, pn)
|
|
471
|
+
}
|
|
472
|
+
inst.log = (severity, msg, pn) => inst.add(severity, msg, pn)
|
|
473
|
+
}
|
|
474
|
+
if (impl.postConstruct) impl.postConstruct.call(inst)
|
|
475
|
+
return inst
|
|
476
|
+
},
|
|
477
|
+
}
|
|
478
|
+
})()
|
|
479
|
+
|
|
480
|
+
// ── Logging mixin ─────────────────────────────────────────────────────────────
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Apply the Logging mixin to a class prototype.
|
|
484
|
+
*
|
|
485
|
+
* Installs the following on proto:
|
|
486
|
+
* - `logger` getter — returns `LoggerManager.logger`
|
|
487
|
+
* - `getLogger()` — method alias for the logger getter
|
|
488
|
+
* - `messageWithContext(text, context)` — builds an auto-formatting message object
|
|
489
|
+
* - `createLogMessage(text, context)` — alias for messageWithContext (used in extensions)
|
|
490
|
+
*
|
|
491
|
+
* @param {Object} proto - The prototype object (e.g. MyClass.prototype) to augment.
|
|
492
|
+
*/
|
|
493
|
+
export function applyLogging(proto) {
|
|
494
|
+
Object.defineProperty(proto, 'logger', {
|
|
495
|
+
get() {
|
|
496
|
+
return LoggerManager.logger
|
|
497
|
+
},
|
|
498
|
+
configurable: true,
|
|
499
|
+
})
|
|
500
|
+
|
|
501
|
+
proto.getLogger = () => LoggerManager.logger
|
|
502
|
+
|
|
503
|
+
proto.messageWithContext = (text, context = {}) =>
|
|
504
|
+
Logger.AutoFormattingMessage.attach({ text, ...context })
|
|
505
|
+
|
|
506
|
+
proto.createLogMessage = proto.messageWithContext
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Plain object implementing the Logging mixin interface, for use in non-class contexts
|
|
511
|
+
* (e.g. top-level module functions).
|
|
512
|
+
*/
|
|
513
|
+
export const Logging = {
|
|
514
|
+
get logger() {
|
|
515
|
+
return LoggerManager.logger
|
|
516
|
+
},
|
|
517
|
+
getLogger() {
|
|
518
|
+
return LoggerManager.logger
|
|
519
|
+
},
|
|
520
|
+
messageWithContext(text, context = {}) {
|
|
521
|
+
return Logger.AutoFormattingMessage.attach({ text, ...context })
|
|
522
|
+
},
|
|
523
|
+
createLogMessage(text, context = {}) {
|
|
524
|
+
return Logger.AutoFormattingMessage.attach({ text, ...context })
|
|
525
|
+
},
|
|
526
|
+
}
|