@antora/assembler 1.0.0-alpha.2 → 1.0.0-alpha.5

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.
@@ -27,13 +27,15 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
27
27
  if (config.enabled === false) return undefined
28
28
  let asciidocAttrs
29
29
  if (!config.asciidoc) {
30
- config.asciidoc = { attributes: (asciidocAttrs = { doctype: 'book' }) }
30
+ config.asciidoc = { attributes: (asciidocAttrs = {}) }
31
31
  } else if (!(asciidocAttrs = config.asciidoc.attributes)) {
32
- config.asciidoc.attributes = asciidocAttrs = { doctype: 'book' }
32
+ config.asciidoc.attributes = asciidocAttrs = {}
33
33
  }
34
- Object.assign(asciidocAttrs, { revdate: new Date().toISOString().split('T')[0], 'page-partial': null })
34
+ if (!('doctype' in asciidocAttrs)) asciidocAttrs.doctype = 'book'
35
+ asciidocAttrs.revdate = new Date().toISOString().split('T')[0]
36
+ asciidocAttrs['page-partial'] = null
35
37
  if (config.componentVersions == null) {
36
- config.componentVersions = ['**']
38
+ config.componentVersions = ['*']
37
39
  } else if (typeof config.componentVersions === 'string') {
38
40
  config.componentVersions = config.componentVersions.split(', ')
39
41
  }
