@antora/assembler 1.0.0-beta.8 → 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 +36 -15
- 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 +375 -407
- 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,114 +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
|
-
if (!relativePart) {
|
|
404
|
-
// Q: should we validate the internal ID here?
|
|
405
|
-
return text && ~text.indexOf('=')
|
|
406
|
-
? `xref:${idPrefix}${fragment}[${text}]`
|
|
407
|
-
: `<<${idPrefix}${fragment}${text ? ',' + text.replace(/\\]/g, ']') : ''}>>`
|
|
408
|
-
}
|
|
409
|
-
if (~dollarIdx) {
|
|
410
|
-
if (relativePart.slice(dollarIdx).startsWith('$./')) {
|
|
411
|
-
relativePart = relativePart.slice(0, dollarIdx + 1) + topicPrefix + relativePart.slice(dollarIdx + 3)
|
|
412
|
-
}
|
|
413
|
-
if ((isPage = /\bpage\$/.test(relativePart))) relativePart = relativePart.replace('page$', '')
|
|
414
|
-
} else {
|
|
415
|
-
isPage = true
|
|
416
|
-
if (relativePart.startsWith('./')) relativePart = topicPrefix + relativePart.slice(2)
|
|
417
|
-
if (~hashIdx && !relativePart.endsWith('.adoc')) relativePart += '.adoc'
|
|
418
|
-
}
|
|
419
|
-
if (!isPage || ~relativePart.indexOf('@') || /:.*:/.test(relativePart)) {
|
|
420
|
-
if (siteRoot && (resource = contentCatalog.resolveResource(relativePart, page.src, 'page'))?.pub) {
|
|
421
|
-
text ||= resource.asciidoc?.xreftext || target
|
|
422
|
-
return `${resolveLinkTarget(resource, siteRoot, pubRoot, linkRefStyle)}${fragment && '#' + fragment}[${text}]`
|
|
423
|
-
}
|
|
424
|
-
// TODO: handle unresolved resource better
|
|
425
|
-
return m
|
|
426
|
-
}
|
|
427
|
-
let targetModule
|
|
428
|
-
const colonIdx = relativePart.indexOf(':')
|
|
429
|
-
if (~colonIdx) {
|
|
430
|
-
targetModule = relativePart.slice(0, colonIdx)
|
|
431
|
-
relativePart = relativePart.slice(colonIdx + 1)
|
|
432
|
-
if (relativePart.startsWith('./')) relativePart = topicPrefix + relativePart.slice(2)
|
|
433
|
-
} else {
|
|
434
|
-
targetModule = module_
|
|
435
|
-
}
|
|
436
|
-
const pageResourceRef = targetModule === 'ROOT' ? relativePart : `${targetModule}:${relativePart}`
|
|
437
|
-
if (!(resource = pagesInOutline.get(pageResourceRef))) {
|
|
438
|
-
if (siteRoot && (resource = contentCatalog.resolvePage(pageResourceRef, page.src)) && resource.out) {
|
|
439
|
-
text ||= resource.asciidoc?.xreftext || target
|
|
440
|
-
return `${resolveLinkTarget(resource, siteRoot, pubRoot, linkRefStyle)}${fragment && '#' + fragment}[${text}]`
|
|
441
|
-
}
|
|
442
|
-
// TODO: handle unresolved page better
|
|
443
|
-
return m
|
|
444
|
-
}
|
|
445
|
-
if (targetModule !== 'ROOT') relativePart = `${targetModule}${idCoordinateSeparator}${relativePart}`
|
|
446
|
-
relativePart = relativePart.replace(/\.adoc$/, '').replace(/[/.]/g, '-')
|
|
447
|
-
const refid = fragment
|
|
448
|
-
? `${relativePart}${idScopeSeparator}${fragment}`
|
|
449
|
-
: relativePart + (ReservedIdNames.includes(relativePart) ? idScopeSeparator : '')
|
|
450
|
-
if (
|
|
451
|
-
text &&
|
|
452
|
-
(assemblyModel.dropExplicitXrefText === 'always' ||
|
|
453
|
-
(assemblyModel.dropExplicitXrefText === 'if-redundant' && text === resource.title))
|
|
454
|
-
) {
|
|
455
|
-
text = ''
|
|
456
|
-
}
|
|
457
|
-
return `<<${refid}${text ? ',' + text.replace(/\\]/g, ']') : ''}>>`
|
|
515
|
+
line = line.replace(/\[\[([\p{Alpha}_:][\p{Alpha}0-9_\-:.]*)(|, *.+?)\]\]/gu, (_, refid, text) => {
|
|
516
|
+
return `[[${pageIdLeader}${refid}${text}]]`
|
|
458
517
|
})
|
|
459
518
|
}
|
|
460
|
-
if (~line.indexOf('
|
|
461
|
-
line =
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
})
|
|
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
|
+
)
|
|
474
532
|
}
|
|
475
533
|
if (~line.indexOf('image:') && !line.startsWith('image::')) {
|
|
476
|
-
line = line.
|
|
477
|
-
if (isResourceSpec(target)) {
|
|
478
|
-
const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
|
|
479
|
-
// TODO: handle (or report) unresolved image better
|
|
480
|
-
if (image?.out && (filetype !== 'html' || siteRoot)) {
|
|
481
|
-
pagesInOutline.assembled.assets.add(image)
|
|
482
|
-
return filetype === 'html'
|
|
483
|
-
? `image:${resolveLinkTarget(image, siteRoot, pubRoot, linkRefStyle)}[${attrlist}]`
|
|
484
|
-
: `image:${resolveEmbedTarget(image, outDirname, embedRefStyle, true)}[${attrlist}]`
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
return m
|
|
488
|
-
})
|
|
534
|
+
line = rewriteInlineImages(line, contentCatalog, assemblyModel, page.src, assembled.assets, true, doc)
|
|
489
535
|
}
|
|
490
536
|
lines[idx] = line
|
|
491
537
|
}
|
|
@@ -511,53 +557,43 @@ function mergeAsciiDoc (
|
|
|
511
557
|
return '='.repeat(targetMarkerLength) + ' ' + rest
|
|
512
558
|
})
|
|
513
559
|
// NOTE: ID will be undefined if sectids are turned off
|
|
514
|
-
if (block.getId()) rewriteStyleAttribute(block, lines, idx,
|
|
560
|
+
if (block.getId()) rewriteStyleAttribute(block, lines, idx, pageIdLeader, blockStyle)
|
|
515
561
|
} else {
|
|
516
562
|
if (context === 'image') {
|
|
517
563
|
let line = lines[idx] || ''
|
|
518
564
|
let prefix = ''
|
|
519
565
|
// Q: can we use startsWith('image::') in certain cases?
|
|
520
566
|
let imageMacroOffset = (
|
|
521
|
-
lastImageMacroAt?.[0] === idx ? line.
|
|
567
|
+
lastImageMacroAt?.[0] === idx ? line.substring(0, lastImageMacroAt[1]) : line
|
|
522
568
|
).lastIndexOf('image::')
|
|
523
569
|
if (imageMacroOffset > 0) {
|
|
524
570
|
if (
|
|
525
571
|
block.getDocument().isNested() &&
|
|
526
|
-
(prefix = line.
|
|
572
|
+
(prefix = line.substring(0, imageMacroOffset)).trimRight().endsWith('|')
|
|
527
573
|
) {
|
|
528
|
-
line = line.
|
|
574
|
+
line = line.substring(prefix.length)
|
|
529
575
|
} else {
|
|
530
576
|
imageMacroOffset = -1
|
|
531
577
|
}
|
|
532
578
|
}
|
|
533
|
-
if (imageMacroOffset
|
|
579
|
+
if (~imageMacroOffset) {
|
|
534
580
|
const target = block.getAttribute('target')
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
// FIXME: handle (or report) case when image is not resolved
|
|
538
|
-
if (image?.out && (filetype !== 'html' || siteRoot)) {
|
|
539
|
-
const attrlist = line.slice(line.indexOf('[') + 1, -1)
|
|
540
|
-
pagesInOutline.assembled.assets.add(image)
|
|
541
|
-
lines[idx] =
|
|
542
|
-
filetype === 'html'
|
|
543
|
-
? `${prefix}image::${resolveLinkTarget(image, siteRoot, pubRoot, linkRefStyle, false)}[${attrlist}]`
|
|
544
|
-
: `${prefix}image::${resolveEmbedTarget(image, outDirname, embedRefStyle)}[${attrlist}]`
|
|
545
|
-
}
|
|
546
|
-
}
|
|
581
|
+
const newTarget = rewriteImageRef(target, contentCatalog, assemblyModel, page.src, assembled.assets)
|
|
582
|
+
if (newTarget) lines[idx] = `${prefix}image::${newTarget}${line.substring(line.indexOf('['))}`
|
|
547
583
|
lastImageMacroAt = [idx, imageMacroOffset]
|
|
548
584
|
}
|
|
549
585
|
} else if (context === 'document' && block.hasHeader()) {
|
|
550
586
|
// nested document
|
|
551
587
|
idx = (block.getHeader().getLineNumber() || idx + 1) - 1
|
|
552
588
|
}
|
|
553
|
-
if (block.getId()) rewriteStyleAttribute(block, lines, idx,
|
|
589
|
+
if (block.getId()) rewriteStyleAttribute(block, lines, idx, pageIdLeader)
|
|
554
590
|
}
|
|
555
591
|
})
|
|
556
592
|
safePush(
|
|
557
593
|
buffer,
|
|
558
594
|
lines.filter((it) => it !== undefined)
|
|
559
595
|
)
|
|
560
|
-
const attributeEntries = Object.entries(doc.source_header_attributes
|
|
596
|
+
const attributeEntries = Object.entries(doc.source_header_attributes.$$smap)
|
|
561
597
|
if (attributeEntries.length) {
|
|
562
598
|
const resolvedAttributeEntries = attributeEntries.reduce(
|
|
563
599
|
(accum, [name, val]) => {
|
|
@@ -569,7 +605,7 @@ function mergeAsciiDoc (
|
|
|
569
605
|
} else if (val !== initialVal) {
|
|
570
606
|
accum.push(`:${name}:${initialVal ? ' ' + initialVal : ''}`)
|
|
571
607
|
}
|
|
572
|
-
} else if (!(val == null || doc.isAttributeLocked(name) ||
|
|
608
|
+
} else if (!(val == null || doc.isAttributeLocked(name) || DISCARD_ATTRIBUTE_NAMES.includes(name))) {
|
|
573
609
|
accum.push(`:!${name}:`)
|
|
574
610
|
}
|
|
575
611
|
return accum
|
|
@@ -579,26 +615,13 @@ function mergeAsciiDoc (
|
|
|
579
615
|
if (resolvedAttributeEntries.length > 1) safePush(buffer, resolvedAttributeEntries)
|
|
580
616
|
}
|
|
581
617
|
} else if (level) {
|
|
582
|
-
if (atDocumentRoot && navtitlePlain === componentVersion.title) {
|
|
618
|
+
if (atDocumentRoot && rootLevel === 0 && navtitlePlain === componentVersion.title) {
|
|
583
619
|
buffer.inBody ??= false
|
|
584
620
|
level--
|
|
585
621
|
} else {
|
|
586
622
|
buffer.inBody = true
|
|
587
623
|
buffer.push('')
|
|
588
|
-
|
|
589
|
-
// Q: should we unset docname, page-module, etc?
|
|
590
|
-
let toggleSectids, syntheticId
|
|
591
|
-
if (!('sectids' in asciidocConfig.attributes)) {
|
|
592
|
-
buffer.push(':!sectids:')
|
|
593
|
-
toggleSectids = true
|
|
594
|
-
} else if (typeof asciidocConfig.attributes.sectids === 'string') {
|
|
595
|
-
if ('sectids' in mutableAttributes) {
|
|
596
|
-
buffer.push(':!sectids:')
|
|
597
|
-
toggleSectids = true
|
|
598
|
-
} else {
|
|
599
|
-
syntheticId = `__object-id-${global.Opal.hash(outlineEntry).$object_id()}`
|
|
600
|
-
}
|
|
601
|
-
}
|
|
624
|
+
const syntheticId = idSeparators.generateIdFromTitle(navtitleAsciiDoc, idSeparators)
|
|
602
625
|
let sectionTitle = navtitleAsciiDoc
|
|
603
626
|
if (urlType === 'external') {
|
|
604
627
|
sectionTitle = `${url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
@@ -606,27 +629,19 @@ function mergeAsciiDoc (
|
|
|
606
629
|
const resource = files.find((it) => it.pub.url === url)
|
|
607
630
|
if (resource) {
|
|
608
631
|
if (resource.src.family === 'page' && pagesInOutline.has(resource.pub.url)) {
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
if (resource.src.module !== 'ROOT') refid = `${resource.src.module}${idCoordinateSeparator}${refid}`
|
|
612
|
-
sectionTitle = `<<${refid},${navtitleAsciiDoc}>>`
|
|
632
|
+
const refid = generateScopedId(resource.src, componentVersion, idSeparators, filetype, true)
|
|
633
|
+
sectionTitle = `xref:${refid}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
613
634
|
} else if (siteRoot) {
|
|
614
|
-
sectionTitle = `${resolveLinkTarget(resource, siteRoot, pubRoot,
|
|
635
|
+
sectionTitle = `${resolveLinkTarget(resource, siteRoot, pubRoot, linkReferenceStyle, true)}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
615
636
|
}
|
|
616
637
|
}
|
|
617
638
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
buffer.push(syntheticId ? `[discrete#${syntheticId}]` : '[discrete]')
|
|
622
|
-
} else if (syntheticId) {
|
|
623
|
-
buffer.push(`[#${syntheticId}]`)
|
|
624
|
-
}
|
|
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, '')}]`)
|
|
625
642
|
buffer.push(`${'='.repeat(hlevel)} ${sectionTitle}`)
|
|
626
|
-
if (toggleSectids) buffer.push(':sectids:')
|
|
627
643
|
}
|
|
628
644
|
}
|
|
629
|
-
|
|
630
645
|
if (hasItems) {
|
|
631
646
|
const nextLevel = level + 1
|
|
632
647
|
// NOTE: drop first child if same as parent; should we keep if content is different?
|
|
@@ -642,6 +657,7 @@ function mergeAsciiDoc (
|
|
|
642
657
|
item,
|
|
643
658
|
files,
|
|
644
659
|
pagesInOutline,
|
|
660
|
+
idSeparators,
|
|
645
661
|
asciidocConfig,
|
|
646
662
|
mutableAttributes,
|
|
647
663
|
assemblyModel,
|
|
@@ -654,11 +670,32 @@ function mergeAsciiDoc (
|
|
|
654
670
|
return buffer
|
|
655
671
|
}
|
|
656
672
|
|
|
657
|
-
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
|
+
}
|
|
658
696
|
const doctitleIdx = doc.getHeader().getLineNumber() - 1
|
|
659
697
|
const end = doc.getBlocks()[0]?.getLineNumber() ?? lines.length
|
|
660
|
-
let belowDoctitle
|
|
661
|
-
let open
|
|
698
|
+
let belowDoctitle, current, open
|
|
662
699
|
const implicitLines = []
|
|
663
700
|
for (let idx = 0; idx < end; idx++) {
|
|
664
701
|
if (idx === doctitleIdx) {
|
|
@@ -669,41 +706,51 @@ function processDocumentHeader (doc, lines, buffer, ignoreLines) {
|
|
|
669
706
|
}
|
|
670
707
|
const line = lines[idx]
|
|
671
708
|
if (open === ':' || open === '-:') {
|
|
672
|
-
if (line)
|
|
673
|
-
|
|
674
|
-
if (!line.endsWith(' \\')) open = undefined
|
|
675
|
-
} else {
|
|
676
|
-
open = undefined
|
|
677
|
-
}
|
|
709
|
+
if (open === ':') current.lines.push(line)
|
|
710
|
+
if (!line?.endsWith(' \\')) current = open = undefined
|
|
678
711
|
} else if (line) {
|
|
679
|
-
const chr0 = line.charAt()
|
|
680
712
|
let attributeEntryMatch
|
|
713
|
+
const chr0 = line.charAt()
|
|
681
714
|
if (chr0 === '/' && line.charAt(1) === '/') {
|
|
682
|
-
if (line.startsWith('////')) {
|
|
683
|
-
|
|
684
|
-
|
|
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) === '/') {
|
|
685
725
|
break
|
|
726
|
+
} else {
|
|
727
|
+
entries.push({ type: 'line_comment', lines: [line] })
|
|
728
|
+
current = undefined
|
|
686
729
|
}
|
|
687
|
-
buffer.push(line)
|
|
688
730
|
} else if (open) {
|
|
689
|
-
|
|
690
|
-
} else if (chr0 === ':' && ~line.indexOf(':', 2) && (attributeEntryMatch = line.match(
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
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
|
|
694
738
|
} else {
|
|
695
739
|
if (line.endsWith(' \\')) open = ':'
|
|
696
|
-
|
|
740
|
+
entries.push((current = { type: 'attribute_entry', name, negated, lines: [line], lineno: idx + 1 }))
|
|
741
|
+
if (assemblyHeaderAttributes.has(name)) current.promote = true
|
|
697
742
|
}
|
|
698
743
|
} else if (belowDoctitle) {
|
|
699
744
|
if (implicitLines.length === 2 || !/[\p{Alpha}0-9]/u.test(chr0)) break
|
|
700
745
|
implicitLines.push(line)
|
|
701
746
|
} else if (chr0 === '[' && line.charAt(line.length - 1) === ']') {
|
|
747
|
+
current = undefined
|
|
702
748
|
const attrlist = line
|
|
703
|
-
.
|
|
749
|
+
.substring(1, line.length - 1)
|
|
704
750
|
.trim()
|
|
705
|
-
.replace(/(?:^\w[\w-]*(?!=|[\w-]))?([
|
|
706
|
-
|
|
751
|
+
.replace(/(?:^\w[\w-]*(?!=|[\w-]))?([#.]\w[\w-]*)*/, '')
|
|
752
|
+
.replace(NAMED_ID_ATTR_RX, '')
|
|
753
|
+
if (attrlist) entries.push({ type: 'attrlist', lines: ['[' + attrlist + ']'] })
|
|
707
754
|
}
|
|
708
755
|
} else if (belowDoctitle) {
|
|
709
756
|
break
|
|
@@ -711,23 +758,26 @@ function processDocumentHeader (doc, lines, buffer, ignoreLines) {
|
|
|
711
758
|
lines[idx] = undefined
|
|
712
759
|
ignoreLines.push(idx)
|
|
713
760
|
}
|
|
714
|
-
return
|
|
715
|
-
.getRoles()
|
|
716
|
-
.map((role) => '.' + role)
|
|
717
|
-
.join('')
|
|
761
|
+
return entries
|
|
718
762
|
}
|
|
719
763
|
|
|
720
|
-
function generateSlug (
|
|
721
|
-
return
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
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')
|
|
731
781
|
}
|
|
732
782
|
|
|
733
783
|
function fixSectionLevels (sections, expectedLevel) {
|
|
@@ -738,64 +788,6 @@ function fixSectionLevels (sections, expectedLevel) {
|
|
|
738
788
|
})
|
|
739
789
|
}
|
|
740
790
|
|
|
741
|
-
function rewriteStyleAttribute (block, lines, idx, idPrefix, replacementStyle = '') {
|
|
742
|
-
let prevLine = lines[idx - 1]
|
|
743
|
-
const char0 = prevLine?.charAt()
|
|
744
|
-
if (char0) {
|
|
745
|
-
if (
|
|
746
|
-
(char0 === '.' && /^\.\.?[^ \t.]/.test(prevLine)) ||
|
|
747
|
-
(char0 === '[' &&
|
|
748
|
-
prevLine.charAt(1) === '[' &&
|
|
749
|
-
/^\[\[(?:|[\p{Alpha}_:][\p{Alpha}0-9_\-:.]*(?:, *.+)?)\]\]$/u.test(prevLine))
|
|
750
|
-
) {
|
|
751
|
-
return rewriteStyleAttribute(block, lines, idx - 1, idPrefix, replacementStyle)
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
let cellSpec
|
|
755
|
-
if (
|
|
756
|
-
char0 &&
|
|
757
|
-
(char0 === '[' || (block.getDocument().isNested() && (cellSpec = prevLine.match(/^([^[|]*)\| *(\[.+)/)))) &&
|
|
758
|
-
prevLine.charAt(prevLine.length - 1) === ']'
|
|
759
|
-
) {
|
|
760
|
-
if (cellSpec) {
|
|
761
|
-
prevLine = cellSpec[2]
|
|
762
|
-
cellSpec = cellSpec[1]
|
|
763
|
-
}
|
|
764
|
-
let rawStyle
|
|
765
|
-
const commaIdx = prevLine.indexOf(',')
|
|
766
|
-
if (~commaIdx) {
|
|
767
|
-
rawStyle = prevLine.slice(1, commaIdx)
|
|
768
|
-
if (~rawStyle.indexOf('=')) rawStyle = undefined
|
|
769
|
-
} else if (!~prevLine.indexOf('=')) {
|
|
770
|
-
rawStyle = prevLine.slice(1, prevLine.length - 1)
|
|
771
|
-
}
|
|
772
|
-
if (rawStyle) {
|
|
773
|
-
if (~rawStyle.indexOf('#')) {
|
|
774
|
-
prevLine = prevLine.replace(/#[^.%,\]]+/, `#${idPrefix}${block.getId()}`)
|
|
775
|
-
if (replacementStyle) prevLine = prevLine.replace(/^[^#.%,\]]+/, `[${replacementStyle}`)
|
|
776
|
-
} else {
|
|
777
|
-
prevLine = `[${
|
|
778
|
-
replacementStyle ? rawStyle.replace(/^[^.%]*/, replacementStyle) : rawStyle
|
|
779
|
-
}#${idPrefix}${block.getId()}${prevLine.slice(rawStyle.length + 1)}`
|
|
780
|
-
}
|
|
781
|
-
} else {
|
|
782
|
-
prevLine = `[${replacementStyle}#${idPrefix}${block.getId()}${rawStyle == null ? ',' : ''}${prevLine.slice(1)}`
|
|
783
|
-
}
|
|
784
|
-
if (cellSpec) prevLine = `${cellSpec}|${prevLine}`
|
|
785
|
-
lines[idx - 1] = prevLine
|
|
786
|
-
} else {
|
|
787
|
-
lines.splice(idx, 0, `[${replacementStyle}#${idPrefix}${block.getId()}]`)
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
function isResourceSpec (str) {
|
|
792
|
-
return !(~str.indexOf(':') && (~str.indexOf('://') || (str.startsWith('data:') && ~str.indexOf(','))))
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
function getObjectId (obj) {
|
|
796
|
-
return global.Opal.id(obj)
|
|
797
|
-
}
|
|
798
|
-
|
|
799
791
|
// NOTE: blank lines at top and bottom of document create mismatch when using line numbers to navigate source lines
|
|
800
792
|
// IMPORTANT: this must not leave behind lines the parser will drop!
|
|
801
793
|
// IDEA: another option is to capture initial lineno of reader and use as offset (but preseves those blank lines)
|
|
@@ -818,35 +810,11 @@ function safePush (onto, entries) {
|
|
|
818
810
|
}
|
|
819
811
|
}
|
|
820
812
|
|
|
821
|
-
function
|
|
822
|
-
const
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
function resolveLinkTarget (resource, siteRoot, pubRoot, referenceStyle, escapeForInline = true) {
|
|
828
|
-
let target
|
|
829
|
-
if (resource.site?.url) {
|
|
830
|
-
target = ['', resource.pub.url]
|
|
831
|
-
} else {
|
|
832
|
-
switch (referenceStyle) {
|
|
833
|
-
case 'absolute':
|
|
834
|
-
target = ['', siteRoot.url + resource.pub.url]
|
|
835
|
-
break
|
|
836
|
-
case 'root-relative':
|
|
837
|
-
target = ['link:', siteRoot.path + resource.pub.url]
|
|
838
|
-
break
|
|
839
|
-
default:
|
|
840
|
-
target = ['link:', computeRelativeUrl(pubRoot + '/', resource.pub.url)]
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
if (escapeForInline) target[1] = target[1].replace(/_/g, '{underscore}')
|
|
844
|
-
return target.join('')
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
function computeRelativeUrl (from, to) {
|
|
848
|
-
const rel = path.relative(from, to)
|
|
849
|
-
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}`
|
|
850
818
|
}
|
|
851
819
|
|
|
852
820
|
module.exports = produceAssemblyFile
|