@antora/assembler 1.0.0-beta.9 → 1.0.0-rc.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/adapters/asciidoctor/jsonl-logger.rb +25 -0
- package/lib/assemble-content.js +93 -70
- package/lib/configure.js +31 -7
- package/lib/constants.js +22 -0
- package/lib/filter-component-versions.js +25 -62
- package/lib/load-config.js +74 -39
- package/lib/log-command.js +20 -0
- package/lib/produce-assembly-file.js +374 -403
- package/lib/produce-assembly-files.js +51 -60
- package/lib/select-mutable-attributes.js +1 -0
- package/lib/util/create-resource-key.js +7 -0
- package/lib/util/generate-scoped-id.js +25 -0
- package/lib/util/matcher.js +150 -0
- package/lib/util/parse-resource-ref.js +36 -0
- package/lib/util/resolver.js +36 -0
- package/lib/util/rewriter.js +206 -0
- package/lib/util/rx.js +5 -0
- package/lib/util/unconvert-inline-asciidoc.js +22 -14
- package/package.json +14 -12
- package/lib/util/compute-out.js +0 -57
- package/lib/util/create-asciidoc-file.js +0 -17
|
@@ -1,15 +1,32 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const createResourceKey = require('./util/create-resource-key')
|
|
4
|
+
const generateScopedId = require('./util/generate-scoped-id')
|
|
5
|
+
const { resolveLinkTarget } = require('./util/resolver')
|
|
6
|
+
const {
|
|
7
|
+
rewriteXrefs,
|
|
8
|
+
rewriteImageAttr,
|
|
9
|
+
rewriteImageRef,
|
|
10
|
+
rewriteInlineImages,
|
|
11
|
+
rewriteStyleAttribute,
|
|
12
|
+
} = require('./util/rewriter')
|
|
5
13
|
const sanitize = require('./util/sanitize')
|
|
6
14
|
const unconvertInlineAsciiDoc = require('./util/unconvert-inline-asciidoc')
|
|
7
15
|
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
16
|
+
const ATTR_ENTRY_RX = /^:(!?[\p{Alpha}0-9_][^:]*):(?: |$)/u
|
|
17
|
+
const BUILT_IN_NAMED_ENTITIES = { amp: '&', apos: "'", gt: '>', lt: '<', nbsp: ' ', quot: '"' }
|
|
18
|
+
const CHAR_REF_RX = /&(?:([a-z][a-z]+\d{0,2})|#(?:(\d{2,6})|x([a-z\d]{2,5})));/g
|
|
19
|
+
const DISCARD_ATTRIBUTE_NAMES = [
|
|
20
|
+
'doctype',
|
|
21
|
+
'leveloffset',
|
|
22
|
+
'preface-title',
|
|
23
|
+
'assembly-header-attributes',
|
|
24
|
+
'assembly-navtitle',
|
|
25
|
+
'assembly-slug',
|
|
26
|
+
'assembly-style',
|
|
27
|
+
'underscore',
|
|
28
|
+
]
|
|
29
|
+
const { NAMED_ID_ATTR_RX } = require('./util/rx')
|
|
13
30
|
|
|
14
31
|
function produceAssemblyFile (
|
|
15
32
|
loadAsciiDoc,
|
|
@@ -23,7 +40,30 @@ function produceAssemblyFile (
|
|
|
23
40
|
) {
|
|
24
41
|
const pagesByUrl = files.reduce((map, it) => (it.src.family === 'page' ? map.set(it.pub.url, it) : map), new Map())
|
|
25
42
|
if (outline.urlType === 'internal' && !pagesByUrl.get(outline.url) && !(outline.items || []).length) return
|
|
26
|
-
const pagesInOutline = selectPagesInOutline(outline, pagesByUrl
|
|
43
|
+
const pagesInOutline = selectPagesInOutline(outline, pagesByUrl)
|
|
44
|
+
const { rootLevel, xmlIds } = assemblyModel
|
|
45
|
+
const idSeparators = {
|
|
46
|
+
prefix:
|
|
47
|
+
'assembler-idprefix' in asciidocConfig.attributes ? (asciidocConfig.attributes['assembler-idprefix'] ?? '') : '_',
|
|
48
|
+
scope: xmlIds ? '---' : ':::',
|
|
49
|
+
coordinate: xmlIds ? '----' : ':',
|
|
50
|
+
generateIdFromTitle: generateIdFromTitle.bind(
|
|
51
|
+
loadAsciiDoc(
|
|
52
|
+
{ contents: Buffer.alloc(0), src: { family: 'page', relative: 'generate-id-from-title.adoc' } },
|
|
53
|
+
undefined,
|
|
54
|
+
asciidocConfig
|
|
55
|
+
)
|
|
56
|
+
),
|
|
57
|
+
}
|
|
58
|
+
const { name: component, version } = componentVersion
|
|
59
|
+
asciidocConfig = prepareAsciiDocConfig(
|
|
60
|
+
contentCatalog,
|
|
61
|
+
{ component, version },
|
|
62
|
+
pagesInOutline,
|
|
63
|
+
asciidocConfig,
|
|
64
|
+
assemblyModel,
|
|
65
|
+
idSeparators
|
|
66
|
+
)
|
|
27
67
|
const buffer = mergeAsciiDoc(
|
|
28
68
|
loadAsciiDoc,
|
|
29
69
|
contentCatalog,
|
|
@@ -32,61 +72,87 @@ function produceAssemblyFile (
|
|
|
32
72
|
outline,
|
|
33
73
|
files,
|
|
34
74
|
pagesInOutline,
|
|
75
|
+
idSeparators,
|
|
35
76
|
asciidocConfig,
|
|
36
77
|
mutableAttributes,
|
|
37
78
|
assemblyModel
|
|
38
79
|
)
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return createAsciiDocFile(contentCatalog, {
|
|
80
|
+
const stem =
|
|
81
|
+
(buffer.slug ? sanitizeSlug(buffer.slug) : generateSlug(rootLevel === 0 ? 'index' : buffer.navtitle)) ||
|
|
82
|
+
'export-' + (assemblyModel.stemSeq = (assemblyModel.exportSeq ?? 0) + 1)
|
|
83
|
+
const downloadStem = [component, version, stem === 'index' ? '' : stem].filter((it) => it).join('-')
|
|
84
|
+
const file = contentCatalog.createFile({
|
|
45
85
|
asciidoc: asciidocConfig,
|
|
46
86
|
assembler: { assembled: pagesInOutline.assembled, downloadStem, rootLevel },
|
|
47
87
|
contents: Buffer.from(buffer.join('\n') + '\n'),
|
|
48
|
-
src: {
|
|
49
|
-
|
|
50
|
-
version: componentVersion.version,
|
|
51
|
-
module: 'ROOT',
|
|
52
|
-
family: 'export',
|
|
53
|
-
relative: stem + '.adoc',
|
|
54
|
-
},
|
|
88
|
+
src: { component, version, componentVersion, module: 'ROOT', family: 'export', relative: stem + '.adoc' },
|
|
89
|
+
pub: false,
|
|
55
90
|
})
|
|
91
|
+
file.path = file.out.path // use out path as file path so assets can easily be published to same hierarchy
|
|
92
|
+
delete file.out
|
|
93
|
+
return file
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function prepareAsciiDocConfig (contentCatalog, ctx, pagesInOutline, asciidocConfig, assemblyModel, idSeparators) {
|
|
97
|
+
let attributesModified
|
|
98
|
+
const configShared = asciidocConfig.$shared
|
|
99
|
+
const sharedAttributes = asciidocConfig.attributes
|
|
100
|
+
if (!configShared) {
|
|
101
|
+
if (configShared == null) {
|
|
102
|
+
const assets = pagesInOutline.assembled.assets
|
|
103
|
+
for (const [name, val] of Object.entries(sharedAttributes)) {
|
|
104
|
+
if (!(typeof val === 'string' && ~val.indexOf(':'))) continue
|
|
105
|
+
let newVal
|
|
106
|
+
if (!name.endsWith('-image') || !(newVal = rewriteImageAttr(val, contentCatalog, assemblyModel, ctx, assets))) {
|
|
107
|
+
if (~(newVal = val).indexOf('image:')) {
|
|
108
|
+
newVal = rewriteInlineImages(newVal, contentCatalog, assemblyModel, ctx, assets)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (newVal !== val) sharedAttributes[name] = newVal
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
for (const [name, val] of Object.entries(sharedAttributes)) {
|
|
115
|
+
if (!(typeof val === 'string' && ~val.indexOf(':'))) continue
|
|
116
|
+
if (~val.indexOf('xref:')) {
|
|
117
|
+
const newVal = rewriteXrefs(val, contentCatalog, assemblyModel, ctx, false, pagesInOutline, idSeparators)
|
|
118
|
+
if (newVal !== val) (attributesModified ??= {})[name] = newVal
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (configShared == null) Object.defineProperty(asciidocConfig, '$shared', { value: attributesModified == null })
|
|
122
|
+
}
|
|
123
|
+
return Object.assign({}, asciidocConfig, { attributes: Object.assign({}, sharedAttributes, attributesModified) })
|
|
56
124
|
}
|
|
57
125
|
|
|
58
126
|
function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
|
|
59
127
|
const doctype = assemblyModel.doctype ?? 'book'
|
|
60
128
|
const navtitlePlain = sanitize(navtitle)
|
|
61
129
|
const navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
62
|
-
|
|
63
|
-
if (navtitlePlain !== componentVersion.title) doctitle = `${componentVersion.title}: ${doctitle}`
|
|
130
|
+
const doctitle = (componentVersion.title === navtitlePlain ? '' : componentVersion.title + ': ') + navtitleAsciiDoc
|
|
64
131
|
const version = componentVersion.version === 'master' ? '' : componentVersion.version
|
|
132
|
+
const displayVersion = componentVersion.displayVersion
|
|
65
133
|
const buffer = [
|
|
66
134
|
`= ${doctitle}`,
|
|
67
|
-
...(version ? [`:revnumber: ${
|
|
135
|
+
...(version ? [`:revnumber: ${displayVersion}`] : []),
|
|
68
136
|
...(doctype === 'article' ? [] : [`:doctype: ${doctype ?? 'book'}`]),
|
|
69
137
|
':underscore: _',
|
|
70
138
|
// Q: should we pass these via the CLI so they cannot be modified?
|
|
71
139
|
`:page-component-name: ${componentVersion.name}`,
|
|
72
140
|
`:page-component-version:${version ? ' ' + version : ''}`,
|
|
73
141
|
':page-version: {page-component-version}',
|
|
74
|
-
`:page-component-display-version: ${
|
|
142
|
+
`:page-component-display-version: ${displayVersion}`,
|
|
75
143
|
`:page-component-title: ${componentVersion.title}`,
|
|
76
144
|
]
|
|
77
145
|
return Object.assign(buffer, { navtitle })
|
|
78
146
|
}
|
|
79
147
|
|
|
80
|
-
function selectPagesInOutline (outlineEntry, pagesByUrl,
|
|
148
|
+
function selectPagesInOutline (outlineEntry, pagesByUrl, accum) {
|
|
81
149
|
accum ??= Object.assign(new Map(), { assembled: { pages: new Map(), assets: new Set() } })
|
|
82
150
|
const page = outlineEntry.urlType === 'internal' ? pagesByUrl.get(outlineEntry.url) : undefined
|
|
83
151
|
if (page) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
accum.set(page.pub.url, page)
|
|
152
|
+
accum.set(createResourceKey(page.src), page)
|
|
153
|
+
accum.set(outlineEntry.url, page)
|
|
88
154
|
}
|
|
89
|
-
for (const item of outlineEntry.items || []) selectPagesInOutline(item, pagesByUrl,
|
|
155
|
+
for (const item of outlineEntry.items || []) selectPagesInOutline(item, pagesByUrl, accum)
|
|
90
156
|
return accum
|
|
91
157
|
}
|
|
92
158
|
|
|
@@ -98,6 +164,7 @@ function mergeAsciiDoc (
|
|
|
98
164
|
outlineEntry,
|
|
99
165
|
files,
|
|
100
166
|
pagesInOutline,
|
|
167
|
+
idSeparators,
|
|
101
168
|
asciidocConfig,
|
|
102
169
|
mutableAttributes,
|
|
103
170
|
assemblyModel,
|
|
@@ -110,94 +177,84 @@ function mergeAsciiDoc (
|
|
|
110
177
|
buffer.inBody ??= false
|
|
111
178
|
return buffer
|
|
112
179
|
}
|
|
113
|
-
let navtitle = outlineEntry.content
|
|
180
|
+
let navtitle = outlineEntry.navtitle ?? outlineEntry.content
|
|
114
181
|
let navtitlePlain = sanitize(navtitle)
|
|
115
182
|
let navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
116
|
-
const { items = [], unresolved, urlType, url } = outlineEntry
|
|
117
|
-
const {
|
|
118
|
-
|
|
119
|
-
filetype,
|
|
120
|
-
embedReferenceStyle: embedRefStyle,
|
|
121
|
-
linkReferenceStyle: linkRefStyle,
|
|
122
|
-
outDirname,
|
|
123
|
-
siteRoot,
|
|
124
|
-
xmlIds,
|
|
125
|
-
} = assemblyModel
|
|
183
|
+
const { items = [], roles = [], unresolved, urlType, url } = outlineEntry
|
|
184
|
+
const { filetype, linkReferenceStyle, pubRoot, siteRoot, logger, rootLevel } = assemblyModel
|
|
185
|
+
const assembled = pagesInOutline.assembled
|
|
126
186
|
// FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
|
|
127
187
|
const page = urlType === 'internal' && !unresolved ? pagesInOutline.get(url) : undefined
|
|
128
188
|
const atDocumentRoot = !buffer.inBody
|
|
129
|
-
const atBookRoot = atDocumentRoot && !level && doctype === 'book' && (supportsParts = true)
|
|
189
|
+
const atBookRoot = atDocumentRoot && !level && assemblyModel.doctype === 'book' && (supportsParts = true)
|
|
130
190
|
const hasItems = items.length > 0
|
|
131
|
-
|
|
132
|
-
const idSeparator = xmlIds ? '-' : ':'
|
|
133
|
-
const idScopeSeparator = idSeparator.repeat(3)
|
|
134
|
-
const idCoordinateSeparator = idSeparator === '-' ? '----' : idSeparator
|
|
135
|
-
if (page && !pagesInOutline.assembled.pages.has(page)) {
|
|
191
|
+
if (page && !assembled.pages.has(page)) {
|
|
136
192
|
const contents = page.src.contents
|
|
137
193
|
if (contents == null) {
|
|
138
194
|
buffer.inBody ??= false
|
|
139
195
|
return buffer
|
|
140
196
|
}
|
|
197
|
+
const isRootPage = atDocumentRoot && !level
|
|
141
198
|
const { component, version, module: module_, relative, origin, mediaType } = page.src
|
|
142
|
-
const topicPrefix = ~relative.indexOf('/') ? path.dirname(relative) + '/' : ''
|
|
143
199
|
const pageAsAsciiDoc = new page.constructor(
|
|
144
200
|
Object.assign({}, page, { contents: trimAsciiDoc(contents), mediaType })
|
|
145
201
|
)
|
|
146
202
|
const doc = loadAsciiDoc(pageAsAsciiDoc, contentCatalog, asciidocConfig)
|
|
147
|
-
|
|
203
|
+
doc.logger = logger
|
|
204
|
+
? Object.assign(doc.getLogger().$dup(), {
|
|
205
|
+
delegate: {
|
|
206
|
+
warn () {
|
|
207
|
+
return logger.warn.apply(logger, arguments)
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
})
|
|
211
|
+
: doc.getLogger()
|
|
212
|
+
doc.source_header_attributes ??= doc.parent.$to_h()
|
|
213
|
+
if (isRootPage && doc.hasAttribute('assembly-slug')) buffer.slug = doc.getAttribute('assembly-slug')
|
|
214
|
+
let hasAssemblyNavtitleAttr
|
|
215
|
+
if ((hasAssemblyNavtitleAttr = !!doc.getAttribute('assembly-navtitle'))) {
|
|
148
216
|
navtitleAsciiDoc = doc.getAttribute('assembly-navtitle')
|
|
149
217
|
navtitlePlain = sanitize((navtitle = doc.$apply_reftext_subs(navtitleAsciiDoc)))
|
|
150
218
|
if (buffer.inBody == null) {
|
|
151
219
|
buffer.navtitle = navtitle
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (navtitlePlain !== componentVersion.title) doctitle = `${componentVersion.title}: ${doctitle}`
|
|
220
|
+
if (rootLevel) {
|
|
221
|
+
const doctitle =
|
|
222
|
+
(componentVersion.title === navtitlePlain ? '' : componentVersion.title + ': ') + navtitleAsciiDoc
|
|
156
223
|
buffer[0] = `= ${doctitle}`
|
|
157
224
|
}
|
|
158
225
|
}
|
|
159
226
|
}
|
|
160
|
-
if (atDocumentRoot) {
|
|
161
|
-
const authors = doc.getAuthors()
|
|
162
|
-
if (authors.length) {
|
|
163
|
-
const authorLine = authors
|
|
164
|
-
.map((author) => {
|
|
165
|
-
const email = author.getEmail()
|
|
166
|
-
return email ? `${author.getName()} <${author.getEmail()}>` : author.getName()
|
|
167
|
-
})
|
|
168
|
-
.join('; ')
|
|
169
|
-
buffer.splice(1, 0, authorLine)
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
227
|
// NOTE: in Antora, docname is relative src path from module without file extension
|
|
173
228
|
const docname = doc.getAttribute('docname')
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
let idScope = docnameForId
|
|
177
|
-
let idPrefix
|
|
178
|
-
if (qualifyId) {
|
|
179
|
-
idScope = [component, module_ === 'ROOT' ? '' : module_, idScope].join(idCoordinateSeparator)
|
|
180
|
-
} else if (module_ !== 'ROOT') {
|
|
181
|
-
idScope = module_ + idCoordinateSeparator + idScope
|
|
182
|
-
} else if (ReservedIdNames.includes(docnameForId)) {
|
|
183
|
-
idScope = idPrefix = idScope + idScopeSeparator
|
|
184
|
-
}
|
|
185
|
-
idPrefix ??= idScope + idScopeSeparator
|
|
229
|
+
const pageId = generateScopedId(page.src, componentVersion, idSeparators, filetype)
|
|
230
|
+
const pageIdLeader = pageId + idSeparators.scope
|
|
186
231
|
let pageFragment = ''
|
|
187
|
-
let pageRoles =
|
|
232
|
+
let pageRoles = []
|
|
188
233
|
let pageStyle = doc.getAttribute('assembly-style', '')
|
|
234
|
+
if (
|
|
235
|
+
!pageStyle &&
|
|
236
|
+
atBookRoot &&
|
|
237
|
+
rootLevel === 0 &&
|
|
238
|
+
assemblyModel.sectionMergeStrategy === 'fuse' &&
|
|
239
|
+
!doc.source_header_attributes['$key?']('assembly-style')
|
|
240
|
+
) {
|
|
241
|
+
pageStyle = 'preface'
|
|
242
|
+
}
|
|
189
243
|
let part
|
|
190
244
|
if ((part = pageStyle === 'part')) {
|
|
191
245
|
pageStyle = ''
|
|
192
246
|
if (!supportsParts) part = undefined
|
|
193
247
|
} else if ((part = pageStyle.endsWith('-part'))) {
|
|
194
|
-
pageStyle = pageStyle.
|
|
248
|
+
pageStyle = pageStyle.substring(0, pageStyle.length - 5)
|
|
195
249
|
if (!supportsParts) part = undefined
|
|
196
250
|
}
|
|
197
251
|
let nextSectionLevel = 1
|
|
198
252
|
const lines = doc.getSourceLines()
|
|
199
253
|
const ignoreLines = []
|
|
200
|
-
buffer.inBody
|
|
254
|
+
if (!buffer.inBody) {
|
|
255
|
+
buffer.inBody = true
|
|
256
|
+
buffer.endHeaderIdx = buffer.length - 1
|
|
257
|
+
}
|
|
201
258
|
buffer.push('')
|
|
202
259
|
buffer.push(`:docname: ${docname}`)
|
|
203
260
|
if (component !== lastComponentVersion.name) {
|
|
@@ -221,81 +278,153 @@ function mergeAsciiDoc (
|
|
|
221
278
|
buffer.push(`:page-origin-refname: ${origin.branch || origin.tag}`)
|
|
222
279
|
buffer.push(`:page-origin-reftype: ${origin.branch ? 'branch' : 'tag'}`)
|
|
223
280
|
buffer.push(`:page-origin-refhash: ${origin.worktree ? '(worktree)' : origin.refhash}`)
|
|
224
|
-
|
|
281
|
+
let headerHasBlockAttrs
|
|
282
|
+
if (doc.hasHeader()) {
|
|
283
|
+
for (const entry of processDocumentHeader(doc, lines, ignoreLines, isRootPage)) {
|
|
284
|
+
if (entry.type === 'author_line') {
|
|
285
|
+
buffer.splice(1, 0, entry.lines[0])
|
|
286
|
+
buffer.endHeaderIdx += 1
|
|
287
|
+
} else if (entry.type === 'attribute_entry') {
|
|
288
|
+
const name = entry.name
|
|
289
|
+
if (!entry.negated && !doc.isAttributeLocked(name)) {
|
|
290
|
+
let val, newVal
|
|
291
|
+
if (
|
|
292
|
+
name.endsWith('-image') &&
|
|
293
|
+
(val = doc.getAttribute(name)) &&
|
|
294
|
+
(newVal = rewriteImageAttr(val, contentCatalog, assemblyModel, page.src, assembled.assets))
|
|
295
|
+
) {
|
|
296
|
+
if (newVal !== val) entry.lines = [`:${name}: ${newVal}`]
|
|
297
|
+
} else if (~(val = entry.lines.join('\n').substring(name.length + 3)).indexOf(':')) {
|
|
298
|
+
newVal = val
|
|
299
|
+
if (~newVal.indexOf('image:')) {
|
|
300
|
+
newVal = rewriteInlineImages(
|
|
301
|
+
newVal,
|
|
302
|
+
contentCatalog,
|
|
303
|
+
assemblyModel,
|
|
304
|
+
page.src,
|
|
305
|
+
assembled.assets,
|
|
306
|
+
false,
|
|
307
|
+
doc
|
|
308
|
+
)
|
|
309
|
+
}
|
|
310
|
+
if (~newVal.indexOf('xref:')) {
|
|
311
|
+
newVal = rewriteXrefs(
|
|
312
|
+
newVal,
|
|
313
|
+
contentCatalog,
|
|
314
|
+
assemblyModel,
|
|
315
|
+
page.src,
|
|
316
|
+
false,
|
|
317
|
+
pagesInOutline,
|
|
318
|
+
idSeparators,
|
|
319
|
+
pageIdLeader,
|
|
320
|
+
doc,
|
|
321
|
+
{ file: relative, lineno: entry.lineno }
|
|
322
|
+
)
|
|
323
|
+
}
|
|
324
|
+
if (newVal !== val) entry.lines = `:${entry.name}: ${newVal}`.split('\n')
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (entry.promote) {
|
|
328
|
+
buffer.splice(buffer.endHeaderIdx + 1, 0, ...entry.lines)
|
|
329
|
+
buffer.endHeaderIdx += entry.lines.length
|
|
330
|
+
} else {
|
|
331
|
+
buffer.push(...entry.lines)
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
if (entry.type === 'attrlist') headerHasBlockAttrs = true
|
|
335
|
+
buffer.push(...entry.lines)
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
pageRoles = doc.getRoles()
|
|
339
|
+
}
|
|
340
|
+
if (roles.length) pageRoles = pageRoles.concat(roles[0] === 'page' ? roles.slice(1) : roles)
|
|
225
341
|
let heading
|
|
226
342
|
if (pageStyle && (part && level === 1 ? (level = 0) : level) === 0) {
|
|
227
343
|
let htitleAsciiDoc = navtitleAsciiDoc
|
|
228
|
-
let
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
344
|
+
let notitle
|
|
345
|
+
if (pageStyle === 'preface') {
|
|
346
|
+
const hasPrefaceTitleAttr =
|
|
347
|
+
doc.isAttribute('preface-title') ||
|
|
348
|
+
doc.source_header_attributes['$key?']('preface-title') ||
|
|
349
|
+
doc.isAttributeLocked('preface-title')
|
|
350
|
+
const htitleOverride = hasPrefaceTitleAttr
|
|
351
|
+
? doc.getAttribute('preface-title', '')
|
|
352
|
+
: atDocumentRoot && (rootLevel ? true : !hasAssemblyNavtitleAttr)
|
|
353
|
+
? ''
|
|
354
|
+
: undefined
|
|
355
|
+
if (htitleOverride != null) {
|
|
356
|
+
if (htitleOverride === '') {
|
|
357
|
+
if (headerHasBlockAttrs || doc.hasSections()) {
|
|
358
|
+
htitleAsciiDoc = '{empty}'
|
|
359
|
+
notitle = true
|
|
360
|
+
} else {
|
|
361
|
+
if (hasPrefaceTitleAttr) buffer.splice(++buffer.endHeaderIdx, 0, ':preface-title:')
|
|
362
|
+
htitleAsciiDoc = undefined
|
|
363
|
+
}
|
|
364
|
+
} else if (hasPrefaceTitleAttr && !(headerHasBlockAttrs || doc.hasSections())) {
|
|
365
|
+
buffer.splice(++buffer.endHeaderIdx, 0, `:preface-title: ${unconvertInlineAsciiDoc(htitleOverride)}`)
|
|
366
|
+
htitleAsciiDoc = undefined
|
|
367
|
+
} else {
|
|
368
|
+
htitleAsciiDoc = unconvertInlineAsciiDoc(htitleOverride)
|
|
369
|
+
}
|
|
370
|
+
}
|
|
236
371
|
}
|
|
237
|
-
if (
|
|
238
|
-
assemblyModel = Object.assign({}, assemblyModel, { sectionMergeStrategy: 'discrete' })
|
|
239
|
-
} else {
|
|
372
|
+
if (htitleAsciiDoc != null) {
|
|
240
373
|
if (atDocumentRoot) {
|
|
241
|
-
|
|
242
|
-
|
|
374
|
+
// NOTE use pageStyle as fallback because this section must have a unique ID
|
|
375
|
+
pageFragment = `#${pageIdLeader}${doc.getId() ?? pageStyle}`
|
|
376
|
+
buffer.unshift(`[#${pageId}]`)
|
|
243
377
|
} else {
|
|
244
|
-
pageFragment = `#${
|
|
378
|
+
pageFragment = `#${pageId}`
|
|
245
379
|
}
|
|
246
380
|
heading = { title: htitleAsciiDoc, level: part ? 1 : 2 }
|
|
247
|
-
|
|
381
|
+
if (notitle) heading.notitle = true
|
|
382
|
+
nextSectionLevel = 2 // next section level is 2, even for special part
|
|
248
383
|
}
|
|
249
384
|
} else if (level) {
|
|
250
|
-
if (atDocumentRoot && navtitlePlain === componentVersion.title) {
|
|
385
|
+
if (atDocumentRoot && rootLevel === 0 && navtitlePlain === componentVersion.title && !hasAssemblyNavtitleAttr) {
|
|
251
386
|
level--
|
|
252
387
|
} else {
|
|
253
|
-
pageFragment = `#${
|
|
388
|
+
pageFragment = `#${pageId}`
|
|
254
389
|
if (part && level === 1) level--
|
|
255
390
|
if ((heading = { title: navtitleAsciiDoc, level: level + 1 }).level > 6) {
|
|
256
391
|
Object.assign(heading, { level: 6, style: `discrete.h${heading.level}` })
|
|
257
392
|
}
|
|
258
393
|
}
|
|
394
|
+
} else if (atDocumentRoot && rootLevel === 0 && hasAssemblyNavtitleAttr) {
|
|
395
|
+
pageFragment = `#${pageId}`
|
|
396
|
+
heading = { title: navtitleAsciiDoc, level: (nextSectionLevel = part ? 1 : 2) }
|
|
397
|
+
} else if (atBookRoot) {
|
|
398
|
+
nextSectionLevel = undefined
|
|
259
399
|
}
|
|
400
|
+
let enclosed
|
|
260
401
|
if (heading) {
|
|
261
|
-
|
|
402
|
+
const rolesAttr = pageRoles.reduce((str, it) => str + '.' + it, '')
|
|
403
|
+
const notitleAttrs = heading.notitle ? '%notitle,toclevels=0,outlinelevels=0' : ''
|
|
404
|
+
buffer.push(`[${heading.style ?? pageStyle}${pageFragment}${rolesAttr}${notitleAttrs}]`)
|
|
262
405
|
buffer.push(`${'='.repeat(heading.level)} ${heading.title}`)
|
|
263
|
-
} else
|
|
264
|
-
buffer.unshift(`[#${
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (doc.isAttributeLocked('sectids')) {
|
|
278
|
-
syntheticId = `__object-id-${getObjectId(outlineEntry)}`
|
|
406
|
+
} else {
|
|
407
|
+
if (atDocumentRoot) buffer.unshift(`[#${pageId}]`)
|
|
408
|
+
if (assemblyModel.sectionMergeStrategy === 'enclose' && hasItems && doc.hasSections()) {
|
|
409
|
+
enclosed = true
|
|
410
|
+
// TODO: make overview section title configurable
|
|
411
|
+
//let overviewTitle = doc.getDocumentTitle()
|
|
412
|
+
//if (overviewTitle === navtitle) overviewTitle = doc.getAttribute('overview-title', 'Overview')
|
|
413
|
+
const overviewTitle = doc.getAttribute('overview-title', 'Overview')
|
|
414
|
+
const syntheticId = idSeparators.generateIdFromTitle(navtitleAsciiDoc, idSeparators)
|
|
415
|
+
let hlevel = level + 2
|
|
416
|
+
if (hlevel > 6) {
|
|
417
|
+
const blockStyle = `discrete.h${hlevel}`
|
|
418
|
+
hlevel = 6
|
|
419
|
+
buffer.push(`[${blockStyle}#${syntheticId}]`)
|
|
279
420
|
} else {
|
|
280
|
-
buffer.push(
|
|
281
|
-
toggleSectids = true
|
|
421
|
+
buffer.push(`[#${syntheticId}]`)
|
|
282
422
|
}
|
|
423
|
+
buffer.push(`${'='.repeat(hlevel)} ${overviewTitle}`)
|
|
283
424
|
}
|
|
284
|
-
let hlevel = level + 2
|
|
285
|
-
if (hlevel > 6) {
|
|
286
|
-
const blockStyle = `discrete.h${hlevel}`
|
|
287
|
-
hlevel = 6
|
|
288
|
-
buffer.push(syntheticId ? `[${blockStyle}#${syntheticId}]` : `[${blockStyle}]`)
|
|
289
|
-
} else if (syntheticId) {
|
|
290
|
-
buffer.push(`[#${syntheticId}]`)
|
|
291
|
-
}
|
|
292
|
-
buffer.push(`${'='.repeat(hlevel)} ${overviewTitle}`)
|
|
293
|
-
if (toggleSectids) buffer.push(':sectids:')
|
|
294
|
-
}
|
|
295
|
-
pagesInOutline.assembled.pages.set(page, pageFragment)
|
|
296
|
-
if (doc.hasSections()) {
|
|
297
|
-
fixSectionLevels(doc.getSections(), atBookRoot && !pageStyle ? undefined : nextSectionLevel)
|
|
298
425
|
}
|
|
426
|
+
assembled.pages.set(page, pageFragment)
|
|
427
|
+
if (doc.hasSections()) fixSectionLevels(doc.getSections(), nextSectionLevel)
|
|
299
428
|
const allBlocks = doc.findBy({ traverse_documents: true }, (it) =>
|
|
300
429
|
it.getContext() === 'document'
|
|
301
430
|
? it.getDocument().isNested()
|
|
@@ -366,7 +495,7 @@ function mergeAsciiDoc (
|
|
|
366
495
|
if (
|
|
367
496
|
line.charAt() === ':' &&
|
|
368
497
|
~line.indexOf(':', 2) &&
|
|
369
|
-
(line.match(
|
|
498
|
+
(line.match(ATTR_ENTRY_RX) || ['', ''])[1].replace('!', '') === 'leveloffset'
|
|
370
499
|
) {
|
|
371
500
|
if (lines[idx - 1] === '') lines[idx - 1] = undefined
|
|
372
501
|
lines[idx] = undefined
|
|
@@ -378,111 +507,31 @@ function mergeAsciiDoc (
|
|
|
378
507
|
if (!refs['$key?'](refid) && (~refid.indexOf(' ') || refid.toLowerCase() !== refid)) {
|
|
379
508
|
if ((refid = doc.$resolve_id(refid))['$nil?']()) return m
|
|
380
509
|
}
|
|
381
|
-
return `<<${
|
|
510
|
+
return `<<${pageIdLeader}${refid}${text ? ',' + text : ''}>>`
|
|
382
511
|
})
|
|
383
512
|
}
|
|
384
513
|
// NOTE: the next check takes care of inline and block anchors
|
|
385
514
|
if (~line.indexOf('[[')) {
|
|
386
|
-
line = line.replace(/\[\[([\p{Alpha}_:][\p{Alpha}0-9_\-:.]*)(|, *.+?)\]\]/gu,
|
|
387
|
-
|
|
388
|
-
if (~line.indexOf('xref:')) {
|
|
389
|
-
// Q: should we allow : as first character of target?
|
|
390
|
-
line = line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(|.*?[^\\])\]/gu, (m, target, text) => {
|
|
391
|
-
let relativePart, fragment, resource, isPage
|
|
392
|
-
const hashIdx = target.indexOf('#')
|
|
393
|
-
const dollarIdx = target.indexOf('$')
|
|
394
|
-
if (~hashIdx) {
|
|
395
|
-
relativePart = target.slice(0, hashIdx)
|
|
396
|
-
fragment = target.slice(hashIdx + 1)
|
|
397
|
-
} else if (~dollarIdx || target.endsWith('.adoc')) {
|
|
398
|
-
relativePart = target
|
|
399
|
-
fragment = ''
|
|
400
|
-
} else {
|
|
401
|
-
fragment = target
|
|
402
|
-
}
|
|
403
|
-
// Q: should we validate the internal ID here?
|
|
404
|
-
if (!relativePart) return `xref:${idPrefix}${fragment}[${text}]`
|
|
405
|
-
if (~dollarIdx) {
|
|
406
|
-
if (relativePart.slice(dollarIdx).startsWith('$./')) {
|
|
407
|
-
relativePart = relativePart.slice(0, dollarIdx + 1) + topicPrefix + relativePart.slice(dollarIdx + 3)
|
|
408
|
-
}
|
|
409
|
-
if ((isPage = /\bpage\$/.test(relativePart))) relativePart = relativePart.replace('page$', '')
|
|
410
|
-
} else {
|
|
411
|
-
isPage = true
|
|
412
|
-
if (relativePart.startsWith('./')) relativePart = topicPrefix + relativePart.slice(2)
|
|
413
|
-
if (~hashIdx && !relativePart.endsWith('.adoc')) relativePart += '.adoc'
|
|
414
|
-
}
|
|
415
|
-
if (!isPage || ~relativePart.indexOf('@') || /:.*:/.test(relativePart)) {
|
|
416
|
-
if (siteRoot && (resource = contentCatalog.resolveResource(relativePart, page.src, 'page'))?.pub) {
|
|
417
|
-
text ||= resource.asciidoc?.xreftext || target
|
|
418
|
-
return `${resolveLinkTarget(resource, siteRoot, pubRoot, linkRefStyle)}${fragment && '#' + fragment}[${text}]`
|
|
419
|
-
}
|
|
420
|
-
// TODO: handle unresolved resource better
|
|
421
|
-
return m
|
|
422
|
-
}
|
|
423
|
-
let targetModule
|
|
424
|
-
const colonIdx = relativePart.indexOf(':')
|
|
425
|
-
if (~colonIdx) {
|
|
426
|
-
targetModule = relativePart.slice(0, colonIdx)
|
|
427
|
-
relativePart = relativePart.slice(colonIdx + 1)
|
|
428
|
-
if (relativePart.startsWith('./')) relativePart = topicPrefix + relativePart.slice(2)
|
|
429
|
-
} else {
|
|
430
|
-
targetModule = module_
|
|
431
|
-
}
|
|
432
|
-
const pageResourceRef = targetModule === 'ROOT' ? relativePart : `${targetModule}:${relativePart}`
|
|
433
|
-
if (!(resource = pagesInOutline.get(pageResourceRef))) {
|
|
434
|
-
if (siteRoot && (resource = contentCatalog.resolvePage(pageResourceRef, page.src)) && resource.out) {
|
|
435
|
-
text ||= resource.asciidoc?.xreftext || target
|
|
436
|
-
return `${resolveLinkTarget(resource, siteRoot, pubRoot, linkRefStyle)}${fragment && '#' + fragment}[${text}]`
|
|
437
|
-
}
|
|
438
|
-
// TODO: handle unresolved page better
|
|
439
|
-
return m
|
|
440
|
-
}
|
|
441
|
-
if (targetModule !== 'ROOT') relativePart = `${targetModule}${idCoordinateSeparator}${relativePart}`
|
|
442
|
-
relativePart = relativePart.replace(/\.adoc$/, '').replace(/[/.]/g, '-')
|
|
443
|
-
if (fragment === resource.asciidoc.id) fragment = ''
|
|
444
|
-
const refid = fragment
|
|
445
|
-
? `${relativePart}${idScopeSeparator}${fragment}`
|
|
446
|
-
: relativePart + (ReservedIdNames.includes(relativePart) ? idScopeSeparator : '')
|
|
447
|
-
if (
|
|
448
|
-
text &&
|
|
449
|
-
(assemblyModel.dropExplicitXrefText === 'always' ||
|
|
450
|
-
(assemblyModel.dropExplicitXrefText === 'if-redundant' && text === resource.title))
|
|
451
|
-
) {
|
|
452
|
-
text = ''
|
|
453
|
-
}
|
|
454
|
-
return `xref:${refid}[${text}]`
|
|
515
|
+
line = line.replace(/\[\[([\p{Alpha}_:][\p{Alpha}0-9_\-:.]*)(|, *.+?)\]\]/gu, (_, refid, text) => {
|
|
516
|
+
return `[[${pageIdLeader}${refid}${text}]]`
|
|
455
517
|
})
|
|
456
518
|
}
|
|
457
|
-
if (~line.indexOf('
|
|
458
|
-
line =
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
})
|
|
519
|
+
if (~line.indexOf('xref:')) {
|
|
520
|
+
line = rewriteXrefs(
|
|
521
|
+
line,
|
|
522
|
+
contentCatalog,
|
|
523
|
+
assemblyModel,
|
|
524
|
+
page.src,
|
|
525
|
+
true,
|
|
526
|
+
pagesInOutline,
|
|
527
|
+
idSeparators,
|
|
528
|
+
pageIdLeader,
|
|
529
|
+
doc,
|
|
530
|
+
{ file: relative, lineno: idx + 1 }
|
|
531
|
+
)
|
|
471
532
|
}
|
|
472
533
|
if (~line.indexOf('image:') && !line.startsWith('image::')) {
|
|
473
|
-
line = line.
|
|
474
|
-
if (isResourceSpec(target)) {
|
|
475
|
-
const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
|
|
476
|
-
// TODO: handle (or report) unresolved image better
|
|
477
|
-
if (image?.out && (filetype !== 'html' || siteRoot)) {
|
|
478
|
-
pagesInOutline.assembled.assets.add(image)
|
|
479
|
-
return filetype === 'html'
|
|
480
|
-
? `image:${resolveLinkTarget(image, siteRoot, pubRoot, linkRefStyle)}[${attrlist}]`
|
|
481
|
-
: `image:${resolveEmbedTarget(image, outDirname, embedRefStyle, true)}[${attrlist}]`
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
return m
|
|
485
|
-
})
|
|
534
|
+
line = rewriteInlineImages(line, contentCatalog, assemblyModel, page.src, assembled.assets, true, doc)
|
|
486
535
|
}
|
|
487
536
|
lines[idx] = line
|
|
488
537
|
}
|
|
@@ -508,53 +557,43 @@ function mergeAsciiDoc (
|
|
|
508
557
|
return '='.repeat(targetMarkerLength) + ' ' + rest
|
|
509
558
|
})
|
|
510
559
|
// NOTE: ID will be undefined if sectids are turned off
|
|
511
|
-
if (block.getId()) rewriteStyleAttribute(block, lines, idx,
|
|
560
|
+
if (block.getId()) rewriteStyleAttribute(block, lines, idx, pageIdLeader, blockStyle)
|
|
512
561
|
} else {
|
|
513
562
|
if (context === 'image') {
|
|
514
563
|
let line = lines[idx] || ''
|
|
515
564
|
let prefix = ''
|
|
516
565
|
// Q: can we use startsWith('image::') in certain cases?
|
|
517
566
|
let imageMacroOffset = (
|
|
518
|
-
lastImageMacroAt?.[0] === idx ? line.
|
|
567
|
+
lastImageMacroAt?.[0] === idx ? line.substring(0, lastImageMacroAt[1]) : line
|
|
519
568
|
).lastIndexOf('image::')
|
|
520
569
|
if (imageMacroOffset > 0) {
|
|
521
570
|
if (
|
|
522
571
|
block.getDocument().isNested() &&
|
|
523
|
-
(prefix = line.
|
|
572
|
+
(prefix = line.substring(0, imageMacroOffset)).trimRight().endsWith('|')
|
|
524
573
|
) {
|
|
525
|
-
line = line.
|
|
574
|
+
line = line.substring(prefix.length)
|
|
526
575
|
} else {
|
|
527
576
|
imageMacroOffset = -1
|
|
528
577
|
}
|
|
529
578
|
}
|
|
530
|
-
if (imageMacroOffset
|
|
579
|
+
if (~imageMacroOffset) {
|
|
531
580
|
const target = block.getAttribute('target')
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
// FIXME: handle (or report) case when image is not resolved
|
|
535
|
-
if (image?.out && (filetype !== 'html' || siteRoot)) {
|
|
536
|
-
const attrlist = line.slice(line.indexOf('[') + 1, -1)
|
|
537
|
-
pagesInOutline.assembled.assets.add(image)
|
|
538
|
-
lines[idx] =
|
|
539
|
-
filetype === 'html'
|
|
540
|
-
? `${prefix}image::${resolveLinkTarget(image, siteRoot, pubRoot, linkRefStyle, false)}[${attrlist}]`
|
|
541
|
-
: `${prefix}image::${resolveEmbedTarget(image, outDirname, embedRefStyle)}[${attrlist}]`
|
|
542
|
-
}
|
|
543
|
-
}
|
|
581
|
+
const newTarget = rewriteImageRef(target, contentCatalog, assemblyModel, page.src, assembled.assets)
|
|
582
|
+
if (newTarget) lines[idx] = `${prefix}image::${newTarget}${line.substring(line.indexOf('['))}`
|
|
544
583
|
lastImageMacroAt = [idx, imageMacroOffset]
|
|
545
584
|
}
|
|
546
585
|
} else if (context === 'document' && block.hasHeader()) {
|
|
547
586
|
// nested document
|
|
548
587
|
idx = (block.getHeader().getLineNumber() || idx + 1) - 1
|
|
549
588
|
}
|
|
550
|
-
if (block.getId()) rewriteStyleAttribute(block, lines, idx,
|
|
589
|
+
if (block.getId()) rewriteStyleAttribute(block, lines, idx, pageIdLeader)
|
|
551
590
|
}
|
|
552
591
|
})
|
|
553
592
|
safePush(
|
|
554
593
|
buffer,
|
|
555
594
|
lines.filter((it) => it !== undefined)
|
|
556
595
|
)
|
|
557
|
-
const attributeEntries = Object.entries(doc.source_header_attributes
|
|
596
|
+
const attributeEntries = Object.entries(doc.source_header_attributes.$$smap)
|
|
558
597
|
if (attributeEntries.length) {
|
|
559
598
|
const resolvedAttributeEntries = attributeEntries.reduce(
|
|
560
599
|
(accum, [name, val]) => {
|
|
@@ -566,7 +605,7 @@ function mergeAsciiDoc (
|
|
|
566
605
|
} else if (val !== initialVal) {
|
|
567
606
|
accum.push(`:${name}:${initialVal ? ' ' + initialVal : ''}`)
|
|
568
607
|
}
|
|
569
|
-
} else if (!(val == null || doc.isAttributeLocked(name) ||
|
|
608
|
+
} else if (!(val == null || doc.isAttributeLocked(name) || DISCARD_ATTRIBUTE_NAMES.includes(name))) {
|
|
570
609
|
accum.push(`:!${name}:`)
|
|
571
610
|
}
|
|
572
611
|
return accum
|
|
@@ -576,26 +615,13 @@ function mergeAsciiDoc (
|
|
|
576
615
|
if (resolvedAttributeEntries.length > 1) safePush(buffer, resolvedAttributeEntries)
|
|
577
616
|
}
|
|
578
617
|
} else if (level) {
|
|
579
|
-
if (atDocumentRoot && navtitlePlain === componentVersion.title) {
|
|
618
|
+
if (atDocumentRoot && rootLevel === 0 && navtitlePlain === componentVersion.title) {
|
|
580
619
|
buffer.inBody ??= false
|
|
581
620
|
level--
|
|
582
621
|
} else {
|
|
583
622
|
buffer.inBody = true
|
|
584
623
|
buffer.push('')
|
|
585
|
-
|
|
586
|
-
// Q: should we unset docname, page-module, etc?
|
|
587
|
-
let toggleSectids, syntheticId
|
|
588
|
-
if (!('sectids' in asciidocConfig.attributes)) {
|
|
589
|
-
buffer.push(':!sectids:')
|
|
590
|
-
toggleSectids = true
|
|
591
|
-
} else if (typeof asciidocConfig.attributes.sectids === 'string') {
|
|
592
|
-
if ('sectids' in mutableAttributes) {
|
|
593
|
-
buffer.push(':!sectids:')
|
|
594
|
-
toggleSectids = true
|
|
595
|
-
} else {
|
|
596
|
-
syntheticId = `__object-id-${global.Opal.hash(outlineEntry).$object_id()}`
|
|
597
|
-
}
|
|
598
|
-
}
|
|
624
|
+
const syntheticId = idSeparators.generateIdFromTitle(navtitleAsciiDoc, idSeparators)
|
|
599
625
|
let sectionTitle = navtitleAsciiDoc
|
|
600
626
|
if (urlType === 'external') {
|
|
601
627
|
sectionTitle = `${url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
@@ -603,27 +629,19 @@ function mergeAsciiDoc (
|
|
|
603
629
|
const resource = files.find((it) => it.pub.url === url)
|
|
604
630
|
if (resource) {
|
|
605
631
|
if (resource.src.family === 'page' && pagesInOutline.has(resource.pub.url)) {
|
|
606
|
-
|
|
607
|
-
if (refid.startsWith('./')) refid = topicPrefix + refid.slice(2)
|
|
608
|
-
if (resource.src.module !== 'ROOT') refid = `${resource.src.module}${idCoordinateSeparator}${refid}`
|
|
632
|
+
const refid = generateScopedId(resource.src, componentVersion, idSeparators, filetype, true)
|
|
609
633
|
sectionTitle = `xref:${refid}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
610
634
|
} else if (siteRoot) {
|
|
611
|
-
sectionTitle = `${resolveLinkTarget(resource, siteRoot, pubRoot,
|
|
635
|
+
sectionTitle = `${resolveLinkTarget(resource, siteRoot, pubRoot, linkReferenceStyle, true)}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
612
636
|
}
|
|
613
637
|
}
|
|
614
638
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
buffer.push(syntheticId ? `[discrete#${syntheticId}]` : '[discrete]')
|
|
619
|
-
} else if (syntheticId) {
|
|
620
|
-
buffer.push(`[#${syntheticId}]`)
|
|
621
|
-
}
|
|
639
|
+
const hlevel = level > 5 ? 6 : level + 1
|
|
640
|
+
const hroles = urlType === 'internal' ? roles.slice(1) : roles
|
|
641
|
+
buffer.push(`[${hlevel > 6 ? 'discrete' : ''}#${syntheticId}${hroles.reduce((str, it) => str + '.' + it, '')}]`)
|
|
622
642
|
buffer.push(`${'='.repeat(hlevel)} ${sectionTitle}`)
|
|
623
|
-
if (toggleSectids) buffer.push(':sectids:')
|
|
624
643
|
}
|
|
625
644
|
}
|
|
626
|
-
|
|
627
645
|
if (hasItems) {
|
|
628
646
|
const nextLevel = level + 1
|
|
629
647
|
// NOTE: drop first child if same as parent; should we keep if content is different?
|
|
@@ -639,6 +657,7 @@ function mergeAsciiDoc (
|
|
|
639
657
|
item,
|
|
640
658
|
files,
|
|
641
659
|
pagesInOutline,
|
|
660
|
+
idSeparators,
|
|
642
661
|
asciidocConfig,
|
|
643
662
|
mutableAttributes,
|
|
644
663
|
assemblyModel,
|
|
@@ -651,11 +670,32 @@ function mergeAsciiDoc (
|
|
|
651
670
|
return buffer
|
|
652
671
|
}
|
|
653
672
|
|
|
654
|
-
function processDocumentHeader (doc, lines,
|
|
673
|
+
function processDocumentHeader (doc, lines, ignoreLines, isRootPage) {
|
|
674
|
+
const entries = []
|
|
675
|
+
let assemblyHeaderAttributes = isRootPage && doc.getAttribute('assembly-header-attributes', '%authors')
|
|
676
|
+
assemblyHeaderAttributes = new Set(assemblyHeaderAttributes ? assemblyHeaderAttributes.split(/, */) : undefined)
|
|
677
|
+
if (assemblyHeaderAttributes.size) {
|
|
678
|
+
if (assemblyHeaderAttributes.has('%authors')) {
|
|
679
|
+
const authors = doc.getAuthors()
|
|
680
|
+
if (authors.length) {
|
|
681
|
+
entries.push({
|
|
682
|
+
type: 'author_line',
|
|
683
|
+
promote: true,
|
|
684
|
+
lines: [
|
|
685
|
+
authors
|
|
686
|
+
.map((author) => (author.getEmail() ? `${author.getName()} <${author.getEmail()}>` : author.getName()))
|
|
687
|
+
.join('; '),
|
|
688
|
+
],
|
|
689
|
+
})
|
|
690
|
+
}
|
|
691
|
+
assemblyHeaderAttributes.delete('%authors')
|
|
692
|
+
}
|
|
693
|
+
const headerAttributes = doc.source_header_attributes
|
|
694
|
+
for (const name of assemblyHeaderAttributes) headerAttributes.$delete(name)
|
|
695
|
+
}
|
|
655
696
|
const doctitleIdx = doc.getHeader().getLineNumber() - 1
|
|
656
697
|
const end = doc.getBlocks()[0]?.getLineNumber() ?? lines.length
|
|
657
|
-
let belowDoctitle
|
|
658
|
-
let open
|
|
698
|
+
let belowDoctitle, current, open
|
|
659
699
|
const implicitLines = []
|
|
660
700
|
for (let idx = 0; idx < end; idx++) {
|
|
661
701
|
if (idx === doctitleIdx) {
|
|
@@ -666,41 +706,51 @@ function processDocumentHeader (doc, lines, buffer, ignoreLines) {
|
|
|
666
706
|
}
|
|
667
707
|
const line = lines[idx]
|
|
668
708
|
if (open === ':' || open === '-:') {
|
|
669
|
-
if (line)
|
|
670
|
-
|
|
671
|
-
if (!line.endsWith(' \\')) open = undefined
|
|
672
|
-
} else {
|
|
673
|
-
open = undefined
|
|
674
|
-
}
|
|
709
|
+
if (open === ':') current.lines.push(line)
|
|
710
|
+
if (!line?.endsWith(' \\')) current = open = undefined
|
|
675
711
|
} else if (line) {
|
|
676
|
-
const chr0 = line.charAt()
|
|
677
712
|
let attributeEntryMatch
|
|
713
|
+
const chr0 = line.charAt()
|
|
678
714
|
if (chr0 === '/' && line.charAt(1) === '/') {
|
|
679
|
-
if (line.startsWith('////')) {
|
|
680
|
-
|
|
681
|
-
|
|
715
|
+
if (line.startsWith('////') && line === '/'.repeat(line.length)) {
|
|
716
|
+
if (open) {
|
|
717
|
+
current.lines.push(line)
|
|
718
|
+
if (open === line) current = open = undefined
|
|
719
|
+
} else {
|
|
720
|
+
entries.push((current = { type: 'block_comment', lines: [(open = line)] }))
|
|
721
|
+
}
|
|
722
|
+
} else if (open) {
|
|
723
|
+
current.lines.push(line)
|
|
724
|
+
} else if (belowDoctitle && line.charAt(2) === '/') {
|
|
682
725
|
break
|
|
726
|
+
} else {
|
|
727
|
+
entries.push({ type: 'line_comment', lines: [line] })
|
|
728
|
+
current = undefined
|
|
683
729
|
}
|
|
684
|
-
buffer.push(line)
|
|
685
730
|
} else if (open) {
|
|
686
|
-
|
|
687
|
-
} else if (chr0 === ':' && ~line.indexOf(':', 2) && (attributeEntryMatch = line.match(
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
731
|
+
current.lines.push(line)
|
|
732
|
+
} else if (chr0 === ':' && ~line.indexOf(':', 2) && (attributeEntryMatch = line.match(ATTR_ENTRY_RX))) {
|
|
733
|
+
let name = attributeEntryMatch[1]
|
|
734
|
+
const negated = name.charAt() === '!' || name.charAt(name.length - 1) === '!'
|
|
735
|
+
if (negated) name = name.replace('!', '')
|
|
736
|
+
if (DISCARD_ATTRIBUTE_NAMES.includes(name) && !assemblyHeaderAttributes.has(name)) {
|
|
737
|
+
if (line.endsWith(' \\')) open = '-:' // disallow value continuation
|
|
691
738
|
} else {
|
|
692
739
|
if (line.endsWith(' \\')) open = ':'
|
|
693
|
-
|
|
740
|
+
entries.push((current = { type: 'attribute_entry', name, negated, lines: [line], lineno: idx + 1 }))
|
|
741
|
+
if (assemblyHeaderAttributes.has(name)) current.promote = true
|
|
694
742
|
}
|
|
695
743
|
} else if (belowDoctitle) {
|
|
696
744
|
if (implicitLines.length === 2 || !/[\p{Alpha}0-9]/u.test(chr0)) break
|
|
697
745
|
implicitLines.push(line)
|
|
698
746
|
} else if (chr0 === '[' && line.charAt(line.length - 1) === ']') {
|
|
747
|
+
current = undefined
|
|
699
748
|
const attrlist = line
|
|
700
|
-
.
|
|
749
|
+
.substring(1, line.length - 1)
|
|
701
750
|
.trim()
|
|
702
|
-
.replace(/(?:^\w[\w-]*(?!=|[\w-]))?([
|
|
703
|
-
|
|
751
|
+
.replace(/(?:^\w[\w-]*(?!=|[\w-]))?([#.]\w[\w-]*)*/, '')
|
|
752
|
+
.replace(NAMED_ID_ATTR_RX, '')
|
|
753
|
+
if (attrlist) entries.push({ type: 'attrlist', lines: ['[' + attrlist + ']'] })
|
|
704
754
|
}
|
|
705
755
|
} else if (belowDoctitle) {
|
|
706
756
|
break
|
|
@@ -708,23 +758,26 @@ function processDocumentHeader (doc, lines, buffer, ignoreLines) {
|
|
|
708
758
|
lines[idx] = undefined
|
|
709
759
|
ignoreLines.push(idx)
|
|
710
760
|
}
|
|
711
|
-
return
|
|
712
|
-
.getRoles()
|
|
713
|
-
.map((role) => '.' + role)
|
|
714
|
-
.join('')
|
|
761
|
+
return entries
|
|
715
762
|
}
|
|
716
763
|
|
|
717
|
-
function generateSlug (
|
|
718
|
-
return
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
764
|
+
function generateSlug (string) {
|
|
765
|
+
if (string === 'index') return string
|
|
766
|
+
return sanitizeSlug(
|
|
767
|
+
string
|
|
768
|
+
.toLowerCase()
|
|
769
|
+
.replace(/<[^>]+>/g, '')
|
|
770
|
+
.replace(CHAR_REF_RX, (_, name, dec, hex) => {
|
|
771
|
+
if (name) return BUILT_IN_NAMED_ENTITIES[name] ?? '?'
|
|
772
|
+
return String.fromCharCode(dec ? parseInt(dec, 10) : parseInt(hex, 16))
|
|
773
|
+
})
|
|
774
|
+
.replace(/[\x27\u2019]/g, '')
|
|
775
|
+
.replace(/[_.]/, '-')
|
|
776
|
+
)
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
function sanitizeSlug (slug) {
|
|
780
|
+
return slug.replace(/[^\p{Alpha}0-9_.-]/gu, '-').replace(/^-+|-+$|(-)-+/g, '$1')
|
|
728
781
|
}
|
|
729
782
|
|
|
730
783
|
function fixSectionLevels (sections, expectedLevel) {
|
|
@@ -735,64 +788,6 @@ function fixSectionLevels (sections, expectedLevel) {
|
|
|
735
788
|
})
|
|
736
789
|
}
|
|
737
790
|
|
|
738
|
-
function rewriteStyleAttribute (block, lines, idx, idPrefix, replacementStyle = '') {
|
|
739
|
-
let prevLine = lines[idx - 1]
|
|
740
|
-
const char0 = prevLine?.charAt()
|
|
741
|
-
if (char0) {
|
|
742
|
-
if (
|
|
743
|
-
(char0 === '.' && /^\.\.?[^ \t.]/.test(prevLine)) ||
|
|
744
|
-
(char0 === '[' &&
|
|
745
|
-
prevLine.charAt(1) === '[' &&
|
|
746
|
-
/^\[\[(?:|[\p{Alpha}_:][\p{Alpha}0-9_\-:.]*(?:, *.+)?)\]\]$/u.test(prevLine))
|
|
747
|
-
) {
|
|
748
|
-
return rewriteStyleAttribute(block, lines, idx - 1, idPrefix, replacementStyle)
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
let cellSpec
|
|
752
|
-
if (
|
|
753
|
-
char0 &&
|
|
754
|
-
(char0 === '[' || (block.getDocument().isNested() && (cellSpec = prevLine.match(/^([^[|]*)\| *(\[.+)/)))) &&
|
|
755
|
-
prevLine.charAt(prevLine.length - 1) === ']'
|
|
756
|
-
) {
|
|
757
|
-
if (cellSpec) {
|
|
758
|
-
prevLine = cellSpec[2]
|
|
759
|
-
cellSpec = cellSpec[1]
|
|
760
|
-
}
|
|
761
|
-
let rawStyle
|
|
762
|
-
const commaIdx = prevLine.indexOf(',')
|
|
763
|
-
if (~commaIdx) {
|
|
764
|
-
rawStyle = prevLine.slice(1, commaIdx)
|
|
765
|
-
if (~rawStyle.indexOf('=')) rawStyle = undefined
|
|
766
|
-
} else if (!~prevLine.indexOf('=')) {
|
|
767
|
-
rawStyle = prevLine.slice(1, prevLine.length - 1)
|
|
768
|
-
}
|
|
769
|
-
if (rawStyle) {
|
|
770
|
-
if (~rawStyle.indexOf('#')) {
|
|
771
|
-
prevLine = prevLine.replace(/#[^.%,\]]+/, `#${idPrefix}${block.getId()}`)
|
|
772
|
-
if (replacementStyle) prevLine = prevLine.replace(/^[^#.%,\]]+/, `[${replacementStyle}`)
|
|
773
|
-
} else {
|
|
774
|
-
prevLine = `[${
|
|
775
|
-
replacementStyle ? rawStyle.replace(/^[^.%]*/, replacementStyle) : rawStyle
|
|
776
|
-
}#${idPrefix}${block.getId()}${prevLine.slice(rawStyle.length + 1)}`
|
|
777
|
-
}
|
|
778
|
-
} else {
|
|
779
|
-
prevLine = `[${replacementStyle}#${idPrefix}${block.getId()}${rawStyle == null ? ',' : ''}${prevLine.slice(1)}`
|
|
780
|
-
}
|
|
781
|
-
if (cellSpec) prevLine = `${cellSpec}|${prevLine}`
|
|
782
|
-
lines[idx - 1] = prevLine
|
|
783
|
-
} else {
|
|
784
|
-
lines.splice(idx, 0, `[${replacementStyle}#${idPrefix}${block.getId()}]`)
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
function isResourceSpec (str) {
|
|
789
|
-
return !(~str.indexOf(':') && (~str.indexOf('://') || (str.startsWith('data:') && ~str.indexOf(','))))
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
function getObjectId (obj) {
|
|
793
|
-
return global.Opal.id(obj)
|
|
794
|
-
}
|
|
795
|
-
|
|
796
791
|
// NOTE: blank lines at top and bottom of document create mismatch when using line numbers to navigate source lines
|
|
797
792
|
// IMPORTANT: this must not leave behind lines the parser will drop!
|
|
798
793
|
// IDEA: another option is to capture initial lineno of reader and use as offset (but preseves those blank lines)
|
|
@@ -815,35 +810,11 @@ function safePush (onto, entries) {
|
|
|
815
810
|
}
|
|
816
811
|
}
|
|
817
812
|
|
|
818
|
-
function
|
|
819
|
-
const
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
function resolveLinkTarget (resource, siteRoot, pubRoot, referenceStyle, escapeForInline = true) {
|
|
825
|
-
let target
|
|
826
|
-
if (resource.site?.url) {
|
|
827
|
-
target = ['', resource.pub.url]
|
|
828
|
-
} else {
|
|
829
|
-
switch (referenceStyle) {
|
|
830
|
-
case 'absolute':
|
|
831
|
-
target = ['', siteRoot.url + resource.pub.url]
|
|
832
|
-
break
|
|
833
|
-
case 'root-relative':
|
|
834
|
-
target = ['link:', siteRoot.path + resource.pub.url]
|
|
835
|
-
break
|
|
836
|
-
default:
|
|
837
|
-
target = ['link:', computeRelativeUrl(pubRoot + '/', resource.pub.url)]
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
if (escapeForInline) target[1] = target[1].replace(/_/g, '{underscore}')
|
|
841
|
-
return target.join('')
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
function computeRelativeUrl (from, to) {
|
|
845
|
-
const rel = path.relative(from, to)
|
|
846
|
-
return to.charAt(to.length - 1) === '/' ? rel + '/' : rel
|
|
813
|
+
function generateIdFromTitle (titleAsciiDoc, idSeparators) {
|
|
814
|
+
const Section = this.$class().$const_get('::Asciidoctor::Section')
|
|
815
|
+
const baseId = Section.$generate_id(titleAsciiDoc, this)
|
|
816
|
+
this.getCatalog().refs['$[]='](baseId, true)
|
|
817
|
+
return `_${idSeparators.coordinate}${baseId}`
|
|
847
818
|
}
|
|
848
819
|
|
|
849
820
|
module.exports = produceAssemblyFile
|