@@ -105,7 +105,7 @@ function aggregateAsciiDoc (
105
105
  page = new page.constructor(Object.assign({}, page, { contents, mediaType: 'text/asciidoc' }))
106
106
  const { module: module_, relative, origin } = page.src
107
107
  const doc = loadAsciiDoc(page, contentCatalog, asciidocConfig)
108
- const ids = doc.getCatalog().ids
108
+ const refs = doc.getCatalog().refs
109
109
  // NOTE: in Antora, docname is relative src path from module without file extension
110
110
  const docname = doc.getAttribute('docname')
111
111
  //const idprefix = `${module_ === 'ROOT' ? '' : module_ + ':'}${docname}//`
@@ -166,27 +166,19 @@ function aggregateAsciiDoc (
166
166
  buffer.push(`${'='.repeat(hlevel)} ${overviewTitle}`)
167
167
  if (toggleSectids) buffer.push(':sectids:')
168
168
  }
169
- const siteUrl = doc.getAttribute('site-url')
169
+ const siteUrl = ((val) => {
170
+ if (!val || val === '/') return ''
171
+ return val.charAt(val.length - 1) === '/' ? val.substr(0, val.length - 1) : val
172
+ })(doc.getAttribute('site-url'))
170
173
  const lines = doc.getSourceLines()
171
174
  const ignoreLines = []
172
175
  // TODO: think more about when multipart is allowed; perhaps configurable
173
176
  if (doc.hasSections()) fixSectionLevels(doc.getSections(), level === 0)
174
- // TODO: update / simplify findBy calls when upgrading to Asciidoctor 2
175
- const allBlocks = doc
176
- .findBy((it) => it.getContext() !== 'document')
177
- .reduce((accum, block) => {
178
- accum.push(block)
179
- if (block.getContext() === 'table') {
180
- const rows = block.rows
181
- ;[...rows.body, ...rows.foot].forEach((row) => {
182
- row.forEach((cell) => {
183
- if (cell.style !== 'asciidoc') return
184
- accum.push(...cell.inner_document.findBy((it) => it.getContext() !== 'document'))
185
- })
186
- })
187
- }
188
- return accum
189
- }, [])
177
+ const allBlocks = doc.findBy({ traverse_documents: true }, (it) =>
178
+ it.getContext() === 'document'
179
+ ? it.getDocument().isNested()
180
+ : !(it.getContext() === 'table_cell' && it.getStyle() === 'asciidoc')
181
+ )
190
182
  allBlocks.forEach((block) => {
191
183
  const contentModel = block.content_model
192
184
  if (
@@ -218,11 +210,9 @@ function aggregateAsciiDoc (
218
210
  let line = lines[idx]
219
211
  if (~line.indexOf('<<')) {
220
212
  line = line.replace(/(?<![\\+])<<#?([\p{Alpha}0-9_/.:{][^>,]*?)(?:|, *([^>]+?))?>>/gu, (m, refid, text) => {
221
- // support natural xref; note this logic will change when upgrading to Asciidoctor 2
222
- if (!ids['$key?'](refid) && (~refid.indexOf(' ') || refid.toLowerCase() !== refid)) {
223
- if (!(refid = doc.getCatalog().ids.$key(refid).$to_s())) {
224
- return m
225
- }
213
+ // support natural xref
214
+ if (!refs['$key?'](refid) && (~refid.indexOf(' ') || refid.toLowerCase() !== refid)) {
215
+ if ((refid = doc.$resolve_id(refid))['$nil?']()) return m
226
216
  }
227
217
  return `<<${idprefix}${refid}${text ? ',' + text : ''}>>`
228
218
  })
@@ -233,21 +223,26 @@ function aggregateAsciiDoc (
233
223
  }
234
224
  if (~line.indexOf('xref:')) {
235
225
  // Q: should we allow : as first character of target?
236
- line = line.replace(/xref:([\p{Alpha}#/.{].*?)\[(|.*?[^\\])\]/gu, (m, target, text) => {
226
+ line = line.replace(/xref:([\p{Alpha}0-9_/.{#].*?)\[(|.*?[^\\])\]/gu, (m, target, text) => {
237
227
  let pagePart, fragment, targetPage
238
228
  const hashIdx = target.indexOf('#')
239
229
  if (~hashIdx) {
240
230
  pagePart = target.substr(0, hashIdx)
241
231
  fragment = target.substr(hashIdx + 1)
242
232
  // TODO: for now, assume .adoc; in the future, consider other file extensions
243
- if (!(pagePart && pagePart.endsWith('.adoc'))) pagePart += '.adoc'
233
+ if (pagePart && !pagePart.endsWith('.adoc')) pagePart += '.adoc'
244
234
  } else if (target.endsWith('.adoc')) {
245
235
  pagePart = target
246
236
  fragment = ''
247
237
  } else {
248
238
  fragment = target
249
239
  }
250
- if (!pagePart) return `<<${idprefix}${fragment}${text ? ',' + text.replace(/\\]/g, ']') : ''}>>`
240
+ if (!pagePart) {
241
+ // Q: should we validate the internal ID here?
242
+ return text && ~text.indexOf('=')
243
+ ? `xref:${idprefix}${fragment}[${text}]`
244
+ : `<<${idprefix}${fragment}${text ? ',' + text.replace(/\\]/g, ']') : ''}>>`
245
+ }
251
246
  if (~pagePart.indexOf('@') || /:.*:/.test(pagePart)) {
252
247
  // TODO: handle unresolved page better
253
248
  return siteUrl && (targetPage = contentCatalog.resolvePage(pagePart, page.src))
@@ -301,13 +296,14 @@ function aggregateAsciiDoc (
301
296
  // NOTE: need to do this last since it modifies the line numbers
302
297
  // we could remap the line numbers to make them resilient
303
298
  // or we could mark which lines to remove and filter them after
299
+ let lastImageMacroAt
304
300
  ;[...allBlocks].reverse().forEach((block) => {
305
301
  const lineno = block.getLineNumber()
306
302
  // NOTE: lineno is not defined for preamble
307
303
  if (typeof lineno !== 'number') return
308
304
  const context = block.getContext()
309
- const idx = lineno - 1
310
- if (context === 'section') {
305
+ let idx = lineno - 1
306
+ if (context === 'section' && !block.getDocument().isNested()) {
311
307
  if (block.getSectionName() === 'header') {
312
308
  lines.splice(idx, 1)
313
309
  return
@@ -327,34 +323,37 @@ function aggregateAsciiDoc (
327
323
  if (block.getId()) rewriteStyleAttribute(block, lines, idx, idprefix, blockStyle)
328
324
  } else {
329
325
  if (context === 'image') {
330
- const atImageMacro = (lines[idx] || '').startsWith('image::')
331
- // NOTE: the following logic is needed only if parser is messing up line number of image block
332
- //let atImageMacro
333
- //// NOTE: account for line number tracking error in parser when image has block anchor
334
- //if (block.getId()) {
335
- // const originalIdx = idx
336
- // let line
337
- // while ((line = lines[idx]) != null && !(atImageMacro = line.startsWith('image::'))) idx++
338
- // if (!atImageMacro) {
339
- // idx = originalIdx
340
- // if ((lines[idx - 1] || '').startsWith('image::')) {
341
- // atImageMacro = true
342
- // idx--
343
- // }
344
- // }
345
- //} else if ((lines[idx] || '').startsWith('image::')) {
346
- // atImageMacro = true
347
- //}
348
- const target = block.getAttribute('target')
349
- if (atImageMacro && isResourceSpec(target)) {
350
- const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
351
- // FIXME: handle (or report) case when image is not resolved
352
- if (image) {
353
- image.out.assembled = true
354
- const line = lines[idx]
355
- lines[idx] = `image::${image.pub.url.substr(1)}${line.substr(line.indexOf('['))}`
326
+ let line = lines[idx] || ''
327
+ let prefix = ''
328
+ // Q: can we use startsWith('image::') in certain cases?
329
+ let imageMacroOffset = (
330
+ lastImageMacroAt && lastImageMacroAt[0] === idx ? line.substr(0, lastImageMacroAt[1]) : line
331
+ ).lastIndexOf('image::')
332
+ if (imageMacroOffset > 0) {
333
+ if (
334
+ block.getDocument().isNested() &&
335
+ (prefix = line.substr(0, imageMacroOffset)).trimRight().endsWith('|')
336
+ ) {
337
+ line = line.substr(prefix.length)
338
+ } else {
339
+ imageMacroOffset = -1
340
+ }
341
+ }
342
+ if (imageMacroOffset >= 0) {
343
+ const target = block.getAttribute('target')
344
+ if (isResourceSpec(target)) {
345
+ const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
346
+ // FIXME: handle (or report) case when image is not resolved
347
+ if (image) {
348
+ lines[idx] = `${prefix}image::${image.pub.url.substr(1)}${line.substr(line.indexOf('['))}`
349
+ image.out.assembled = true
350
+ }
356
351
  }
352
+ lastImageMacroAt = [idx, imageMacroOffset]
357
353
  }
354
+ } else if (context === 'document' && block.hasHeader()) {
355
+ // nested document
356
+ idx = (block.getHeader().getLineNumber() || idx + 1) - 1
358
357
  }
359
358
  if (block.getId()) rewriteStyleAttribute(block, lines, idx, idprefix)
360
359
  }
@@ -372,7 +371,7 @@ function aggregateAsciiDoc (
372
371
  } else if (val !== initialVal) {
373
372
  accum.push(`:${name}:${initialVal ? ' ' + initialVal : ''}`)
374
373
  }
375
- } else if (val != null && !doc.isAttributeLocked(name)) {
374
+ } else if (val != null && !(doc.isAttributeLocked(name) || name === 'doctype')) {
376
375
  accum.push(`:!${name}:`)
377
376
  }
378
377
  return accum
@@ -7,7 +7,9 @@ const selectMutableAttributes = require('./select-mutable-attributes')
7
7
  function produceAggregateDocuments (loadAsciiDoc, contentCatalog, assemblerConfig) {
8
8
  const { insertStartPage, rootLevel, sectionMergeStrategy, asciidoc: assemblerAsciiDocConfig } = assemblerConfig
9
9
  const assemblerAsciiDocAttributes = Object.assign({}, assemblerAsciiDocConfig.attributes)
10
- delete assemblerAsciiDocAttributes['source-highlighter'] // only used at convert time
10
+ const { doctype, 'source-highlighter': sourceHighlighter } = assemblerAsciiDocAttributes
11
+ delete assemblerAsciiDocAttributes.doctype
12
+ delete assemblerAsciiDocAttributes['source-highlighter']
11
13
  return filterComponentVersions(contentCatalog.getComponents(), assemblerConfig.componentVersions).reduce(
12
14
  (accum, componentVersion) => {
13
15
  const { name: componentName, version, title, navigation } = componentVersion
@@ -30,7 +32,8 @@ function produceAggregateDocuments (loadAsciiDoc, contentCatalog, assemblerConfi
30
32
  const mutableAttributes = startPage
31
33
  ? selectMutableAttributes(loadAsciiDoc, contentCatalog, startPage, mergedAsciiDocConfig)
32
34
  : {}
33
- return accum.concat(
35
+ delete mutableAttributes.doctype
36
+ accum = accum.concat(
34
37
  prepareOutlines(navigation, rootEntry, rootLevel).map((outline) =>
35
38
  produceAggregateDocument(
36
39
  loadAsciiDoc,
@@ -44,6 +47,11 @@ function produceAggregateDocuments (loadAsciiDoc, contentCatalog, assemblerConfi
44
47
  )
45
48
  )
46
49
  )
50
+ mergedAsciiDocConfig.attributes.doctype = doctype
51
+ sourceHighlighter
52
+ ? (mergedAsciiDocConfig.attributes['source-highlighter'] = sourceHighlighter)
53
+ : delete mergedAsciiDocConfig.attributes['source-highlighter']
54
+ return accum
47
55
  },
48
56
  []
49
57
  )
@@ -6,9 +6,8 @@ const { PassThrough } = require('stream')
6
6
  class LazyReadable extends PassThrough {
7
7
  constructor (fn, options) {
8
8
  super(options)
9
- const _read = this._read
10
9
  this._read = function () {
11
- this._read = _read.bind(this)
10
+ delete this._read // restores original method
12
11
  fn.call(this, options).on('error', this.emit.bind(this, 'error')).pipe(this)
13
12
  return this._read.apply(this, arguments)
14
13
  }
@@ -43,11 +43,15 @@ async function runCommand (cmd, argv = [], opts = {}) {
43
43
  ps.on('error', (err) => reject(err.code === 'ENOENT' ? new Error(`Command not found: ${cmdv.join(' ')}`) : err))
44
44
  ps.stdout.on('data', (data) => (output ? process.stdout.write(data) : stdout.push(data)))
45
45
  ps.stderr.on('data', (data) => stderr.push(data))
46
- try {
47
- input instanceof Buffer ? ps.stdin.end(input) : ps.stdin.end()
48
- } catch (err) {
49
- reject(err)
50
- } finally {
46
+ if (input instanceof Buffer) {
47
+ try {
48
+ ps.stdin.on('error', () => undefined)
49
+ ps.stdin.end(input)
50
+ } catch (err) {
51
+ if (!ps.stdin.writableEnded) ps.stdin.end()
52
+ reject(err)
53
+ }
54
+ } else {
51
55
  ps.stdin.end()
52
56
  }
53
57
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antora/assembler",
3
- "version": "1.0.0-alpha.2",
3
+ "version": "1.0.0-alpha.5",
4
4
  "description": "An extension library for Antora that assembles content from multiple pages into a single AsciiDoc file to converted and publish.",
5
5
  "license": "MPL-2.0",
6
6
  "author": "OpenDevise Inc. (https://opendevise.com)",
@@ -25,6 +25,7 @@
25
25
  "./filter-component-versions": "./lib/filter-component-versions.js",
26
26
  "./load-config": "./lib/load-config.js",
27
27
  "./produce-aggregate-document": "./lib/produce-aggregate-document.js",
28
+ "./produce-aggregate-documents": "./lib/produce-aggregate-documents.js",
28
29
  "./select-mutable-attributes": "./lib/select-mutable-attributes.js"
29
30
  },
30
31
  "dependencies": {