@antora/assembler
Version:
A JavaScript library that merges AsciiDoc content from multiple pages in an Antora site into assembly files and delegates to an exporter to convert those files to another format, such as PDF.
201 lines (190 loc) • 7.17 kB
JavaScript
const createResourceKey = require('./create-resource-key')
const generateScopedId = require('./generate-scoped-id')
const parseResourceRef = require('./parse-resource-ref')
const { resolveEmbedTarget, resolveLinkTarget } = require('./resolver')
const { NAMED_ID_ATTR_RX } = require('./rx')
function rewriteXrefs (
line,
contentCatalog,
assemblyModel,
ctx,
escapeForInline,
pagesInOutline,
idSeparators,
idLeader,
doc,
sourceLocation
) {
return line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(|[\s\S]*?[^\\])\]/gu, (_, target, text) =>
rewriteXref(
target,
text,
contentCatalog,
assemblyModel,
ctx,
escapeForInline,
pagesInOutline,
idSeparators,
idLeader,
doc,
sourceLocation
)
)
}
function rewriteXref (
target,
text,
contentCatalog,
assemblyModel,
ctx,
escapeForInline,
pagesInOutline,
idSeparators,
idLeader,
doc,
sourceLocation
) {
const rawTarget = target
if (doc && ~target.indexOf('{')) target = doc.$sub_attributes(target, global.Opal.hash({ attribute_missing: 'skip' }))
let fragment, resourceRef
const hashIdx = target.indexOf('#')
if (~hashIdx) {
resourceRef = target.slice(0, hashIdx)
fragment = target.slice(hashIdx + 1)
} else if (target.endsWith('.adoc') || ~target.indexOf('$')) {
resourceRef = target
fragment = ''
} else {
fragment = target
}
if (!resourceRef) return `xref:${idLeader == null ? rawTarget : idLeader + fragment}[${text}]`
const resourceId = parseResourceRef(resourceRef, ctx, 'page', contentCatalog)
const family = resourceId.family
let resource
if (family !== 'page' || !(resource = pagesInOutline?.get(createResourceKey(resourceId)))) {
if ((resource = contentCatalog.getById(resourceId))?.pub) {
const { linkReferenceStyle, pubRoot, siteRoot } = assemblyModel
text ||= resource.asciidoc?.xreftext || rawTarget
if (siteRoot || linkReferenceStyle === 'relative') {
const hash = fragment && !(family === 'page' && fragment === resource.asciidoc.id) ? '#' + fragment : ''
return `${resolveLinkTarget(resource, siteRoot, pubRoot, linkReferenceStyle, escapeForInline)}${hash}[${text}]`
}
if (doc) {
const msg = `Cannot create external ${family} reference in assembly because site URL is unknown: ${rawTarget}`
doc.logger.warn(doc.createLogMessage(msg, { source_location: sourceLocation }))
}
}
const linkAttrlist = text
? (~text.indexOf(',') ? `"${text}"` : text) + ',role=unresolved'
: (~target.indexOf(':') ? `${rawTarget},` : '') + 'role=unresolved'
return `link:${rawTarget}[${linkAttrlist}]`
}
if (fragment === resource.asciidoc.id) fragment = ''
if (
text &&
(assemblyModel.dropExplicitXrefText === 'always' ||
(assemblyModel.dropExplicitXrefText === 'if-redundant' && text === resource.title))
) {
text = ''
}
const componentVersionCtx = { name: ctx.component, version: ctx.version }
const refid = generateScopedId(
resource.src,
componentVersionCtx,
idSeparators,
assemblyModel.filetype,
text.length > 0
)
return `xref:${fragment ? refid + idSeparators.scope + fragment : refid}[${text}]`
}
function rewriteImageAttr (val, contentCatalog, assemblyModel, ctx, assets) {
const match = val.startsWith('image:') && /^image::?(.+?)\[(.*?)\](@|)$/.exec(val)
if (!match) return
const newTarget = rewriteImageRef(match[1], contentCatalog, assemblyModel, ctx, assets)
return newTarget ? `image:${newTarget}[${match[2]}]${match[3]}` : val
}
function rewriteInlineImages (line, contentCatalog, assemblyModel, ctx, assets, escapeForInline, doc) {
return line.replace(/(?<![\\+])image:([^:\s[](?:[^[]*[^\s[])?)\[(|[\s\S]*?[^\\])\]/g, (m, target, attrlist) => {
const newTarget = rewriteImageRef(target, contentCatalog, assemblyModel, ctx, assets, escapeForInline, doc)
return newTarget ? `image:${newTarget}[${attrlist}]` : m
})
}
function rewriteImageRef (target, contentCatalog, assemblyModel, ctx, assets, escapeForInline, doc) {
if (doc && ~target.indexOf('{')) target = doc.$sub_attributes(target, global.Opal.hash({ attribute_missing: 'skip' }))
const image = isResourceRef(target) && contentCatalog.resolveResource(target, ctx, 'image', ['image'])
if (!image?.out) return
let newTarget
const { filetype, embedReferenceStyle, linkReferenceStyle, outDirname, pubRoot, siteRoot } = assemblyModel
if (filetype !== 'html') {
newTarget = resolveEmbedTarget(image, outDirname, embedReferenceStyle, escapeForInline ?? false)
assets.add(image)
} else if (siteRoot || linkReferenceStyle === 'relative') {
newTarget = resolveLinkTarget(image, siteRoot, pubRoot, linkReferenceStyle, escapeForInline ?? false, false)
if (linkReferenceStyle === 'relative') assets.add(image)
}
if (!newTarget) return
return newTarget
}
function rewriteStyleAttribute (block, lines, idx, idLeader, replacementStyle = '') {
let prevLine = lines[idx - 1]
const char0 = prevLine?.charAt()
if (char0) {
if (
(char0 === '.' && /^\.\.?[^ \t.]/.test(prevLine)) ||
(char0 === '[' &&
prevLine.charAt(1) === '[' &&
/^\[\[(?:|[\p{Alpha}_:][\p{Alpha}0-9_\-:.]*(?:, *.+)?)\]\]$/u.test(prevLine))
) {
return rewriteStyleAttribute(block, lines, idx - 1, idLeader, replacementStyle)
}
}
let cellSpec
if (
char0 &&
(char0 === '[' || (block.getDocument().isNested() && (cellSpec = prevLine.match(/^([^[|]*)\| *(\[.+)/)))) &&
prevLine.charAt(prevLine.length - 1) === ']'
) {
if (cellSpec) {
prevLine = cellSpec[2]
cellSpec = cellSpec[1]
} else if (~prevLine.indexOf('id=')) {
prevLine = `[${prevLine.slice(1, -1).replace(NAMED_ID_ATTR_RX, '')}]`
}
let rawStyle
const commaIdx = prevLine.indexOf(',')
if (~commaIdx) {
rawStyle = prevLine.slice(1, commaIdx)
if (~rawStyle.indexOf('=')) rawStyle = undefined
} else if (!~prevLine.indexOf('=')) {
rawStyle = prevLine.slice(1, prevLine.length - 1)
}
if (rawStyle) {
if (~rawStyle.indexOf('#')) {
prevLine = prevLine.replace(/#[^.%,\]]+/, `#${idLeader}${block.getId()}`)
if (replacementStyle) prevLine = prevLine.replace(/^[^#.%,\]]+/, `[${replacementStyle}`)
} else {
prevLine = `[${
replacementStyle ? rawStyle.replace(/^[^.%]*/, replacementStyle) : rawStyle
}#${idLeader}${block.getId()}${prevLine.slice(rawStyle.length + 1)}`
}
} else {
prevLine = `[${replacementStyle}#${idLeader}${block.getId()}${rawStyle == null ? ',' : ''}${prevLine.slice(1)}`
}
if (cellSpec) prevLine = `${cellSpec}|${prevLine}`
lines[idx - 1] = prevLine
} else {
lines.splice(idx, 0, `[${replacementStyle}#${idLeader}${block.getId()}]`)
}
}
function isResourceRef (str) {
return !(~str.indexOf(':') && (~str.indexOf('://') || (str.startsWith('data:') && ~str.indexOf(','))))
}
module.exports = {
rewriteXref,
rewriteXrefs,
rewriteImageAttr,
rewriteImageRef,
rewriteInlineImages,
rewriteStyleAttribute,
}