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