@antora/assembler 1.0.0-alpha.8 → 1.0.0-beta.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/README.md +7 -4
- package/lib/assemble-content.js +213 -26
- package/lib/configure.js +96 -0
- package/lib/index.js +2 -2
- package/lib/load-config.js +41 -24
- package/lib/{produce-aggregate-document.js → produce-assembly-file.js} +322 -166
- package/lib/produce-assembly-files.js +146 -0
- package/lib/util/compute-out.js +57 -0
- package/lib/util/create-asciidoc-file.js +18 -0
- package/lib/util/sanitize.js +3 -5
- package/lib/util/unconvert-inline-asciidoc.js +95 -0
- package/package.json +18 -13
- package/lib/asciidoctor/reducer-extension.js +0 -235
- package/lib/produce-aggregate-documents.js +0 -142
- package/lib/util/run-command.js +0 -60
package/README.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# Antora Assembler
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Antora Assembler provides a JavaScript library and a set of Antora extensions for merging content in an Antora site into assembly files and converting those files to another format, such as PDF.
|
|
4
4
|
|
|
5
|
-
Assembler
|
|
6
|
-
It
|
|
7
|
-
|
|
5
|
+
The core of Assembler (this package) handles merging AsciiDoc content from multiple pages into an assembly.
|
|
6
|
+
It repeats this process for each selected component version.
|
|
7
|
+
It then delegates to a function or command (e.g., asciidoctor-pdf) designated by an exporter extension (a separate package) to convert the assembly files to an export format, such as PDF, resulting in an export.
|
|
8
|
+
Assembler then publishes the export files along with the other files in the site.
|
|
9
|
+
|
|
10
|
+
The exports can then be viewed offline, either by the browser or a reader application.
|
|
8
11
|
|
|
9
12
|
[Antora](https://antora.org) is a modular static site generator designed for creating documentation sites from AsciiDoc documents.
|
|
10
13
|
The Assembler extends the feature set of Antora by providing the foundation for page aggregation.
|
package/lib/assemble-content.js
CHANGED
|
@@ -1,46 +1,233 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const computeOut = require('./util/compute-out')
|
|
4
|
+
const fs = require('node:fs')
|
|
5
|
+
const { promises: fsp } = fs
|
|
6
|
+
const LazyReadable = require('./util/lazy-readable')
|
|
3
7
|
const loadConfig = require('./load-config')
|
|
4
|
-
const
|
|
8
|
+
const ospath = require('node:path')
|
|
9
|
+
const { posix: path } = ospath
|
|
10
|
+
const produceAssemblyFiles = require('./produce-assembly-files')
|
|
5
11
|
const PromiseQueue = require('./util/promise-queue')
|
|
6
|
-
const
|
|
12
|
+
const { stringify: toJSON } = JSON
|
|
7
13
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return this.resolvePage('index.adoc', { component, version })
|
|
13
|
-
}
|
|
14
|
-
}
|
|
14
|
+
const invariably = { new: () => ({}), void: () => undefined }
|
|
15
|
+
const PACKAGE_NAME = require('../package.json').name
|
|
16
|
+
|
|
17
|
+
async function assembleContent (playbook, contentCatalog, converter, { configSource, navigationCatalog }) {
|
|
15
18
|
const assemblerConfig = await loadConfig(playbook, configSource)
|
|
16
19
|
if (!assemblerConfig) return [] // TODO consider removing and doing this another way
|
|
17
|
-
const
|
|
20
|
+
const context = isBound(this)
|
|
21
|
+
? this
|
|
22
|
+
: {
|
|
23
|
+
getFunctions: invariably.new,
|
|
24
|
+
getLogger: invariably.void,
|
|
25
|
+
getVariables: invariably.new,
|
|
26
|
+
}
|
|
27
|
+
const generatorFunctions = context.getFunctions()
|
|
18
28
|
const { loadAsciiDoc = require('@antora/asciidoc-loader') } = generatorFunctions
|
|
19
|
-
const
|
|
20
|
-
|
|
29
|
+
const targetBackend = converter == null ? undefined : (converter.backend ?? converter.extname?.slice(1))
|
|
30
|
+
const { assembly: assemblyConfig, build: buildConfig } = assemblerConfig
|
|
31
|
+
const { profile = targetBackend } = assemblyConfig
|
|
32
|
+
const intrinsicAttributes = { 'loader-assembler': '' }
|
|
33
|
+
if (profile) {
|
|
34
|
+
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), `build/assembler-${profile}`)
|
|
35
|
+
intrinsicAttributes[`assembler-profile-${profile}`] = ''
|
|
36
|
+
intrinsicAttributes['assembler-profile'] = profile
|
|
37
|
+
if (targetBackend) {
|
|
38
|
+
intrinsicAttributes[`assembler-backend-${targetBackend}`] = ''
|
|
39
|
+
intrinsicAttributes['assembler-backend'] = targetBackend
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), 'build/assembler')
|
|
43
|
+
}
|
|
44
|
+
Object.assign(assemblerConfig.asciidoc.attributes, intrinsicAttributes)
|
|
45
|
+
const assemblyFiles = produceAssemblyFiles(
|
|
46
|
+
loadAsciiDoc,
|
|
47
|
+
contentCatalog,
|
|
48
|
+
assemblerConfig,
|
|
49
|
+
createResolveAssemblyModel(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog)
|
|
50
|
+
)
|
|
51
|
+
const { convert = converter, extname: targetExtname, mediaType: targetMediaType } = converter ?? {}
|
|
52
|
+
if (!(convert && assemblyFiles.length)) return assemblyFiles
|
|
21
53
|
const { publishSite: publishFiles = require('@antora/site-publisher') } = generatorFunctions
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// TODO: pass more information to converter so it doesn't have to compute internal stuff
|
|
25
|
-
// Q: don't we need to pass in the combined/resolved AsciiDoc attributes per file or component version?
|
|
54
|
+
await prepareWorkspace(publishFiles, assemblyFiles, contentCatalog, buildConfig)
|
|
55
|
+
const boundConvert = convert.bind(context)
|
|
26
56
|
return new PromiseQueue({ concurrency: buildConfig.processLimit })
|
|
27
|
-
.add(
|
|
57
|
+
.add(
|
|
58
|
+
assemblyFiles.map((doc) => async () => {
|
|
59
|
+
const convertAttributes = prepareConvertAttributes(doc, targetExtname, buildConfig)
|
|
60
|
+
if (buildConfig.mkdirs) await fsp.mkdir(convertAttributes.outdir, { recursive: true, force: true })
|
|
61
|
+
return boundConvert(doc, convertAttributes, buildConfig).then(
|
|
62
|
+
(fileOrContents = new LazyReadable(() => fs.createReadStream(convertAttributes.outfile))) => {
|
|
63
|
+
return coerceToExportFormat(doc, targetBackend, targetExtname, targetMediaType, fileOrContents)
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
})
|
|
67
|
+
)
|
|
28
68
|
.toPromise()
|
|
29
69
|
.then((files) => {
|
|
30
|
-
if (buildConfig.publish
|
|
31
|
-
|
|
70
|
+
if (!buildConfig.publish) {
|
|
71
|
+
for (const file of files) delete file.assembler.assembled
|
|
72
|
+
return files
|
|
73
|
+
}
|
|
74
|
+
const qualifyExports = buildConfig.qualifyExports
|
|
75
|
+
return files.map((file) => {
|
|
76
|
+
const pages = file.assembler.assembled.pages
|
|
77
|
+
delete file.assembler.assembled
|
|
78
|
+
file = contentCatalog.addFile(Object.assign(file, { out: computeOut.call(contentCatalog, file.src) }))
|
|
79
|
+
const extname = file.extname
|
|
80
|
+
const download = file.assembler.downloadStem + extname
|
|
81
|
+
if (qualifyExports) {
|
|
82
|
+
file.pub.url = '/' + (file.out.path = path.join(file.out.dirname, (file.out.basename = download)))
|
|
83
|
+
} else {
|
|
84
|
+
file.pub.download = download
|
|
85
|
+
}
|
|
86
|
+
pages.forEach((fragment, page) => {
|
|
87
|
+
const assemblerMeta = (page.assembler ??= {})
|
|
88
|
+
;(assemblerMeta.exports ??= []).push({ fragment, file })
|
|
89
|
+
const extnameProp = extname.slice(1)
|
|
90
|
+
if (extnameProp in assemblerMeta) return
|
|
91
|
+
Object.defineProperty(assemblerMeta, extnameProp, {
|
|
92
|
+
configurable: true,
|
|
93
|
+
enumerable: true,
|
|
94
|
+
get: findFirstExportWithExtname.bind(assemblerMeta, extname),
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
return file
|
|
98
|
+
})
|
|
32
99
|
})
|
|
33
100
|
}
|
|
34
101
|
|
|
102
|
+
function createResolveAssemblyModel (context, contentCatalog, defaults, intrinsicAttributes, navigationCatalog) {
|
|
103
|
+
const { assemblerProfiles } = context.getVariables()
|
|
104
|
+
const { insertStartPage, rootLevel, sectionMergeStrategy } = defaults
|
|
105
|
+
if (!assemblerProfiles) {
|
|
106
|
+
return (componentVersion) => {
|
|
107
|
+
const navigation =
|
|
108
|
+
navigationCatalog?.getNavigation(componentVersion.name, componentVersion.version) ?? componentVersion.navigation
|
|
109
|
+
return { insertStartPage, rootLevel, sectionMergeStrategy, navigation }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const boundSendToLog = sendToLog.bind(context.getLogger ? context.getLogger(PACKAGE_NAME) : undefined)
|
|
113
|
+
const { buildNavigation = require('@antora/navigation-builder') } = context.getFunctions()
|
|
114
|
+
return (componentVersion) => {
|
|
115
|
+
const componentVersionProfiles = assemblerProfiles.get(componentVersion.version + '@' + componentVersion.name)
|
|
116
|
+
const { navFiles, messages, ...model } = Object.assign(
|
|
117
|
+
{ insertStartPage, rootLevel, sectionMergeStrategy },
|
|
118
|
+
componentVersionProfiles?.get(intrinsicAttributes['assembler-profile']) ?? componentVersionProfiles?.get()
|
|
119
|
+
)
|
|
120
|
+
const navigationOverride = navigationCatalog?.getNavigation(componentVersion.name, componentVersion.version)
|
|
121
|
+
if (navigationOverride) return Object.assign(model, { navigation: navigationOverride })
|
|
122
|
+
messages?.forEach(boundSendToLog)
|
|
123
|
+
model.navigation = componentVersion.navigation
|
|
124
|
+
if (!navFiles) return model
|
|
125
|
+
if (!navFiles.length) return Object.assign(model, { navigation: [] })
|
|
126
|
+
const navigation_ = model.navigation
|
|
127
|
+
const asciidoc_ = componentVersion.asciidoc
|
|
128
|
+
componentVersion.asciidoc = Object.assign({}, asciidoc_, {
|
|
129
|
+
attributes: Object.assign({}, asciidoc_.attributes, intrinsicAttributes),
|
|
130
|
+
})
|
|
131
|
+
buildNavigation(
|
|
132
|
+
new Proxy(contentCatalog, {
|
|
133
|
+
get (target, property) {
|
|
134
|
+
const method = target[property]
|
|
135
|
+
if (property !== 'findBy') return method
|
|
136
|
+
return (criteria) => (toJSON(criteria) === '{"family":"nav"}' ? navFiles : method.call(target, criteria))
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
)
|
|
140
|
+
model.navigation = componentVersion.navigation
|
|
141
|
+
Object.assign(componentVersion, { asciidoc: asciidoc_, navigation: navigation_ })
|
|
142
|
+
return model
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function prepareConvertAttributes (doc, targetExtname, buildConfig) {
|
|
147
|
+
const {
|
|
148
|
+
asciidoc: { attributes: docAttributes } = { attributes: {} },
|
|
149
|
+
extname: docfilesuffix,
|
|
150
|
+
path: reldocfile,
|
|
151
|
+
src: { family, relative },
|
|
152
|
+
} = doc
|
|
153
|
+
const { cwd = process.cwd(), dir = cwd } = buildConfig
|
|
154
|
+
const docname = family + '$' + relative.slice(0, relative.length - docfilesuffix.length)
|
|
155
|
+
const docfile = ospath.join(dir, reldocfile)
|
|
156
|
+
const docdir = dir
|
|
157
|
+
const imagesdir = ''
|
|
158
|
+
const outfile = docfile.slice(0, docfile.length - docfilesuffix.length) + targetExtname
|
|
159
|
+
const attributes = Object.assign({}, docAttributes, {
|
|
160
|
+
docdir,
|
|
161
|
+
docfile,
|
|
162
|
+
docfilesuffix,
|
|
163
|
+
'docname@': docname,
|
|
164
|
+
imagesdir,
|
|
165
|
+
outdir: ospath.dirname(docfile),
|
|
166
|
+
outfile,
|
|
167
|
+
outfilesuffix: targetExtname,
|
|
168
|
+
toArgs (optionFlag, command) {
|
|
169
|
+
const padCharRef = process.platform === 'win32' && command.startsWith('bundle exec ')
|
|
170
|
+
const args = []
|
|
171
|
+
for (let [name, val] of Object.entries(this)) {
|
|
172
|
+
if (val) {
|
|
173
|
+
val = `${name}=${padCharRef && typeof val.charAt === 'function' && val.charAt() === '&' ? ' ' : ''}${val}`
|
|
174
|
+
} else if (val === '') {
|
|
175
|
+
val = name
|
|
176
|
+
} else {
|
|
177
|
+
val = `!${name}${val === false ? '@' : ''}`
|
|
178
|
+
}
|
|
179
|
+
args.push('-a', val)
|
|
180
|
+
}
|
|
181
|
+
return args
|
|
182
|
+
},
|
|
183
|
+
})
|
|
184
|
+
return Object.defineProperty(attributes, 'toArgs', { enumerable: false })
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function coerceToExportFormat (originalFile, targetBackend, targetExtname, targetMediaType, fileOrContents) {
|
|
188
|
+
const file =
|
|
189
|
+
fileOrContents == null || Buffer.isBuffer(fileOrContents) || typeof fileOrContents.pipe === 'function'
|
|
190
|
+
? Object.assign(originalFile, { contents: fileOrContents })
|
|
191
|
+
: fileOrContents
|
|
192
|
+
;(file.assembler ??= {}).backend = targetBackend
|
|
193
|
+
if (file.extname === targetExtname) return file
|
|
194
|
+
const sourcePath = file.path
|
|
195
|
+
const sourceExtname = file.extname
|
|
196
|
+
const relativeWithoutExtname = file.src.relative.slice(0, file.src.relative.length - sourceExtname.length)
|
|
197
|
+
const newPath = sourcePath.slice(0, sourcePath.length - sourceExtname.length) + targetExtname
|
|
198
|
+
Object.assign(file, { mediaType: (file.src.mediaType = targetMediaType), path: newPath })
|
|
199
|
+
file.src.basename = path.basename((file.src.relative = relativeWithoutExtname + (file.src.extname = targetExtname)))
|
|
200
|
+
return file
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function findFirstExportWithExtname (extname) {
|
|
204
|
+
return this.exports.find((it) => it.file.extname === extname)
|
|
205
|
+
}
|
|
206
|
+
|
|
35
207
|
// TODO: if no workspace dir is defined, we shouldn't continue
|
|
36
|
-
function prepareWorkspace (publishFiles,
|
|
37
|
-
const { dir, clean,
|
|
38
|
-
const files =
|
|
39
|
-
|
|
40
|
-
|
|
208
|
+
function prepareWorkspace (publishFiles, assemblyFiles, contentCatalog, buildConfig) {
|
|
209
|
+
const { dir, clean, keepSource } = buildConfig
|
|
210
|
+
const files = []
|
|
211
|
+
const outPaths = new Set()
|
|
212
|
+
for (const file of assemblyFiles) {
|
|
213
|
+
for (const asset of file.assembler.assembled.assets) {
|
|
214
|
+
if (outPaths.has(asset.out.path)) continue
|
|
215
|
+
files.push(asset)
|
|
216
|
+
outPaths.add(asset.out.path)
|
|
217
|
+
}
|
|
41
218
|
}
|
|
42
|
-
|
|
43
|
-
return publishFiles({ output: { clean, dir } },
|
|
219
|
+
if (keepSource) files.push(...assemblyFiles.map((file) => Object.assign(file, { out: { path: file.path } })))
|
|
220
|
+
return publishFiles({ output: { clean, dir } }, { getFiles: () => files })
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function isBound (obj) {
|
|
224
|
+
if (obj == null) return false
|
|
225
|
+
for (const p in obj) return true
|
|
226
|
+
return false
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function sendToLog (levelAndArgs) {
|
|
230
|
+
if (this) this[levelAndArgs[0]].apply(this, levelAndArgs.slice(1))
|
|
44
231
|
}
|
|
45
232
|
|
|
46
233
|
module.exports = assembleContent
|
package/lib/configure.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function configure (context, ...args) {
|
|
4
|
+
internalConfigure.apply(context, args)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function internalConfigure (converter, config = {}, providers = {}) {
|
|
8
|
+
this.once('componentsRegistered', ({ contentCatalog, assemblerProfiles }) => {
|
|
9
|
+
this.updateVariables(assemblerProfiles ? undefined : { assemblerProfiles: getAssemblerProfiles(contentCatalog) })
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
this.once('beforeProcess', ({ siteAsciiDocConfig }) => {
|
|
13
|
+
siteAsciiDocConfig.keepSource = true
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
this.once('navigationBuilt', async ({ playbook, contentCatalog }) => {
|
|
17
|
+
const { assembleContent = require('./assemble-content'), ...assembleContentConfig } = providers
|
|
18
|
+
assembleContentConfig.configSource ??= config.configFile
|
|
19
|
+
await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentConfig)
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getAssemblerProfiles (contentCatalog, assemblerProfiles = new Map()) {
|
|
24
|
+
contentCatalog.getComponents().forEach((component) => {
|
|
25
|
+
component.versions.forEach((componentVersion) => {
|
|
26
|
+
const source = componentVersion.nav?.origin
|
|
27
|
+
const assemblerConfig = getAssemblerConfigFromDescriptor(source?.descriptor)
|
|
28
|
+
if (!assemblerConfig) return
|
|
29
|
+
const componentVersionRef = `${componentVersion.version}@${componentVersion.name}`
|
|
30
|
+
const filesByPath = componentVersion.files.reduce((accum, it) => accum.set(it.path, it), new Map())
|
|
31
|
+
const componentVersionProfiles = assemblerConfig.reduce((accum, entry) => {
|
|
32
|
+
const data = {}
|
|
33
|
+
const profile = entry.profile ?? undefined
|
|
34
|
+
if (accum.has(profile)) return accum
|
|
35
|
+
const nav = entry.nav
|
|
36
|
+
Object.entries(entry).forEach(([key, val]) => {
|
|
37
|
+
if (key === 'profile' || key === 'nav') return
|
|
38
|
+
const camelKey = key.toLowerCase().replace(/[_-]([a-z0-9])/g, (_, l, idx) => (idx ? l.toUpperCase() : l))
|
|
39
|
+
data[camelKey] = val
|
|
40
|
+
})
|
|
41
|
+
if (nav) {
|
|
42
|
+
data.navFiles = (nav.length ? [...new Set(nav)] : nav).reduce((navFiles, path_) => {
|
|
43
|
+
const navFile = filesByPath.get(path_)
|
|
44
|
+
if (navFile) {
|
|
45
|
+
navFiles.push(initNavFile(navFile, component.name, componentVersion.version, navFiles.length))
|
|
46
|
+
} else {
|
|
47
|
+
;(data.messages ??= []).push([
|
|
48
|
+
'warn',
|
|
49
|
+
{ source },
|
|
50
|
+
`Could not resolve nav file for ${profile || '<default>'} profile in ${componentVersionRef}: ${path_}`,
|
|
51
|
+
])
|
|
52
|
+
}
|
|
53
|
+
return navFiles
|
|
54
|
+
}, [])
|
|
55
|
+
} else if (!Object.keys(data).length) {
|
|
56
|
+
return accum
|
|
57
|
+
}
|
|
58
|
+
return accum.set(profile, data)
|
|
59
|
+
}, new Map())
|
|
60
|
+
assemblerProfiles.set(componentVersionRef, componentVersionProfiles)
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
return assemblerProfiles
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getAssemblerConfigFromDescriptor (descriptor = {}) {
|
|
67
|
+
let assemblerConfig = descriptor.ext?.assembler
|
|
68
|
+
if (!assemblerConfig) return
|
|
69
|
+
if (Array.isArray(assemblerConfig)) {
|
|
70
|
+
if (!assemblerConfig.length) return
|
|
71
|
+
} else {
|
|
72
|
+
assemblerConfig = [assemblerConfig]
|
|
73
|
+
}
|
|
74
|
+
return assemblerConfig
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function initNavFile (file, component, version, index) {
|
|
78
|
+
const src = Object.assign({}, file.src, { component, version, module: 'ROOT', family: 'nav' })
|
|
79
|
+
const filePathSegments = file.path.split('/')
|
|
80
|
+
let relativeStartIdx = 0
|
|
81
|
+
if (filePathSegments[0] === 'modules') {
|
|
82
|
+
relativeStartIdx += 1
|
|
83
|
+
if (filePathSegments.length > 2) {
|
|
84
|
+
relativeStartIdx += 1
|
|
85
|
+
src.module = filePathSegments[1]
|
|
86
|
+
if (filePathSegments.length > 3 && filePathSegments[2] === 'partials') {
|
|
87
|
+
relativeStartIdx += 1
|
|
88
|
+
src.family = 'partial'
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
src.relative = filePathSegments.slice(relativeStartIdx).join('/')
|
|
93
|
+
return Object.assign(new file.constructor(file), { nav: { index }, src })
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = configure
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const assembleContent = require('./assemble-content')
|
|
4
|
-
const
|
|
4
|
+
const configure = require('./configure')
|
|
5
5
|
|
|
6
|
-
module.exports = { assembleContent,
|
|
6
|
+
module.exports = { assembleContent, configure, configureAssembler: configure }
|
package/lib/load-config.js
CHANGED
|
@@ -10,14 +10,14 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
|
|
|
10
10
|
configSource.constructor === Object
|
|
11
11
|
? Promise.resolve(configSource)
|
|
12
12
|
: fsp
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
.access((configSource = expandPath(configSource, { dot: playbook.dir })))
|
|
14
|
+
.then(
|
|
15
|
+
() => true,
|
|
16
|
+
() => false
|
|
17
|
+
)
|
|
18
|
+
.then((exists) =>
|
|
19
|
+
exists ? fsp.readFile(configSource).then((data) => camelCaseKeys(yaml.load(data), ['asciidoc'])) : {}
|
|
20
|
+
)
|
|
21
21
|
).then((config) => {
|
|
22
22
|
if (config.enabled === false) return undefined
|
|
23
23
|
let asciidocAttrs
|
|
@@ -26,30 +26,47 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
|
|
|
26
26
|
} else if (!(asciidocAttrs = config.asciidoc.attributes)) {
|
|
27
27
|
config.asciidoc.attributes = asciidocAttrs = {}
|
|
28
28
|
}
|
|
29
|
-
if (!('doctype' in asciidocAttrs)) asciidocAttrs.doctype = 'book'
|
|
30
29
|
if (!('revdate' in asciidocAttrs)) asciidocAttrs.revdate = getLocalDate()
|
|
31
30
|
asciidocAttrs['page-partial'] = null
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
const remapComponentVersionsKey = !('componentVersionFilter' in config)
|
|
32
|
+
const componentVersionFilter = (config.componentVersionFilter ??= {})
|
|
33
|
+
if (remapComponentVersionsKey && 'componentVersions' in config) {
|
|
34
|
+
componentVersionFilter.names = config.componentVersions
|
|
35
|
+
delete config.componentVersions
|
|
37
36
|
}
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
|
|
37
|
+
if (componentVersionFilter.names == null) {
|
|
38
|
+
componentVersionFilter.names = ['*']
|
|
39
|
+
} else if (typeof componentVersionFilter.names === 'string') {
|
|
40
|
+
componentVersionFilter.names = componentVersionFilter.names.split(', ')
|
|
42
41
|
}
|
|
43
|
-
const
|
|
42
|
+
const remapAssemblyKeys = !('assembly' in config)
|
|
43
|
+
const assembly = (config.assembly ??= {})
|
|
44
|
+
if (remapAssemblyKeys) {
|
|
45
|
+
for (const key of ['rootLevel', 'insertStartPage', 'sectionMergeStrategy']) {
|
|
46
|
+
if (!(key in config)) continue
|
|
47
|
+
assembly[key] = config[key]
|
|
48
|
+
delete config[key]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (!('doctype' in assembly)) assembly.doctype = 'doctype' in asciidocAttrs ? asciidocAttrs.doctype : 'book'
|
|
52
|
+
delete asciidocAttrs.doctype
|
|
53
|
+
if (!('rootLevel' in assembly)) assembly.rootLevel = 0
|
|
54
|
+
if (!('insertStartPage' in assembly)) assembly.insertStartPage = true
|
|
55
|
+
if (['discrete', 'fuse', 'enclose'].indexOf(assembly.sectionMergeStrategy) < 0) {
|
|
56
|
+
assembly.sectionMergeStrategy = 'discrete'
|
|
57
|
+
}
|
|
58
|
+
const build = (config.build ??= {})
|
|
44
59
|
if (build.dir === '$' + '{playbook.output.dir}') {
|
|
45
|
-
//build.dir = playbook.output.dir
|
|
46
60
|
throw new Error('Not implemented')
|
|
47
|
-
} else {
|
|
48
|
-
build.dir = expandPath(build.dir || './build/assembler', { dot: playbook.dir })
|
|
49
61
|
}
|
|
50
|
-
build.
|
|
62
|
+
build.dir &&= expandPath(build.dir, { dot: playbook.dir })
|
|
63
|
+
build.cwd = playbook.dir // use playbook.dir for finding and loading require scripts
|
|
51
64
|
if (!('clean' in build) && 'output' in playbook) build.clean = playbook.output.clean
|
|
52
65
|
if (!('publish' in build)) build.publish = true
|
|
66
|
+
if ('keepAggregateSource' in build) {
|
|
67
|
+
build.keepSource = build.keepAggregateSource
|
|
68
|
+
delete build.keepAggregateSource
|
|
69
|
+
}
|
|
53
70
|
if (!build.processLimit) {
|
|
54
71
|
build.processLimit = 'processLimit' in build ? Infinity : Math.round(os.cpus().length * 0.5)
|
|
55
72
|
}
|
|
@@ -57,7 +74,7 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
|
|
|
57
74
|
})
|
|
58
75
|
}
|
|
59
76
|
|
|
60
|
-
function camelCaseKeys (o, stopPaths = [], p) {
|
|
77
|
+
function camelCaseKeys (o, stopPaths = [], p = undefined) {
|
|
61
78
|
if (Array.isArray(o)) return o.map((it) => camelCaseKeys(it, stopPaths, p))
|
|
62
79
|
if (o == null || o.constructor !== Object) return o
|
|
63
80
|
const pathPrefix = p ? p + '.' : ''
|