@antora/assembler 1.0.0-beta.9 → 1.0.0-rc.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.
- 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 +79 -39
- package/lib/log-command.js +20 -0
- package/lib/produce-assembly-file.js +372 -405
- 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,79 @@ 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, sectionMergeStrategy, 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 (!pageStyle && atBookRoot && rootLevel === 0 && !doc.source_header_attributes['$key?']('assembly-style')) {
|
|
235
|
+
pageStyle = 'preface'
|
|
236
|
+
}
|
|
189
237
|
let part
|
|
190
238
|
if ((part = pageStyle === 'part')) {
|
|
191
239
|
pageStyle = ''
|
|
192
240
|
if (!supportsParts) part = undefined
|
|
193
241
|
} else if ((part = pageStyle.endsWith('-part'))) {
|
|
194
|
-
pageStyle = pageStyle.
|
|
242
|
+
pageStyle = pageStyle.substring(0, pageStyle.length - 5)
|
|
195
243
|
if (!supportsParts) part = undefined
|
|
196
244
|
}
|
|
245
|
+
const hasSections = doc.hasSections()
|
|
197
246
|
let nextSectionLevel = 1
|
|
198
247
|
const lines = doc.getSourceLines()
|
|
199
248
|
const ignoreLines = []
|
|
200
|
-
buffer.inBody
|
|
249
|
+
if (!buffer.inBody) {
|
|
250
|
+
buffer.inBody = true
|
|
251
|
+
buffer.endHeaderIdx = buffer.length - 1
|
|
252
|
+
}
|
|
201
253
|
buffer.push('')
|
|
202
254
|
buffer.push(`:docname: ${docname}`)
|
|
203
255
|
if (component !== lastComponentVersion.name) {
|
|
@@ -221,88 +273,161 @@ function mergeAsciiDoc (
|
|
|
221
273
|
buffer.push(`:page-origin-refname: ${origin.branch || origin.tag}`)
|
|
222
274
|
buffer.push(`:page-origin-reftype: ${origin.branch ? 'branch' : 'tag'}`)
|
|
223
275
|
buffer.push(`:page-origin-refhash: ${origin.worktree ? '(worktree)' : origin.refhash}`)
|
|
224
|
-
|
|
276
|
+
let headerHasBlockAttrs
|
|
277
|
+
if (doc.hasHeader()) {
|
|
278
|
+
for (const entry of processDocumentHeader(doc, lines, ignoreLines, isRootPage)) {
|
|
279
|
+
if (entry.type === 'author_line') {
|
|
280
|
+
buffer.splice(1, 0, entry.lines[0])
|
|
281
|
+
buffer.endHeaderIdx += 1
|
|
282
|
+
} else if (entry.type === 'attribute_entry') {
|
|
283
|
+
const name = entry.name
|
|
284
|
+
if (!entry.negated && !doc.isAttributeLocked(name)) {
|
|
285
|
+
let val, newVal
|
|
286
|
+
if (
|
|
287
|
+
name.endsWith('-image') &&
|
|
288
|
+
(val = doc.getAttribute(name)) &&
|
|
289
|
+
(newVal = rewriteImageAttr(val, contentCatalog, assemblyModel, page.src, assembled.assets))
|
|
290
|
+
) {
|
|
291
|
+
if (newVal !== val) entry.lines = [`:${name}: ${newVal}`]
|
|
292
|
+
} else if (~(val = entry.lines.join('\n').substring(name.length + 3)).indexOf(':')) {
|
|
293
|
+
newVal = val
|
|
294
|
+
if (~newVal.indexOf('image:')) {
|
|
295
|
+
newVal = rewriteInlineImages(
|
|
296
|
+
newVal,
|
|
297
|
+
contentCatalog,
|
|
298
|
+
assemblyModel,
|
|
299
|
+
page.src,
|
|
300
|
+
assembled.assets,
|
|
301
|
+
false,
|
|
302
|
+
doc
|
|
303
|
+
)
|
|
304
|
+
}
|
|
305
|
+
if (~newVal.indexOf('xref:')) {
|
|
306
|
+
newVal = rewriteXrefs(
|
|
307
|
+
newVal,
|
|
308
|
+
contentCatalog,
|
|
309
|
+
assemblyModel,
|
|
310
|
+
page.src,
|
|
311
|
+
false,
|
|
312
|
+
pagesInOutline,
|
|
313
|
+
idSeparators,
|
|
314
|
+
pageIdLeader,
|
|
315
|
+
doc,
|
|
316
|
+
{ file: relative, lineno: entry.lineno }
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
if (newVal !== val) entry.lines = `:${entry.name}: ${newVal}`.split('\n')
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (entry.promote) {
|
|
323
|
+
buffer.splice(buffer.endHeaderIdx + 1, 0, ...entry.lines)
|
|
324
|
+
buffer.endHeaderIdx += entry.lines.length
|
|
325
|
+
} else {
|
|
326
|
+
buffer.push(...entry.lines)
|
|
327
|
+
}
|
|
328
|
+
} else {
|
|
329
|
+
if (entry.type === 'attrlist') headerHasBlockAttrs = true
|
|
330
|
+
buffer.push(...entry.lines)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
pageRoles = doc.getRoles()
|
|
334
|
+
}
|
|
335
|
+
if (roles.length) pageRoles = pageRoles.concat(roles[0] === 'page' ? roles.slice(1) : roles)
|
|
225
336
|
let heading
|
|
226
337
|
if (pageStyle && (part && level === 1 ? (level = 0) : level) === 0) {
|
|
227
338
|
let htitleAsciiDoc = navtitleAsciiDoc
|
|
228
|
-
let
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
339
|
+
let notitle
|
|
340
|
+
if (pageStyle === 'preface') {
|
|
341
|
+
const hasPrefaceTitleAttr =
|
|
342
|
+
doc.isAttribute('preface-title') ||
|
|
343
|
+
doc.source_header_attributes['$key?']('preface-title') ||
|
|
344
|
+
doc.isAttributeLocked('preface-title')
|
|
345
|
+
const htitleOverride = hasPrefaceTitleAttr
|
|
346
|
+
? doc.getAttribute('preface-title', '')
|
|
347
|
+
: atDocumentRoot && (rootLevel ? true : !hasAssemblyNavtitleAttr)
|
|
348
|
+
? ''
|
|
349
|
+
: undefined
|
|
350
|
+
if (htitleOverride != null) {
|
|
351
|
+
const keepSections = hasSections && sectionMergeStrategy !== 'discrete'
|
|
352
|
+
if (htitleOverride === '') {
|
|
353
|
+
if (headerHasBlockAttrs || keepSections) {
|
|
354
|
+
htitleAsciiDoc = '{empty}'
|
|
355
|
+
notitle = true
|
|
356
|
+
} else {
|
|
357
|
+
if (hasPrefaceTitleAttr) buffer.splice(++buffer.endHeaderIdx, 0, ':preface-title:')
|
|
358
|
+
htitleAsciiDoc = undefined
|
|
359
|
+
}
|
|
360
|
+
} else if (hasPrefaceTitleAttr && !headerHasBlockAttrs && !keepSections) {
|
|
361
|
+
buffer.splice(++buffer.endHeaderIdx, 0, `:preface-title: ${unconvertInlineAsciiDoc(htitleOverride)}`)
|
|
362
|
+
htitleAsciiDoc = undefined
|
|
363
|
+
} else {
|
|
364
|
+
htitleAsciiDoc = unconvertInlineAsciiDoc(htitleOverride)
|
|
365
|
+
}
|
|
366
|
+
}
|
|
236
367
|
}
|
|
237
|
-
if (
|
|
238
|
-
assemblyModel = Object.assign({}, assemblyModel, { sectionMergeStrategy: 'discrete' })
|
|
239
|
-
} else {
|
|
368
|
+
if (htitleAsciiDoc != null) {
|
|
240
369
|
if (atDocumentRoot) {
|
|
241
|
-
|
|
242
|
-
|
|
370
|
+
// NOTE use pageStyle as fallback because this section must have a unique ID
|
|
371
|
+
pageFragment = `#${pageIdLeader}${doc.getId() ?? pageStyle}`
|
|
372
|
+
buffer.unshift(`[#${pageId}]`)
|
|
243
373
|
} else {
|
|
244
|
-
pageFragment = `#${
|
|
374
|
+
pageFragment = `#${pageId}`
|
|
245
375
|
}
|
|
246
376
|
heading = { title: htitleAsciiDoc, level: part ? 1 : 2 }
|
|
247
|
-
|
|
377
|
+
if (notitle) heading.notitle = true
|
|
378
|
+
nextSectionLevel = 2 // next section level is 2, even for special part
|
|
248
379
|
}
|
|
249
380
|
} else if (level) {
|
|
250
|
-
if (atDocumentRoot && navtitlePlain === componentVersion.title) {
|
|
381
|
+
if (atDocumentRoot && rootLevel === 0 && navtitlePlain === componentVersion.title && !hasAssemblyNavtitleAttr) {
|
|
251
382
|
level--
|
|
252
383
|
} else {
|
|
253
|
-
pageFragment = `#${
|
|
384
|
+
pageFragment = `#${pageId}`
|
|
254
385
|
if (part && level === 1) level--
|
|
255
386
|
if ((heading = { title: navtitleAsciiDoc, level: level + 1 }).level > 6) {
|
|
256
387
|
Object.assign(heading, { level: 6, style: `discrete.h${heading.level}` })
|
|
257
388
|
}
|
|
258
389
|
}
|
|
390
|
+
} else if (atDocumentRoot && rootLevel === 0 && hasAssemblyNavtitleAttr) {
|
|
391
|
+
pageFragment = `#${pageId}`
|
|
392
|
+
heading = { title: navtitleAsciiDoc, level: (nextSectionLevel = part ? 1 : 2) }
|
|
393
|
+
} else if (atBookRoot) {
|
|
394
|
+
nextSectionLevel = undefined
|
|
259
395
|
}
|
|
396
|
+
let enclosed
|
|
260
397
|
if (heading) {
|
|
261
|
-
|
|
398
|
+
const rolesAttr = pageRoles.reduce((str, it) => str + '.' + it, '')
|
|
399
|
+
const notitleAttrs = heading.notitle ? '%notitle,toclevels=0,outlinelevels=0' : ''
|
|
400
|
+
buffer.push(`[${heading.style ?? pageStyle}${pageFragment}${rolesAttr}${notitleAttrs}]`)
|
|
262
401
|
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)}`
|
|
402
|
+
} else {
|
|
403
|
+
if (atDocumentRoot) buffer.unshift(`[#${pageId}]`)
|
|
404
|
+
if (sectionMergeStrategy === 'enclose' && hasItems && hasSections) {
|
|
405
|
+
enclosed = true
|
|
406
|
+
// TODO: make overview section title configurable
|
|
407
|
+
//let overviewTitle = doc.getDocumentTitle()
|
|
408
|
+
//if (overviewTitle === navtitle) overviewTitle = doc.getAttribute('overview-title', 'Overview')
|
|
409
|
+
const overviewTitle = doc.getAttribute('overview-title', 'Overview')
|
|
410
|
+
const syntheticId = idSeparators.generateIdFromTitle(navtitleAsciiDoc, idSeparators)
|
|
411
|
+
let hlevel = level + 2
|
|
412
|
+
if (hlevel > 6) {
|
|
413
|
+
const blockStyle = `discrete.h${hlevel}`
|
|
414
|
+
hlevel = 6
|
|
415
|
+
buffer.push(`[${blockStyle}#${syntheticId}]`)
|
|
279
416
|
} else {
|
|
280
|
-
buffer.push(
|
|
281
|
-
toggleSectids = true
|
|
417
|
+
buffer.push(`[#${syntheticId}]`)
|
|
282
418
|
}
|
|
419
|
+
buffer.push(`${'='.repeat(hlevel)} ${overviewTitle}`)
|
|
283
420
|
}
|
|
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
421
|
}
|
|
422
|
+
assembled.pages.set(page, pageFragment)
|
|
423
|
+
if (hasSections) fixSectionLevels(doc.getSections(), nextSectionLevel)
|
|
299
424
|
const allBlocks = doc.findBy({ traverse_documents: true }, (it) =>
|
|
300
425
|
it.getContext() === 'document'
|
|
301
426
|
? it.getDocument().isNested()
|
|
302
427
|
: !(it.getContext() === 'table_cell' && it.getStyle() === 'asciidoc')
|
|
303
428
|
)
|
|
304
429
|
if (doc.getDoctype() === 'manpage') {
|
|
305
|
-
const firstSectionIdx =
|
|
430
|
+
const firstSectionIdx = hasSections ? doc.getSections()[0].getLineNumber() - 1 : lines.length
|
|
306
431
|
for (let idx = 0; idx < firstSectionIdx; idx++) {
|
|
307
432
|
if (~ignoreLines.indexOf(idx)) continue
|
|
308
433
|
const line = lines[idx]
|
|
@@ -366,7 +491,7 @@ function mergeAsciiDoc (
|
|
|
366
491
|
if (
|
|
367
492
|
line.charAt() === ':' &&
|
|
368
493
|
~line.indexOf(':', 2) &&
|
|
369
|
-
(line.match(
|
|
494
|
+
(line.match(ATTR_ENTRY_RX) || ['', ''])[1].replace('!', '') === 'leveloffset'
|
|
370
495
|
) {
|
|
371
496
|
if (lines[idx - 1] === '') lines[idx - 1] = undefined
|
|
372
497
|
lines[idx] = undefined
|
|
@@ -378,111 +503,31 @@ function mergeAsciiDoc (
|
|
|
378
503
|
if (!refs['$key?'](refid) && (~refid.indexOf(' ') || refid.toLowerCase() !== refid)) {
|
|
379
504
|
if ((refid = doc.$resolve_id(refid))['$nil?']()) return m
|
|
380
505
|
}
|
|
381
|
-
return `<<${
|
|
506
|
+
return `<<${pageIdLeader}${refid}${text ? ',' + text : ''}>>`
|
|
382
507
|
})
|
|
383
508
|
}
|
|
384
509
|
// NOTE: the next check takes care of inline and block anchors
|
|
385
510
|
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}]`
|
|
511
|
+
line = line.replace(/\[\[([\p{Alpha}_:][\p{Alpha}0-9_\-:.]*)(|, *.+?)\]\]/gu, (_, refid, text) => {
|
|
512
|
+
return `[[${pageIdLeader}${refid}${text}]]`
|
|
455
513
|
})
|
|
456
514
|
}
|
|
457
|
-
if (~line.indexOf('
|
|
458
|
-
line =
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
})
|
|
515
|
+
if (~line.indexOf('xref:')) {
|
|
516
|
+
line = rewriteXrefs(
|
|
517
|
+
line,
|
|
518
|
+
contentCatalog,
|
|
519
|
+
assemblyModel,
|
|
520
|
+
page.src,
|
|
521
|
+
true,
|
|
522
|
+
pagesInOutline,
|
|
523
|
+
idSeparators,
|
|
524
|
+
pageIdLeader,
|
|
525
|
+
doc,
|
|
526
|
+
{ file: relative, lineno: idx + 1 }
|
|
527
|
+
)
|
|
471
528
|
}
|
|
472
529
|
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
|
-
})
|
|
530
|
+
line = rewriteInlineImages(line, contentCatalog, assemblyModel, page.src, assembled.assets, true, doc)
|
|
486
531
|
}
|
|
487
532
|
lines[idx] = line
|
|
488
533
|
}
|
|
@@ -498,7 +543,7 @@ function mergeAsciiDoc (
|
|
|
498
543
|
let idx = lineno - 1
|
|
499
544
|
if (context === 'section' && !block.getDocument().isNested()) {
|
|
500
545
|
if (block.getSectionName() === 'header') return
|
|
501
|
-
let blockStyle =
|
|
546
|
+
let blockStyle = sectionMergeStrategy === 'discrete' ? 'discrete' : undefined
|
|
502
547
|
lines[idx] = lines[idx].replace(/^=+ (.+)/, (_, rest) => {
|
|
503
548
|
let targetMarkerLength = block.level + 1 + level + (enclosed ? 1 : 0)
|
|
504
549
|
if (targetMarkerLength > 6) {
|
|
@@ -508,53 +553,43 @@ function mergeAsciiDoc (
|
|
|
508
553
|
return '='.repeat(targetMarkerLength) + ' ' + rest
|
|
509
554
|
})
|
|
510
555
|
// NOTE: ID will be undefined if sectids are turned off
|
|
511
|
-
if (block.getId()) rewriteStyleAttribute(block, lines, idx,
|
|
556
|
+
if (block.getId()) rewriteStyleAttribute(block, lines, idx, pageIdLeader, blockStyle)
|
|
512
557
|
} else {
|
|
513
558
|
if (context === 'image') {
|
|
514
559
|
let line = lines[idx] || ''
|
|
515
560
|
let prefix = ''
|
|
516
561
|
// Q: can we use startsWith('image::') in certain cases?
|
|
517
562
|
let imageMacroOffset = (
|
|
518
|
-
lastImageMacroAt?.[0] === idx ? line.
|
|
563
|
+
lastImageMacroAt?.[0] === idx ? line.substring(0, lastImageMacroAt[1]) : line
|
|
519
564
|
).lastIndexOf('image::')
|
|
520
565
|
if (imageMacroOffset > 0) {
|
|
521
566
|
if (
|
|
522
567
|
block.getDocument().isNested() &&
|
|
523
|
-
(prefix = line.
|
|
568
|
+
(prefix = line.substring(0, imageMacroOffset)).trimRight().endsWith('|')
|
|
524
569
|
) {
|
|
525
|
-
line = line.
|
|
570
|
+
line = line.substring(prefix.length)
|
|
526
571
|
} else {
|
|
527
572
|
imageMacroOffset = -1
|
|
528
573
|
}
|
|
529
574
|
}
|
|
530
|
-
if (imageMacroOffset
|
|
575
|
+
if (~imageMacroOffset) {
|
|
531
576
|
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
|
-
}
|
|
577
|
+
const newTarget = rewriteImageRef(target, contentCatalog, assemblyModel, page.src, assembled.assets)
|
|
578
|
+
if (newTarget) lines[idx] = `${prefix}image::${newTarget}${line.substring(line.indexOf('['))}`
|
|
544
579
|
lastImageMacroAt = [idx, imageMacroOffset]
|
|
545
580
|
}
|
|
546
581
|
} else if (context === 'document' && block.hasHeader()) {
|
|
547
582
|
// nested document
|
|
548
583
|
idx = (block.getHeader().getLineNumber() || idx + 1) - 1
|
|
549
584
|
}
|
|
550
|
-
if (block.getId()) rewriteStyleAttribute(block, lines, idx,
|
|
585
|
+
if (block.getId()) rewriteStyleAttribute(block, lines, idx, pageIdLeader)
|
|
551
586
|
}
|
|
552
587
|
})
|
|
553
588
|
safePush(
|
|
554
589
|
buffer,
|
|
555
590
|
lines.filter((it) => it !== undefined)
|
|
556
591
|
)
|
|
557
|
-
const attributeEntries = Object.entries(doc.source_header_attributes
|
|
592
|
+
const attributeEntries = Object.entries(doc.source_header_attributes.$$smap)
|
|
558
593
|
if (attributeEntries.length) {
|
|
559
594
|
const resolvedAttributeEntries = attributeEntries.reduce(
|
|
560
595
|
(accum, [name, val]) => {
|
|
@@ -566,7 +601,7 @@ function mergeAsciiDoc (
|
|
|
566
601
|
} else if (val !== initialVal) {
|
|
567
602
|
accum.push(`:${name}:${initialVal ? ' ' + initialVal : ''}`)
|
|
568
603
|
}
|
|
569
|
-
} else if (!(val == null || doc.isAttributeLocked(name) ||
|
|
604
|
+
} else if (!(val == null || doc.isAttributeLocked(name) || DISCARD_ATTRIBUTE_NAMES.includes(name))) {
|
|
570
605
|
accum.push(`:!${name}:`)
|
|
571
606
|
}
|
|
572
607
|
return accum
|
|
@@ -576,26 +611,13 @@ function mergeAsciiDoc (
|
|
|
576
611
|
if (resolvedAttributeEntries.length > 1) safePush(buffer, resolvedAttributeEntries)
|
|
577
612
|
}
|
|
578
613
|
} else if (level) {
|
|
579
|
-
if (atDocumentRoot && navtitlePlain === componentVersion.title) {
|
|
614
|
+
if (atDocumentRoot && rootLevel === 0 && navtitlePlain === componentVersion.title) {
|
|
580
615
|
buffer.inBody ??= false
|
|
581
616
|
level--
|
|
582
617
|
} else {
|
|
583
618
|
buffer.inBody = true
|
|
584
619
|
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
|
-
}
|
|
620
|
+
const syntheticId = idSeparators.generateIdFromTitle(navtitleAsciiDoc, idSeparators)
|
|
599
621
|
let sectionTitle = navtitleAsciiDoc
|
|
600
622
|
if (urlType === 'external') {
|
|
601
623
|
sectionTitle = `${url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
@@ -603,27 +625,19 @@ function mergeAsciiDoc (
|
|
|
603
625
|
const resource = files.find((it) => it.pub.url === url)
|
|
604
626
|
if (resource) {
|
|
605
627
|
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}`
|
|
628
|
+
const refid = generateScopedId(resource.src, componentVersion, idSeparators, filetype, true)
|
|
609
629
|
sectionTitle = `xref:${refid}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
610
630
|
} else if (siteRoot) {
|
|
611
|
-
sectionTitle = `${resolveLinkTarget(resource, siteRoot, pubRoot,
|
|
631
|
+
sectionTitle = `${resolveLinkTarget(resource, siteRoot, pubRoot, linkReferenceStyle, true)}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
612
632
|
}
|
|
613
633
|
}
|
|
614
634
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
buffer.push(syntheticId ? `[discrete#${syntheticId}]` : '[discrete]')
|
|
619
|
-
} else if (syntheticId) {
|
|
620
|
-
buffer.push(`[#${syntheticId}]`)
|
|
621
|
-
}
|
|
635
|
+
const hlevel = level > 5 ? 6 : level + 1
|
|
636
|
+
const hroles = urlType === 'internal' ? roles.slice(1) : roles
|
|
637
|
+
buffer.push(`[${hlevel > 6 ? 'discrete' : ''}#${syntheticId}${hroles.reduce((str, it) => str + '.' + it, '')}]`)
|
|
622
638
|
buffer.push(`${'='.repeat(hlevel)} ${sectionTitle}`)
|
|
623
|
-
if (toggleSectids) buffer.push(':sectids:')
|
|
624
639
|
}
|
|
625
640
|
}
|
|
626
|
-
|
|
627
641
|
if (hasItems) {
|
|
628
642
|
const nextLevel = level + 1
|
|
629
643
|
// NOTE: drop first child if same as parent; should we keep if content is different?
|
|
@@ -639,6 +653,7 @@ function mergeAsciiDoc (
|
|
|
639
653
|
item,
|
|
640
654
|
files,
|
|
641
655
|
pagesInOutline,
|
|
656
|
+
idSeparators,
|
|
642
657
|
asciidocConfig,
|
|
643
658
|
mutableAttributes,
|
|
644
659
|
assemblyModel,
|
|
@@ -651,11 +666,32 @@ function mergeAsciiDoc (
|
|
|
651
666
|
return buffer
|
|
652
667
|
}
|
|
653
668
|
|
|
654
|
-
function processDocumentHeader (doc, lines,
|
|
669
|
+
function processDocumentHeader (doc, lines, ignoreLines, isRootPage) {
|
|
670
|
+
const entries = []
|
|
671
|
+
let assemblyHeaderAttributes = isRootPage && doc.getAttribute('assembly-header-attributes', '%authors')
|
|
672
|
+
assemblyHeaderAttributes = new Set(assemblyHeaderAttributes ? assemblyHeaderAttributes.split(/, */) : undefined)
|
|
673
|
+
if (assemblyHeaderAttributes.size) {
|
|
674
|
+
if (assemblyHeaderAttributes.has('%authors')) {
|
|
675
|
+
const authors = doc.getAuthors()
|
|
676
|
+
if (authors.length) {
|
|
677
|
+
entries.push({
|
|
678
|
+
type: 'author_line',
|
|
679
|
+
promote: true,
|
|
680
|
+
lines: [
|
|
681
|
+
authors
|
|
682
|
+
.map((author) => (author.getEmail() ? `${author.getName()} <${author.getEmail()}>` : author.getName()))
|
|
683
|
+
.join('; '),
|
|
684
|
+
],
|
|
685
|
+
})
|
|
686
|
+
}
|
|
687
|
+
assemblyHeaderAttributes.delete('%authors')
|
|
688
|
+
}
|
|
689
|
+
const headerAttributes = doc.source_header_attributes
|
|
690
|
+
for (const name of assemblyHeaderAttributes) headerAttributes.$delete(name)
|
|
691
|
+
}
|
|
655
692
|
const doctitleIdx = doc.getHeader().getLineNumber() - 1
|
|
656
693
|
const end = doc.getBlocks()[0]?.getLineNumber() ?? lines.length
|
|
657
|
-
let belowDoctitle
|
|
658
|
-
let open
|
|
694
|
+
let belowDoctitle, current, open
|
|
659
695
|
const implicitLines = []
|
|
660
696
|
for (let idx = 0; idx < end; idx++) {
|
|
661
697
|
if (idx === doctitleIdx) {
|
|
@@ -666,41 +702,51 @@ function processDocumentHeader (doc, lines, buffer, ignoreLines) {
|
|
|
666
702
|
}
|
|
667
703
|
const line = lines[idx]
|
|
668
704
|
if (open === ':' || open === '-:') {
|
|
669
|
-
if (line)
|
|
670
|
-
|
|
671
|
-
if (!line.endsWith(' \\')) open = undefined
|
|
672
|
-
} else {
|
|
673
|
-
open = undefined
|
|
674
|
-
}
|
|
705
|
+
if (open === ':') current.lines.push(line)
|
|
706
|
+
if (!line?.endsWith(' \\')) current = open = undefined
|
|
675
707
|
} else if (line) {
|
|
676
|
-
const chr0 = line.charAt()
|
|
677
708
|
let attributeEntryMatch
|
|
709
|
+
const chr0 = line.charAt()
|
|
678
710
|
if (chr0 === '/' && line.charAt(1) === '/') {
|
|
679
|
-
if (line.startsWith('////')) {
|
|
680
|
-
|
|
681
|
-
|
|
711
|
+
if (line.startsWith('////') && line === '/'.repeat(line.length)) {
|
|
712
|
+
if (open) {
|
|
713
|
+
current.lines.push(line)
|
|
714
|
+
if (open === line) current = open = undefined
|
|
715
|
+
} else {
|
|
716
|
+
entries.push((current = { type: 'block_comment', lines: [(open = line)] }))
|
|
717
|
+
}
|
|
718
|
+
} else if (open) {
|
|
719
|
+
current.lines.push(line)
|
|
720
|
+
} else if (belowDoctitle && line.charAt(2) === '/') {
|
|
682
721
|
break
|
|
722
|
+
} else {
|
|
723
|
+
entries.push({ type: 'line_comment', lines: [line] })
|
|
724
|
+
current = undefined
|
|
683
725
|
}
|
|
684
|
-
buffer.push(line)
|
|
685
726
|
} else if (open) {
|
|
686
|
-
|
|
687
|
-
} else if (chr0 === ':' && ~line.indexOf(':', 2) && (attributeEntryMatch = line.match(
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
727
|
+
current.lines.push(line)
|
|
728
|
+
} else if (chr0 === ':' && ~line.indexOf(':', 2) && (attributeEntryMatch = line.match(ATTR_ENTRY_RX))) {
|
|
729
|
+
let name = attributeEntryMatch[1]
|
|
730
|
+
const negated = name.charAt() === '!' || name.charAt(name.length - 1) === '!'
|
|
731
|
+
if (negated) name = name.replace('!', '')
|
|
732
|
+
if (DISCARD_ATTRIBUTE_NAMES.includes(name) && !assemblyHeaderAttributes.has(name)) {
|
|
733
|
+
if (line.endsWith(' \\')) open = '-:' // disallow value continuation
|
|
691
734
|
} else {
|
|
692
735
|
if (line.endsWith(' \\')) open = ':'
|
|
693
|
-
|
|
736
|
+
entries.push((current = { type: 'attribute_entry', name, negated, lines: [line], lineno: idx + 1 }))
|
|
737
|
+
if (assemblyHeaderAttributes.has(name)) current.promote = true
|
|
694
738
|
}
|
|
695
739
|
} else if (belowDoctitle) {
|
|
696
740
|
if (implicitLines.length === 2 || !/[\p{Alpha}0-9]/u.test(chr0)) break
|
|
697
741
|
implicitLines.push(line)
|
|
698
742
|
} else if (chr0 === '[' && line.charAt(line.length - 1) === ']') {
|
|
743
|
+
current = undefined
|
|
699
744
|
const attrlist = line
|
|
700
|
-
.
|
|
745
|
+
.substring(1, line.length - 1)
|
|
701
746
|
.trim()
|
|
702
|
-
.replace(/(?:^\w[\w-]*(?!=|[\w-]))?([
|
|
703
|
-
|
|
747
|
+
.replace(/(?:^\w[\w-]*(?!=|[\w-]))?([#.]\w[\w-]*)*/, '')
|
|
748
|
+
.replace(NAMED_ID_ATTR_RX, '')
|
|
749
|
+
if (attrlist) entries.push({ type: 'attrlist', lines: ['[' + attrlist + ']'] })
|
|
704
750
|
}
|
|
705
751
|
} else if (belowDoctitle) {
|
|
706
752
|
break
|
|
@@ -708,23 +754,26 @@ function processDocumentHeader (doc, lines, buffer, ignoreLines) {
|
|
|
708
754
|
lines[idx] = undefined
|
|
709
755
|
ignoreLines.push(idx)
|
|
710
756
|
}
|
|
711
|
-
return
|
|
712
|
-
.getRoles()
|
|
713
|
-
.map((role) => '.' + role)
|
|
714
|
-
.join('')
|
|
757
|
+
return entries
|
|
715
758
|
}
|
|
716
759
|
|
|
717
|
-
function generateSlug (
|
|
718
|
-
return
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
760
|
+
function generateSlug (string) {
|
|
761
|
+
if (string === 'index') return string
|
|
762
|
+
return sanitizeSlug(
|
|
763
|
+
string
|
|
764
|
+
.toLowerCase()
|
|
765
|
+
.replace(/<[^>]+>/g, '')
|
|
766
|
+
.replace(CHAR_REF_RX, (_, name, dec, hex) => {
|
|
767
|
+
if (name) return BUILT_IN_NAMED_ENTITIES[name] ?? '?'
|
|
768
|
+
return String.fromCharCode(dec ? parseInt(dec, 10) : parseInt(hex, 16))
|
|
769
|
+
})
|
|
770
|
+
.replace(/[\x27\u2019]/g, '')
|
|
771
|
+
.replace(/[_.]/, '-')
|
|
772
|
+
)
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
function sanitizeSlug (slug) {
|
|
776
|
+
return slug.replace(/[^\p{Alpha}0-9_.-]/gu, '-').replace(/^-+|-+$|(-)-+/g, '$1')
|
|
728
777
|
}
|
|
729
778
|
|
|
730
779
|
function fixSectionLevels (sections, expectedLevel) {
|
|
@@ -735,64 +784,6 @@ function fixSectionLevels (sections, expectedLevel) {
|
|
|
735
784
|
})
|
|
736
785
|
}
|
|
737
786
|
|
|
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
787
|
// NOTE: blank lines at top and bottom of document create mismatch when using line numbers to navigate source lines
|
|
797
788
|
// IMPORTANT: this must not leave behind lines the parser will drop!
|
|
798
789
|
// IDEA: another option is to capture initial lineno of reader and use as offset (but preseves those blank lines)
|
|
@@ -815,35 +806,11 @@ function safePush (onto, entries) {
|
|
|
815
806
|
}
|
|
816
807
|
}
|
|
817
808
|
|
|
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
|
|
809
|
+
function generateIdFromTitle (titleAsciiDoc, idSeparators) {
|
|
810
|
+
const Section = this.$class().$const_get('::Asciidoctor::Section')
|
|
811
|
+
const baseId = Section.$generate_id(titleAsciiDoc, this)
|
|
812
|
+
this.getCatalog().refs['$[]='](baseId, true)
|
|
813
|
+
return `_${idSeparators.coordinate}${baseId}`
|
|
847
814
|
}
|
|
848
815
|
|
|
849
816
|
module.exports = produceAssemblyFile
|