asciidoctor-rhrev 1.5.0 → 1.5.2
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.
|
@@ -182,29 +182,134 @@ function rollupTableCellRevisionEntries(catalog, doc, prefix) {
|
|
|
182
182
|
})
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
// The cell's own <td>/<th>
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
// real Antora builds: this exact demo site has three separate,
|
|
191
|
-
// non-deduped @asciidoctor/core installs (rhrev, the site itself, and
|
|
192
|
-
// baiyibai-extensions, whose cite-it extension wraps Antora's own document
|
|
193
|
-
// loading), one of them (2.2.8 vs 2.2.9 elsewhere) a different version.
|
|
194
|
-
// Antora's own converter/html5.js reads whatever is currently registered
|
|
195
|
-
// as the 'html5' converter and builds a real Opal subclass on top of it;
|
|
196
|
-
// with three non-identical copies of the Opal runtime involved, that
|
|
197
|
-
// crashed (Object.setPrototypeOf called on null or undefined), confirmed
|
|
198
|
-
// against a real local build, not assumed. That is a dependency-hygiene
|
|
199
|
-
// problem in a sibling package, out of scope here.
|
|
185
|
+
// The cell's own <td>/<th> DOES carry the rhrev-changed role, via a
|
|
186
|
+
// converter-level override, patched directly onto the SAME live converter
|
|
187
|
+
// class object Antora's own pipeline is already using for this document
|
|
188
|
+
// (doc.getConverter().$$class, reached through Asciidoctor's own public
|
|
189
|
+
// API), never a separately-required or separately-registered class.
|
|
200
190
|
//
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
191
|
+
// EARLIER ATTEMPT, SUPERSEDED (kept here since the next engineer will
|
|
192
|
+
// otherwise reach for the same idea first): a real Opal subclass of
|
|
193
|
+
// Html5Converter, registered via ConverterFactory, the same way the Ruby
|
|
194
|
+
// gem's PDF converter is overridden. Built and verified working
|
|
195
|
+
// standalone, but broke real Antora builds: this exact demo site had
|
|
196
|
+
// three separate, non-deduped @asciidoctor/core installs (rhrev, the site
|
|
197
|
+
// itself, and baiyibai-extensions, whose cite-it extension wraps Antora's
|
|
198
|
+
// own document loading), one of them a different version. Antora's own
|
|
199
|
+
// converter/html5.js reads whatever is currently registered as the
|
|
200
|
+
// 'html5' converter and builds a real Opal subclass on top of that; with
|
|
201
|
+
// non-identical copies of the Opal runtime involved, defining a SECOND,
|
|
202
|
+
// independent subclass crashed (Object.setPrototypeOf called on null or
|
|
203
|
+
// undefined), confirmed against a real local build, not assumed. A
|
|
204
|
+
// require('@antora/asciidoc-loader') from inside this file cannot avoid
|
|
205
|
+
// that risk either, even now that baiyibai-extensions' own version drift
|
|
206
|
+
// is fixed (1.15.1): this package ships its own @asciidoctor/core copy
|
|
207
|
+
// for its own test suite, so a plain require() here resolves against
|
|
208
|
+
// THIS package's node_modules first, not necessarily the consuming
|
|
209
|
+
// site's, and remains a live, standing risk of resolving a different
|
|
210
|
+
// copy regardless of what any sibling package currently pins.
|
|
211
|
+
//
|
|
212
|
+
// doc.getConverter() sidesteps the whole class of problem: it returns
|
|
213
|
+
// whichever converter object is ACTUALLY resolved for THIS document, in
|
|
214
|
+
// THIS process, by Antora's own pipeline, guaranteed correct by
|
|
215
|
+
// construction rather than by keeping multiple packages' pins in sync.
|
|
216
|
+
// Confirmed directly: asciidoctor.load(text).getConverter() already
|
|
217
|
+
// returns a real, usable converter instance, with a real $$class, before
|
|
218
|
+
// convert() is ever called, meaning it is available in time from inside a
|
|
219
|
+
// treeprocessor (treeprocessors run during load(), before conversion).
|
|
220
|
+
const PATCHED_CONVERT_TABLE = Symbol.for('rhrev.patchedConvertTable')
|
|
221
|
+
|
|
222
|
+
// Idempotency is keyed on the converter CLASS, not the document: a single
|
|
223
|
+
// Antora build converts many documents against the same resolved
|
|
224
|
+
// converter class object, and redefining $convert_table would otherwise
|
|
225
|
+
// re-wrap the previous wrapper on every single page, nesting deeper each
|
|
226
|
+
// time (and, worse, the second pass would capture the FIRST WRAPPER as
|
|
227
|
+
// its own "original", not the real one, once the reference below is
|
|
228
|
+
// overwritten).
|
|
229
|
+
//
|
|
230
|
+
// Captures the real, original $convert_table function reference directly
|
|
231
|
+
// off the class's own prototype before replacing it, and calls that
|
|
232
|
+
// reference directly, rather than Opal.find_super_dispatcher (Antora's
|
|
233
|
+
// own html5.js pattern, copied from initially, then found wrong for this
|
|
234
|
+
// case, confirmed against a real crash: find_super_dispatcher climbs an
|
|
235
|
+
// inheritance chain, correct when Opal.klass creates a genuine SUBCLASS
|
|
236
|
+
// the way Antora's own converter/html5.js does, but this patches the
|
|
237
|
+
// SAME class object in place, no subclass involved, so there is no
|
|
238
|
+
// superclass boundary to dispatch across; the original implementation
|
|
239
|
+
// lives directly on classDef.$$prototype itself, confirmed directly, and
|
|
240
|
+
// Opal.defn below overwrites that exact slot).
|
|
241
|
+
function patchConvertTableForCellTagging (doc) {
|
|
242
|
+
const Opal = global.Opal
|
|
243
|
+
if (!Opal) return
|
|
244
|
+
const converter = doc.getConverter && doc.getConverter()
|
|
245
|
+
const classDef = converter && converter.$$class
|
|
246
|
+
if (!classDef || classDef[PATCHED_CONVERT_TABLE]) return
|
|
247
|
+
const originalConvertTable = classDef.$$prototype['$convert_table']
|
|
248
|
+
if (typeof originalConvertTable !== 'function') return
|
|
249
|
+
classDef[PATCHED_CONVERT_TABLE] = true
|
|
250
|
+
|
|
251
|
+
Opal.defn(classDef, '$convert_table', function convertTableWithCellTags (node) {
|
|
252
|
+
const html = originalConvertTable.call(this, node)
|
|
253
|
+
return typeof html === 'string' ? liftRhrevChangedToCells(html) : html
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Moves the rhrev-changed marker from a marked cell's own nested content
|
|
258
|
+
// (propagateCellContentMarks above) onto its enclosing <td>/<th>, so the
|
|
259
|
+
// site stylesheet highlights the whole cell box, matching the PDF
|
|
260
|
+
// backend's own margin bar spanning the cell's full row height, rather
|
|
261
|
+
// than only the changed paragraph inside it. Operates on the already-
|
|
262
|
+
// rendered HTML string, not the AST: Asciidoctor's real convert_table has
|
|
263
|
+
// no per-cell hook to override cleanly (table rendering is one self-
|
|
264
|
+
// contained method, the same reason the PDF gem needed a Prawn-level
|
|
265
|
+
// patch rather than an ordinary converter override, confirmed against
|
|
266
|
+
// Asciidoctor's own source), so this calls the real, unmodified super()
|
|
267
|
+
// implementation first and only adjusts its output afterward.
|
|
268
|
+
//
|
|
269
|
+
// Finds the nearest PRECEDING <td/<th opening tag before each marker,
|
|
270
|
+
// confirmed against real rendered output (a plain cell's own <p
|
|
271
|
+
// class="tableblock"> vs an a| cell's <div class="content">, both
|
|
272
|
+
// verified). A table nested inside a cell, itself containing its own
|
|
273
|
+
// <td>/<th> tags between the marker and its true enclosing cell, would
|
|
274
|
+
// defeat that heuristic: a known, accepted limitation, not handled.
|
|
275
|
+
function liftRhrevChangedToCells (html) {
|
|
276
|
+
const cellTagRx = /<(td|th)\b[^>]*>/g
|
|
277
|
+
const cellTags = []
|
|
278
|
+
let m
|
|
279
|
+
while ((m = cellTagRx.exec(html))) {
|
|
280
|
+
cellTags.push({ start: m.index, end: m.index + m[0].length, text: m[0] })
|
|
281
|
+
}
|
|
282
|
+
if (cellTags.length === 0) return html
|
|
283
|
+
|
|
284
|
+
const markerRx = /class="[^"]*\brhrev-changed\b[^"]*"/g
|
|
285
|
+
const toPatch = new Set()
|
|
286
|
+
while ((m = markerRx.exec(html))) {
|
|
287
|
+
let owner
|
|
288
|
+
for (const tag of cellTags) {
|
|
289
|
+
if (tag.start <= m.index) owner = tag
|
|
290
|
+
else break
|
|
291
|
+
}
|
|
292
|
+
if (owner) toPatch.add(owner)
|
|
293
|
+
}
|
|
294
|
+
if (toPatch.size === 0) return html
|
|
295
|
+
|
|
296
|
+
let out = ''
|
|
297
|
+
let cursor = 0
|
|
298
|
+
for (const tag of cellTags) {
|
|
299
|
+
if (!toPatch.has(tag)) continue
|
|
300
|
+
out += html.slice(cursor, tag.start)
|
|
301
|
+
out += addRoleClass(tag.text, 'rhrev-changed')
|
|
302
|
+
cursor = tag.end
|
|
303
|
+
}
|
|
304
|
+
out += html.slice(cursor)
|
|
305
|
+
return out
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function addRoleClass (tagText, role) {
|
|
309
|
+
if (new RegExp(`\\bclass="[^"]*\\b${role}\\b[^"]*"`).test(tagText)) return tagText
|
|
310
|
+
if (/\bclass="/.test(tagText)) return tagText.replace(/\bclass="/, `class="${role} `)
|
|
311
|
+
return tagText.replace(/^<(td|th)\b/, `<$1 class="${role}"`)
|
|
312
|
+
}
|
|
208
313
|
|
|
209
314
|
// Catalog for storing revision entries
|
|
210
315
|
class RevisionCatalog {
|
|
@@ -729,6 +834,17 @@ module.exports.register = function (registry, context = {}) {
|
|
|
729
834
|
const changeAttr = prefix + String(revnumber).replace(/\./g, '-')
|
|
730
835
|
const contexts = ROW_LEVEL_CONTEXTS.concat(ROLLUP_CONTEXTS)
|
|
731
836
|
|
|
837
|
+
// A marked table cell was already tagged on its own nested content by
|
|
838
|
+
// propagateCellContentMarks, in the earlier treeprocessor: that walk
|
|
839
|
+
// is scoped to each cell's own, structurally separate inner document,
|
|
840
|
+
// invisible to this treeprocessor's own outer-document findBy below,
|
|
841
|
+
// so `tagged` (which only reflects THIS loop) is never a reliable
|
|
842
|
+
// signal for whether a cell needs patching. Runs unconditionally once
|
|
843
|
+
// change bars are known enabled, before the tagged/return-early check,
|
|
844
|
+
// so a document whose only marked content is a table cell (no other
|
|
845
|
+
// block matched below) still gets its converter patched.
|
|
846
|
+
patchConvertTableForCellTagging(doc)
|
|
847
|
+
|
|
732
848
|
let tagged = false
|
|
733
849
|
doc.findBy((b) => contexts.includes(b.getContext())).forEach((block) => {
|
|
734
850
|
const attrs = block.getAttributes()
|
package/package.json
CHANGED