@antora/assembler 1.0.0-beta.9 → 1.0.0-rc.10
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 +141 -135
- package/lib/compile-conversion-attributes.js +76 -0
- package/lib/configure.js +44 -16
- package/lib/constants.js +24 -0
- package/lib/filter-component-versions.js +25 -62
- package/lib/index.js +1 -1
- package/lib/load-config.js +84 -43
- package/lib/log-command.js +18 -0
- package/lib/produce-assembly-file.js +539 -509
- package/lib/produce-assembly-files.js +128 -157
- package/lib/util/collate-asciidoc-attributes.js +32 -0
- package/lib/util/create-resource-key.js +7 -0
- package/lib/util/deep-clone.js +18 -0
- package/lib/util/generate-scoped-id.js +26 -0
- package/lib/util/identify-mutable-attributes.js +25 -0
- package/lib/util/lazy-readable.js +12 -3
- 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 +231 -0
- package/lib/util/rx.js +5 -0
- package/lib/util/to-hash.js +7 -0
- package/lib/util/unconvert-inline-asciidoc.js +22 -14
- package/package.json +15 -15
- package/lib/select-mutable-attributes.js +0 -26
- package/lib/util/compute-out.js +0 -57
- package/lib/util/create-asciidoc-file.js +0 -17
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
autoload :JSON, 'json'
|
|
4
|
+
|
|
5
|
+
class JsonlFormatter < Asciidoctor::Logger::BasicFormatter
|
|
6
|
+
def call severity, time, progname, msg
|
|
7
|
+
entry = { 'time' => time.utc.to_i, 'level' => severity.downcase, 'name' => progname }
|
|
8
|
+
if ::String === msg
|
|
9
|
+
entry['msg'] = msg
|
|
10
|
+
else
|
|
11
|
+
loc = msg[:source_location]
|
|
12
|
+
entry['file'] = { 'path' => loc.file, 'line' => loc.lineno }
|
|
13
|
+
entry['msg'] = msg[:text]
|
|
14
|
+
end
|
|
15
|
+
(JSON.generate entry) + ?\n
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
proc do
|
|
20
|
+
logger = Asciidoctor::LoggerManager.logger
|
|
21
|
+
logger.formatter = JsonlFormatter.new
|
|
22
|
+
unless (progname = ::File.basename $PROGRAM_NAME).empty?
|
|
23
|
+
logger.progname = progname
|
|
24
|
+
end
|
|
25
|
+
end.call
|
package/lib/assemble-content.js
CHANGED
|
@@ -1,99 +1,116 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const { promises: fsp } = fs
|
|
3
|
+
const compileConversionAttributes = require('./compile-conversion-attributes')
|
|
4
|
+
const fsp = require('node:fs/promises')
|
|
6
5
|
const LazyReadable = require('./util/lazy-readable')
|
|
7
6
|
const loadConfig = require('./load-config')
|
|
7
|
+
const logCommand = require('./log-command')
|
|
8
8
|
const ospath = require('node:path')
|
|
9
9
|
const { posix: path } = ospath
|
|
10
10
|
const produceAssemblyFiles = require('./produce-assembly-files')
|
|
11
11
|
const PromiseQueue = require('./util/promise-queue')
|
|
12
|
-
const
|
|
12
|
+
const runCommand = require('@antora/run-command-helper')
|
|
13
13
|
|
|
14
|
-
const invariably = { new: () => ({}), void: () => undefined }
|
|
14
|
+
const invariably = { new: () => ({}), null: () => null, void: () => undefined }
|
|
15
15
|
const PACKAGE_NAME = require('../package.json').name
|
|
16
16
|
const NEWLINE_RX = /(?:\r?\n)+/
|
|
17
17
|
|
|
18
|
-
async function assembleContent (playbook, contentCatalog, converter,
|
|
19
|
-
const
|
|
20
|
-
|
|
18
|
+
async function assembleContent (playbook, contentCatalog, converter, providers) {
|
|
19
|
+
const { configSource, navigationCatalog, componentVersionFilter } = providers
|
|
20
|
+
const {
|
|
21
|
+
convert = converter,
|
|
22
|
+
getDefaultCommand,
|
|
23
|
+
extname: targetExtname = '',
|
|
24
|
+
backend: targetBackend = targetExtname.substring(1),
|
|
25
|
+
format: targetFormat = targetBackend,
|
|
26
|
+
embedReferenceStyle = 'relative',
|
|
27
|
+
mediaType: targetMediaType,
|
|
28
|
+
loggerName = `${PACKAGE_NAME} [${targetBackend}-exporter]`,
|
|
29
|
+
xmlCompliant,
|
|
30
|
+
} = converter ?? {}
|
|
31
|
+
const assemblerConfig = await loadConfig.call(this, configSource, playbook, targetBackend ? '-' + targetBackend : '')
|
|
32
|
+
if (assemblerConfig.enabled === false || !contentCatalog.publishableFamilies?.has('export')) return []
|
|
21
33
|
const context = isBound(this)
|
|
22
34
|
? this
|
|
23
35
|
: {
|
|
24
36
|
getFunctions: invariably.new,
|
|
25
37
|
getLogger: invariably.void,
|
|
26
38
|
getVariables: invariably.new,
|
|
39
|
+
require,
|
|
27
40
|
}
|
|
28
41
|
const generatorFunctions = context.getFunctions()
|
|
29
|
-
const { loadAsciiDoc = require('@antora/asciidoc-loader') } = generatorFunctions
|
|
30
|
-
const {
|
|
31
|
-
convert = converter,
|
|
32
|
-
getDefaultCommand,
|
|
33
|
-
extname: targetExtname = '',
|
|
34
|
-
backend: targetBackend = targetExtname.slice(1),
|
|
35
|
-
embedReferenceStyle = 'relative',
|
|
36
|
-
mediaType: targetMediaType,
|
|
37
|
-
loggerName = PACKAGE_NAME,
|
|
38
|
-
} = converter ?? {}
|
|
42
|
+
const { loadAsciiDoc = context.require('@antora/asciidoc-loader') } = generatorFunctions
|
|
39
43
|
const { assembly: assemblyConfig, build: buildConfig } = assemblerConfig
|
|
40
44
|
assemblyConfig.embedReferenceStyle = embedReferenceStyle
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
45
|
+
if (xmlCompliant) assemblyConfig.xmlIds ??= true
|
|
46
|
+
const profile = (assemblyConfig.profile ??= targetBackend)
|
|
47
|
+
const intrinsicAttributes = { 'loader-assembler': '', 'assembler-root-level': assemblyConfig.rootLevel }
|
|
48
|
+
const cwd = (buildConfig.cwd ??= process.cwd())
|
|
44
49
|
if (profile) {
|
|
45
|
-
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), `build/assembler
|
|
50
|
+
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), `build/assembler/${profile}`)
|
|
46
51
|
intrinsicAttributes[`assembler-profile-${profile}`] = ''
|
|
47
52
|
intrinsicAttributes['assembler-profile'] = profile
|
|
48
|
-
if (targetBackend) {
|
|
49
|
-
intrinsicAttributes[`assembler-backend-${targetBackend}`] = ''
|
|
50
|
-
intrinsicAttributes['assembler-backend'] = targetBackend
|
|
51
|
-
}
|
|
52
53
|
} else {
|
|
53
|
-
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), 'build/assembler')
|
|
54
|
+
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), 'build/assembler/_')
|
|
55
|
+
}
|
|
56
|
+
if (targetBackend) {
|
|
57
|
+
intrinsicAttributes[`assembler-backend-${targetBackend}`] = ''
|
|
58
|
+
intrinsicAttributes['assembler-backend'] = targetBackend
|
|
59
|
+
}
|
|
60
|
+
if (targetFormat) {
|
|
61
|
+
intrinsicAttributes[`assembler-format-${targetFormat}`] = ''
|
|
62
|
+
intrinsicAttributes['assembler-format'] = targetFormat
|
|
54
63
|
}
|
|
55
64
|
if (targetExtname) {
|
|
56
|
-
const targetFiletype = targetExtname.
|
|
65
|
+
const targetFiletype = targetExtname.substring(1)
|
|
57
66
|
intrinsicAttributes[`assembler-filetype-${targetFiletype}`] = ''
|
|
58
67
|
intrinsicAttributes['assembler-filetype'] = targetFiletype
|
|
59
68
|
}
|
|
60
|
-
Object.assign(
|
|
69
|
+
Object.assign(assemblyConfig.attributes, intrinsicAttributes)
|
|
61
70
|
const assemblyFiles = produceAssemblyFiles(
|
|
62
71
|
loadAsciiDoc,
|
|
63
72
|
contentCatalog,
|
|
64
73
|
assemblerConfig,
|
|
65
|
-
|
|
74
|
+
generateSelectAssemblerProfile(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog),
|
|
75
|
+
typeof componentVersionFilter === 'function' ? componentVersionFilter : undefined
|
|
66
76
|
)
|
|
67
77
|
if (!(assemblyFiles.length && typeof convert === 'function')) return assemblyFiles
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
const { publishSite: publishFiles = require('@antora/site-publisher') } = generatorFunctions
|
|
78
|
+
buildConfig.command ??= typeof getDefaultCommand === 'function' ? await getDefaultCommand(cwd, playbook) : null
|
|
79
|
+
const { publishSite: publishFiles = context.require('@antora/site-publisher') } = generatorFunctions
|
|
72
80
|
await prepareWorkspace(publishFiles, assemblyFiles, buildConfig)
|
|
81
|
+
const helpers = { runCommand }
|
|
82
|
+
Object.defineProperty(helpers, 'logger', {
|
|
83
|
+
get: () => context?.getLogger(loggerName),
|
|
84
|
+
})
|
|
85
|
+
Object.defineProperty(helpers, 'logCommand', {
|
|
86
|
+
get: () => logCommand.bind(null, helpers.logger),
|
|
87
|
+
})
|
|
73
88
|
const boundConvert = convert.bind(context)
|
|
89
|
+
const boundResolveFileOrContents = resolveFileOrContents.bind(context)
|
|
74
90
|
return new PromiseQueue({ concurrency: buildConfig.processLimit })
|
|
75
91
|
.add(
|
|
76
92
|
assemblyFiles.map((doc) => async () => {
|
|
77
93
|
const relativeToOutput = embedReferenceStyle === 'output-relative'
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
const attributes = await compileConversionAttributes(doc, targetExtname, assemblerConfig, relativeToOutput)
|
|
95
|
+
return boundConvert(doc, attributes, buildConfig, helpers).then((result) =>
|
|
96
|
+
boundResolveFileOrContents(result, attributes, buildConfig, loggerName).then((fileOrContents) =>
|
|
97
|
+
coerceToExportFormat(doc, targetBackend, targetExtname, targetMediaType, fileOrContents)
|
|
98
|
+
)
|
|
99
|
+
)
|
|
84
100
|
})
|
|
85
101
|
)
|
|
86
102
|
.toPromise()
|
|
87
103
|
.then((files) => {
|
|
88
|
-
|
|
104
|
+
const { publish, qualifyExports } = buildConfig
|
|
105
|
+
if (!publish) {
|
|
89
106
|
for (const file of files) delete file.assembler.assembled
|
|
90
107
|
return files
|
|
91
108
|
}
|
|
92
|
-
const qualifyExports = buildConfig.qualifyExports
|
|
93
109
|
return files.map((file) => {
|
|
94
|
-
const pages = file.assembler.assembled.pages
|
|
110
|
+
const pages = file.isNull() ? undefined : file.assembler.assembled.pages
|
|
95
111
|
delete file.assembler.assembled
|
|
96
|
-
|
|
112
|
+
if (!pages) return file
|
|
113
|
+
file = contentCatalog.addFile(file)
|
|
97
114
|
const extname = file.extname
|
|
98
115
|
const download = file.assembler.downloadStem + extname
|
|
99
116
|
if (qualifyExports) {
|
|
@@ -103,8 +120,15 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
103
120
|
}
|
|
104
121
|
pages.forEach((fragment, page) => {
|
|
105
122
|
const assemblerMeta = (page.assembler ??= {})
|
|
106
|
-
|
|
107
|
-
const
|
|
123
|
+
const exports = (assemblerMeta.exports ??= [])
|
|
124
|
+
const exportEntry = { fragment, file }
|
|
125
|
+
if (fragment === pages.rootPageFragment) exportEntry.root = true
|
|
126
|
+
const insertIdx = exports.findIndex(
|
|
127
|
+
({ file: candidate }) =>
|
|
128
|
+
!(candidate.src.component === page.src.component && candidate.src.version === page.src.version)
|
|
129
|
+
)
|
|
130
|
+
~insertIdx ? exports.splice(insertIdx, 0, exportEntry) : exports.push(exportEntry)
|
|
131
|
+
const extnameProp = extname.substring(1)
|
|
108
132
|
if (extnameProp in assemblerMeta) return
|
|
109
133
|
Object.defineProperty(assemblerMeta, extnameProp, {
|
|
110
134
|
configurable: true,
|
|
@@ -117,106 +141,63 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
117
141
|
})
|
|
118
142
|
}
|
|
119
143
|
|
|
120
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Generates a function that selects the active assembler profile, initializes an assembly model using
|
|
146
|
+
* the keys from the profile as well as any inherited shared keys, builds the navigation for the assembly, and
|
|
147
|
+
* returns the initialized assembly model. The assembly model is further populated after the call to this function.
|
|
148
|
+
*/
|
|
149
|
+
function generateSelectAssemblerProfile (context, contentCatalog, baseModel, intrinsicAttributes, navigationCatalog) {
|
|
121
150
|
const logger = context.getLogger?.(PACKAGE_NAME)
|
|
122
151
|
const { assemblerProfiles } = context.getVariables()
|
|
123
152
|
if (!assemblerProfiles) {
|
|
124
153
|
return (componentVersion) => {
|
|
125
154
|
const navigation =
|
|
126
155
|
navigationCatalog?.getNavigation(componentVersion.name, componentVersion.version) ?? componentVersion.navigation
|
|
127
|
-
return Object.assign({
|
|
156
|
+
return Object.assign({}, baseModel, { attributes: Object.assign({}, baseModel.attributes), navigation, logger })
|
|
128
157
|
}
|
|
129
158
|
}
|
|
130
159
|
const boundSendToLog = sendToLog.bind(logger)
|
|
131
|
-
const {
|
|
160
|
+
const { buildAlternateNavigation = context.require('@antora/navigation-builder').buildAlternateNavigation } =
|
|
161
|
+
context.getFunctions()
|
|
132
162
|
return (componentVersion) => {
|
|
133
163
|
const componentVersionProfiles = assemblerProfiles.get(componentVersion.version + '@' + componentVersion.name)
|
|
134
|
-
const overrides =
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
const model = Object.assign({
|
|
164
|
+
const overrides = componentVersionProfiles?.get(baseModel.profile) ?? componentVersionProfiles?.get()
|
|
165
|
+
const navigation = navigationCatalog?.getNavigation(componentVersion.name, componentVersion.version)
|
|
166
|
+
const attributes = Object.assign({}, baseModel.attributes)
|
|
167
|
+
const model = Object.assign({}, baseModel, overrides, { attributes, logger })
|
|
168
|
+
if (!overrides) return Object.assign(model, { navigation: navigation || componentVersion.navigation })
|
|
169
|
+
if ('rootLevel' in overrides) attributes['assembler-root-level'] = overrides.rootLevel
|
|
138
170
|
delete model.navFiles
|
|
139
171
|
delete model.messages
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
172
|
+
if (overrides.attributes) {
|
|
173
|
+
for (const [name, val] of Object.entries(overrides.attributes)) {
|
|
174
|
+
if (!(name in intrinsicAttributes)) attributes[name] = val
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (navigation) return Object.assign(model, { navigation })
|
|
178
|
+
overrides.messages?.forEach(boundSendToLog)
|
|
179
|
+
const navFiles = overrides.navFiles
|
|
180
|
+
if (!navFiles) return Object.assign(model, { navigation: componentVersion.navigation })
|
|
145
181
|
if (!navFiles.length) return Object.assign(model, { navigation: [] })
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
componentVersion.asciidoc = Object.assign({}, asciidoc_, {
|
|
149
|
-
attributes: Object.assign({}, asciidoc_.attributes, intrinsicAttributes),
|
|
182
|
+
model.navigation = buildAlternateNavigation(contentCatalog, componentVersion, navFiles, {
|
|
183
|
+
attributes: model.attributes,
|
|
150
184
|
})
|
|
151
|
-
buildNavigation(
|
|
152
|
-
new Proxy(contentCatalog, {
|
|
153
|
-
get (target, property) {
|
|
154
|
-
const method = target[property]
|
|
155
|
-
if (property !== 'findBy') return method
|
|
156
|
-
return (criteria) => (toJSON(criteria) === '{"family":"nav"}' ? navFiles : method.call(target, criteria))
|
|
157
|
-
},
|
|
158
|
-
})
|
|
159
|
-
)
|
|
160
|
-
model.navigation = componentVersion.navigation
|
|
161
|
-
Object.assign(componentVersion, { asciidoc: asciidoc_, navigation: navigation_ })
|
|
162
185
|
return model
|
|
163
186
|
}
|
|
164
187
|
}
|
|
165
188
|
|
|
166
|
-
function
|
|
167
|
-
const {
|
|
168
|
-
asciidoc: { attributes: docAttributes } = { attributes: {} },
|
|
169
|
-
extname: docfilesuffix,
|
|
170
|
-
path: reldocfile,
|
|
171
|
-
src: { family, relative },
|
|
172
|
-
} = doc
|
|
173
|
-
const { cwd = process.cwd(), dir = cwd } = buildConfig
|
|
174
|
-
const docname = family + '$' + relative.slice(0, relative.length - docfilesuffix.length)
|
|
175
|
-
const docfile = ospath.join(dir, reldocfile)
|
|
176
|
-
const outdir = ospath.dirname(docfile)
|
|
177
|
-
const docdir = relativeToOutput ? dir : outdir
|
|
178
|
-
const imagesdir = ''
|
|
179
|
-
const outfile = docfile.slice(0, docfile.length - docfilesuffix.length) + targetExtname
|
|
180
|
-
const attributes = Object.assign({}, docAttributes, {
|
|
181
|
-
docdir,
|
|
182
|
-
docfile,
|
|
183
|
-
docfilesuffix,
|
|
184
|
-
'docname@': docname,
|
|
185
|
-
imagesdir,
|
|
186
|
-
outdir,
|
|
187
|
-
outfile,
|
|
188
|
-
outfilesuffix: targetExtname,
|
|
189
|
-
toArgs (optionFlag, command) {
|
|
190
|
-
const padCharRef = process.platform === 'win32' && command.startsWith('bundle exec ')
|
|
191
|
-
const args = []
|
|
192
|
-
for (let [name, val] of Object.entries(this)) {
|
|
193
|
-
if (val) {
|
|
194
|
-
val = `${name}=${padCharRef && typeof val.charAt === 'function' && val.charAt() === '&' ? ' ' : ''}${val}`
|
|
195
|
-
} else if (val === '') {
|
|
196
|
-
val = name
|
|
197
|
-
} else {
|
|
198
|
-
val = `!${name}${val === false ? '@' : ''}`
|
|
199
|
-
}
|
|
200
|
-
args.push(optionFlag, val)
|
|
201
|
-
}
|
|
202
|
-
return args
|
|
203
|
-
},
|
|
204
|
-
})
|
|
205
|
-
return Object.defineProperty(attributes, 'toArgs', { enumerable: false })
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function coerceToExportFormat (originalFile, targetBackend, targetExtname, targetMediaType, fileOrContents) {
|
|
189
|
+
function coerceToExportFormat (assemblyFile, targetBackend, targetExtname, targetMediaType, fileOrContents) {
|
|
209
190
|
const file =
|
|
210
191
|
fileOrContents == null || Buffer.isBuffer(fileOrContents) || typeof fileOrContents.pipe === 'function'
|
|
211
|
-
? Object.assign(
|
|
192
|
+
? Object.assign(assemblyFile, { contents: fileOrContents })
|
|
212
193
|
: fileOrContents
|
|
213
194
|
;(file.assembler ??= {}).backend = targetBackend
|
|
214
195
|
if (file.extname === targetExtname) return file
|
|
215
|
-
const sourcePath = file
|
|
216
|
-
const
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
196
|
+
const { path: sourcePath, extname: sourceExtname } = file
|
|
197
|
+
const relativeWithoutExtname = file.src.relative.substring(0, file.src.relative.length - sourceExtname.length)
|
|
198
|
+
const newPath = sourcePath.substring(0, sourcePath.length - sourceExtname.length) + targetExtname
|
|
199
|
+
Object.assign(file, { mediaType: (file.src.mediaType = targetMediaType), path: (file.src.path = newPath) })
|
|
200
|
+
delete file.src.abspath
|
|
220
201
|
file.src.basename = path.basename((file.src.relative = relativeWithoutExtname + (file.src.extname = targetExtname)))
|
|
221
202
|
return file
|
|
222
203
|
}
|
|
@@ -237,41 +218,66 @@ function prepareWorkspace (publishFiles, assemblyFiles, buildConfig) {
|
|
|
237
218
|
outPaths.add(asset.out.path)
|
|
238
219
|
}
|
|
239
220
|
}
|
|
240
|
-
if (keepSource)
|
|
241
|
-
|
|
221
|
+
if (keepSource) {
|
|
222
|
+
for (const file of assemblyFiles) {
|
|
223
|
+
file.out = { path: file.path }
|
|
224
|
+
file.src.contents = file.contents
|
|
225
|
+
file.src.abspath = ospath.join(dir, file.path)
|
|
226
|
+
files.push(file)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return publishFiles({ output: { clean, dir } }, { getFiles: () => files }).then(() => {
|
|
230
|
+
if (keepSource) {
|
|
231
|
+
for (const file of assemblyFiles) delete file.out
|
|
232
|
+
}
|
|
233
|
+
})
|
|
242
234
|
}
|
|
243
235
|
|
|
244
|
-
function resolveFileOrContents (convertResult,
|
|
236
|
+
function resolveFileOrContents (convertResult, attributes, buildConfig, loggerName) {
|
|
245
237
|
let fileOrContents
|
|
246
238
|
if (convertResult?.status != null) {
|
|
247
|
-
|
|
239
|
+
if ('file' in convertResult) {
|
|
240
|
+
fileOrContents = convertResult.file
|
|
241
|
+
} else if ('contents' in convertResult) {
|
|
242
|
+
fileOrContents = convertResult.contents
|
|
243
|
+
} else if (buildConfig.outputOptionFlag === false) {
|
|
244
|
+
fileOrContents = convertResult.stdout
|
|
245
|
+
}
|
|
248
246
|
let logger, match
|
|
249
|
-
if (buildConfig.stderrSink === 'log' && convertResult.stderr
|
|
250
|
-
const docfile =
|
|
247
|
+
if (buildConfig.stderrSink === 'log' && convertResult.stderr?.length && (logger = this.getLogger(loggerName))) {
|
|
248
|
+
const docfile = attributes.docfile
|
|
251
249
|
const command = buildConfig.command
|
|
250
|
+
const file = { path: docfile }
|
|
252
251
|
const stderr = convertResult.stderr.toString().trimEnd()
|
|
252
|
+
const additionalLines = ['Additional stderr lines from command:']
|
|
253
253
|
stderr.split(NEWLINE_RX).forEach((line) => {
|
|
254
|
-
const ctx = { command, file
|
|
254
|
+
const ctx = { command, file }
|
|
255
255
|
if (line.charAt() === '{' && line.charAt(line.length - 1) === '}') {
|
|
256
256
|
const entry = JSON.parse(line)
|
|
257
257
|
if (entry.name) ctx.program = entry.name
|
|
258
258
|
if (entry.file?.line) ctx.line = entry.file.line
|
|
259
259
|
logger[entry.level](ctx, entry.msg)
|
|
260
|
-
} else if ((match = /^(
|
|
260
|
+
} else if ((match = /^([^:]+):(\d+): warning: (.+)/.exec(line))) {
|
|
261
261
|
const [, scriptPath, lineno, msg] = match
|
|
262
262
|
ctx.stack = [{ file: { path: scriptPath }, line: parseInt(lineno, 10) }]
|
|
263
263
|
logger.warn(ctx, msg)
|
|
264
|
+
} else if ((match = /^asciidoctor: ([A-Z]+): (?:[^:]+: line (\d+): )?(.+)/.exec(line))) {
|
|
265
|
+
// NOTE we don't care about the filename in the message since it can only be docfile
|
|
266
|
+
const [, level, lineno, msg] = match
|
|
267
|
+
if (lineno) ctx.line = parseInt(lineno, 10)
|
|
268
|
+
logger[level === 'WARNING' ? 'warn' : level.toLowerCase()](ctx, msg)
|
|
264
269
|
} else {
|
|
265
|
-
|
|
270
|
+
additionalLines.push(line)
|
|
266
271
|
}
|
|
267
272
|
})
|
|
273
|
+
if (additionalLines.length > 1) logger.info({ command, file }, additionalLines.join('\n'))
|
|
268
274
|
}
|
|
269
275
|
} else if (convertResult !== undefined) {
|
|
270
|
-
return convertResult
|
|
276
|
+
return Promise.resolve(convertResult)
|
|
271
277
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
278
|
+
if (fileOrContents !== undefined) return Promise.resolve(fileOrContents)
|
|
279
|
+
const outfile = attributes.outfile
|
|
280
|
+
return fsp.access(outfile).then(() => new LazyReadable(outfile), invariably.null)
|
|
275
281
|
}
|
|
276
282
|
|
|
277
283
|
function isBound (obj) {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fsp = require('node:fs/promises')
|
|
4
|
+
const ospath = require('node:path')
|
|
5
|
+
|
|
6
|
+
async function compileConversionAttributes (doc, targetExtname, assemblerConfig, relativeToOutput = false) {
|
|
7
|
+
const {
|
|
8
|
+
asciidoc: { attributes: docAttributes } = { attributes: {} },
|
|
9
|
+
extname: docfilesuffix,
|
|
10
|
+
path: reldocfile,
|
|
11
|
+
src: { family, relative },
|
|
12
|
+
} = doc
|
|
13
|
+
const { cwd = process.cwd(), dir = cwd, mkdirs } = assemblerConfig.build
|
|
14
|
+
const docname = family + '$' + relative.substring(0, relative.length - docfilesuffix.length)
|
|
15
|
+
const docfile = (doc.src.path = ospath.join(dir, reldocfile))
|
|
16
|
+
const outdir = ospath.dirname(docfile)
|
|
17
|
+
const outfile = docfile.substring(0, docfile.length - docfilesuffix.length) + targetExtname
|
|
18
|
+
const docdir = relativeToOutput ? dir : outdir
|
|
19
|
+
//const imagesoutdir = ospath.join(outdir, '..', '_images/generated')
|
|
20
|
+
const imagesoutdir = outdir
|
|
21
|
+
const attributes = Object.assign({ revdate: `${assemblerConfig.assembly.revdate}@` }, docAttributes, {
|
|
22
|
+
docdir,
|
|
23
|
+
docfile,
|
|
24
|
+
docfilesuffix,
|
|
25
|
+
docname,
|
|
26
|
+
imagesdir: '',
|
|
27
|
+
imagesoutdir,
|
|
28
|
+
outdir,
|
|
29
|
+
outfile,
|
|
30
|
+
outfilesuffix: targetExtname,
|
|
31
|
+
toArgs (attributeOptionFlag, outputOptionFlag) {
|
|
32
|
+
const args = []
|
|
33
|
+
let requireAsciidoctorLogAdapter
|
|
34
|
+
for (const [name, val] of Object.entries(this)) {
|
|
35
|
+
let optionValue
|
|
36
|
+
if (name === 'asciidoctor-log-integration') {
|
|
37
|
+
if ((val ?? false) !== false) requireAsciidoctorLogAdapter = val
|
|
38
|
+
continue
|
|
39
|
+
} else if ((val ?? false) === false) {
|
|
40
|
+
optionValue = `${name}!${val === false ? '=@' : ''}`
|
|
41
|
+
} else {
|
|
42
|
+
optionValue = val === '' ? name : name + '=' + val
|
|
43
|
+
}
|
|
44
|
+
args.push(attributeOptionFlag, optionValue)
|
|
45
|
+
}
|
|
46
|
+
if (requireAsciidoctorLogAdapter) args.push('-r', requireAsciidoctorLogAdapter)
|
|
47
|
+
if (outputOptionFlag) args.push(outputOptionFlag, this.outfile)
|
|
48
|
+
return args
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
Object.defineProperty(attributes, 'outfilesuffix', {
|
|
52
|
+
get () {
|
|
53
|
+
return ospath.extname(this.outfile)
|
|
54
|
+
},
|
|
55
|
+
set (value) {
|
|
56
|
+
const outfile = this.outfile
|
|
57
|
+
const extname = ospath.extname(outfile)
|
|
58
|
+
if (extname && outfile.endsWith(extname)) this.outfile = outfile.substring(0, outfile.length - extname.length)
|
|
59
|
+
this.outfile += value
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
Object.defineProperty(attributes, 'toArgs', { enumerable: false })
|
|
63
|
+
if (mkdirs) await fsp.mkdir(outdir, { recursive: true, force: true })
|
|
64
|
+
if (attributes['asciidoctor-log-integration'] != null) {
|
|
65
|
+
const scriptSourcePath = require.resolve('#asciidoctor-log-adapter')
|
|
66
|
+
let scriptTargetPath = scriptSourcePath
|
|
67
|
+
if (attributes['asciidoctor-log-integration'] === 'copy-to-build-dir') {
|
|
68
|
+
scriptTargetPath = ospath.join(dir, 'asciidoctor-log-adapter.rb')
|
|
69
|
+
await fsp.cp(scriptSourcePath, scriptTargetPath, { force: true, recursive: true })
|
|
70
|
+
}
|
|
71
|
+
attributes['asciidoctor-log-integration'] = scriptTargetPath
|
|
72
|
+
}
|
|
73
|
+
return attributes
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
module.exports = compileConversionAttributes
|
package/lib/configure.js
CHANGED
|
@@ -1,34 +1,61 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { ASSEMBLY_KEYS } = require('./constants')
|
|
4
|
+
|
|
3
5
|
function configure (context, ...args) {
|
|
4
6
|
internalConfigure.apply(context, args)
|
|
5
7
|
}
|
|
6
8
|
|
|
7
9
|
function internalConfigure (converter, config = {}, providers = {}) {
|
|
8
|
-
this.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
if (!this.listeners('beforeProcess').includes(enableKeepSource)) {
|
|
11
|
+
this.once('componentsRegistered', ({ contentCatalog, assemblerProfiles }) => {
|
|
12
|
+
contentCatalog.publishableFamilies.add('export')
|
|
13
|
+
if (!assemblerProfiles) this.updateVariables({ assemblerProfiles: getAssemblerProfiles(contentCatalog) })
|
|
14
|
+
})
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
})
|
|
16
|
+
this.once('beforeProcess', enableKeepSource)
|
|
17
|
+
}
|
|
16
18
|
|
|
17
19
|
this.once('navigationBuilt', async ({ playbook, contentCatalog }) => {
|
|
18
|
-
const { assembleContent = require('./assemble-content'), ...
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
const { assembleContent = require('./assemble-content'), ...assembleContentProviders } = providers
|
|
21
|
+
if (config.configSource?.constructor === Object) {
|
|
22
|
+
assembleContentProviders.configSource = config.configSource
|
|
23
|
+
await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentProviders)
|
|
24
|
+
} else {
|
|
25
|
+
const singleConfig = !('configFiles' in config)
|
|
26
|
+
const configFiles = singleConfig ? config.configFile : config.configFiles
|
|
27
|
+
for (const configSource of Array.isArray(configFiles) ? configFiles : [configFiles]) {
|
|
28
|
+
const assembleContentProvidersWithConfigSource = Object.assign({}, assembleContentProviders, { configSource })
|
|
29
|
+
await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentProvidersWithConfigSource)
|
|
30
|
+
if (singleConfig) break
|
|
31
|
+
}
|
|
32
|
+
}
|
|
21
33
|
})
|
|
22
34
|
}
|
|
23
35
|
|
|
36
|
+
function enableKeepSource ({ siteAsciiDocConfig }) {
|
|
37
|
+
if (siteAsciiDocConfig.keepSource instanceof Boolean) return
|
|
38
|
+
siteAsciiDocConfig.keepSource = Object.assign(new Boolean(true), { oldValue: siteAsciiDocConfig.keepSource })
|
|
39
|
+
this.once('navigationBuilt', restoreKeepSource)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function restoreKeepSource ({ siteAsciiDocConfig, contentCatalog }) {
|
|
43
|
+
if (!(siteAsciiDocConfig.keepSource instanceof Boolean)) return
|
|
44
|
+
if ((siteAsciiDocConfig.keepSource = siteAsciiDocConfig.keepSource.oldValue)) return
|
|
45
|
+
contentCatalog.getPages((page) => delete page.src.contents)
|
|
46
|
+
}
|
|
47
|
+
|
|
24
48
|
function getAssemblerProfiles (contentCatalog, assemblerProfiles = new Map()) {
|
|
25
49
|
contentCatalog.getComponents().forEach((component) => {
|
|
26
50
|
component.versions.forEach((componentVersion) => {
|
|
27
|
-
const source =
|
|
51
|
+
const source =
|
|
52
|
+
componentVersion.nav?.origin ??
|
|
53
|
+
[...(componentVersion.origins ?? [])].find((it) => it.descriptor?.ext?.assembler)
|
|
28
54
|
const assemblerConfig = getAssemblerConfigFromDescriptor(source?.descriptor)
|
|
29
55
|
if (!assemblerConfig) return
|
|
30
|
-
const
|
|
31
|
-
const
|
|
56
|
+
const { name: componentName, version, files } = componentVersion
|
|
57
|
+
const componentVersionKey = `${version}@${componentName}`
|
|
58
|
+
const filesByPath = files.reduce((accum, it) => accum.set(it.path, it), new Map())
|
|
32
59
|
const componentVersionProfiles = assemblerConfig.reduce((accum, entry) => {
|
|
33
60
|
const data = {}
|
|
34
61
|
const profile = entry.profile ?? undefined
|
|
@@ -37,14 +64,15 @@ function getAssemblerProfiles (contentCatalog, assemblerProfiles = new Map()) {
|
|
|
37
64
|
Object.entries(entry).forEach(([key, val]) => {
|
|
38
65
|
if (key === 'profile' || key === 'nav') return
|
|
39
66
|
const camelKey = key.toLowerCase().replace(/[_-]([a-z0-9])/g, (_, l, idx) => (idx ? l.toUpperCase() : l))
|
|
40
|
-
data[camelKey] = val
|
|
67
|
+
if (ASSEMBLY_KEYS.includes(camelKey)) data[camelKey] = val
|
|
41
68
|
})
|
|
42
69
|
if (nav) {
|
|
43
70
|
data.navFiles = (nav.length ? [...new Set(nav)] : nav).reduce((navFiles, path_) => {
|
|
44
71
|
const navFile = filesByPath.get(path_)
|
|
45
72
|
if (navFile) {
|
|
46
|
-
navFiles.push(initNavFile(navFile,
|
|
73
|
+
navFiles.push(initNavFile(navFile, componentName, version, navFiles.length))
|
|
47
74
|
} else {
|
|
75
|
+
const componentVersionRef = `${version === 'master' ? '' : version}@${componentName}`
|
|
48
76
|
;(data.messages ??= []).push([
|
|
49
77
|
'warn',
|
|
50
78
|
{ source },
|
|
@@ -58,7 +86,7 @@ function getAssemblerProfiles (contentCatalog, assemblerProfiles = new Map()) {
|
|
|
58
86
|
}
|
|
59
87
|
return accum.set(profile, data)
|
|
60
88
|
}, new Map())
|
|
61
|
-
assemblerProfiles.set(
|
|
89
|
+
assemblerProfiles.set(componentVersionKey, componentVersionProfiles)
|
|
62
90
|
})
|
|
63
91
|
})
|
|
64
92
|
return assemblerProfiles
|
package/lib/constants.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports = Object.freeze({
|
|
4
|
+
ASSEMBLY_KEYS: [
|
|
5
|
+
'attributes',
|
|
6
|
+
'doctype',
|
|
7
|
+
'dropExplicitXrefText',
|
|
8
|
+
'linkReferenceStyle',
|
|
9
|
+
'insertStartPage',
|
|
10
|
+
'profile',
|
|
11
|
+
'rootLevel',
|
|
12
|
+
'rootPageStyle',
|
|
13
|
+
'sectionMergeStrategy',
|
|
14
|
+
'xmlIds',
|
|
15
|
+
],
|
|
16
|
+
LEGACY_ASSEMBLY_KEYS: [
|
|
17
|
+
'dropExplicitXrefText',
|
|
18
|
+
'linkReferenceStyle',
|
|
19
|
+
'insertStartPage',
|
|
20
|
+
'rootLevel',
|
|
21
|
+
'sectionMergeStrategy',
|
|
22
|
+
],
|
|
23
|
+
CAMEL_CASE_STOP_KEYS: ['asciidoc.attributes', 'assembly.attributes'],
|
|
24
|
+
})
|