@antora/assembler 1.0.0-alpha.4 → 1.0.0-alpha.6

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.
@@ -121,6 +121,7 @@ const IncludeDirectiveTracker = (() => {
121
121
  this.$$reducer.includePushed = true
122
122
  const directiveLineno = this.lineno - 1 // we're below the include line, which is 1-based
123
123
  const prevIncDepth = this.include_stack.length
124
+ let offset = lineno > 1 ? lineno - 1 : 0
124
125
  const result = Opal.send(this, Opal.find_super_dispatcher(this, 'push_include', pushInclude), [
125
126
  data,
126
127
  file,
@@ -128,12 +129,12 @@ const IncludeDirectiveTracker = (() => {
128
129
  lineno,
129
130
  attrs,
130
131
  ])
131
- pushIncludeReplacement.call(
132
- this,
133
- directiveLineno,
134
- this.include_stack.length > prevIncDepth ? this.$lines() : [],
135
- lineno > 1 ? lineno - 1 : 0
136
- )
132
+ let incLines = []
133
+ if (this.include_stack.length > prevIncDepth) {
134
+ incLines = this.$lines()
135
+ if (attrs['$key?']('leveloffset') && incLines[0].startsWith(':leveloffset: ') && incLines[1] === '') offset -= 2
136
+ }
137
+ pushIncludeReplacement.call(this, directiveLineno, incLines, offset)
137
138
  return result
138
139
  })
139
140
 
@@ -174,8 +175,12 @@ function treeProcessor () {
174
175
  let targetLines, idx
175
176
  if (into != null) {
176
177
  targetLines = incReplacements[into].lines
177
- // adds assurance that we're replacing the correct line
178
- if (targetLines[(idx = lineno - 1)] !== line) return
178
+ // adds extra assurance that the program is replacing the correct line
179
+ if (targetLines[(idx = lineno - 1)] !== line) {
180
+ const msg = `include directive to reduce not found; expected: "${line}"; got: "${targetLines[idx]}"`
181
+ doc.getLogger().error(msg)
182
+ return
183
+ }
179
184
  }
180
185
  if ((drop || []).length) {
181
186
  drop
@@ -1,13 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const camelCaseKeys = require('camelcase-keys')
4
3
  const expandPath = require('@antora/expand-path-helper')
5
4
  const { promises: fsp } = require('fs')
6
5
  const os = require('os')
7
6
  const yaml = require('js-yaml')
8
7
 
9
- const CAMEL_CASE_KEYS_OPTS = { deep: true, stopPaths: ['asciidoc'] }
10
-
11
8
  function loadConfig (playbook, configSource = './antora-assembler.yml') {
12
9
  return (
13
10
  configSource.constructor === Object
@@ -19,21 +16,21 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
19
16
  () => false
20
17
  )
21
18
  .then((exists) =>
22
- exists
23
- ? fsp.readFile(configSource).then((data) => camelCaseKeys(yaml.load(data), CAMEL_CASE_KEYS_OPTS))
24
- : {}
19
+ exists ? fsp.readFile(configSource).then((data) => camelCaseKeys(yaml.load(data), ['asciidoc'])) : {}
25
20
  )
26
21
  ).then((config) => {
27
22
  if (config.enabled === false) return undefined
28
23
  let asciidocAttrs
29
24
  if (!config.asciidoc) {
30
- config.asciidoc = { attributes: (asciidocAttrs = { doctype: 'book' }) }
25
+ config.asciidoc = { attributes: (asciidocAttrs = {}) }
31
26
  } else if (!(asciidocAttrs = config.asciidoc.attributes)) {
32
- config.asciidoc.attributes = asciidocAttrs = { doctype: 'book' }
27
+ config.asciidoc.attributes = asciidocAttrs = {}
33
28
  }
34
- Object.assign(asciidocAttrs, { revdate: new Date().toISOString().split('T')[0], 'page-partial': null })
29
+ if (!('doctype' in asciidocAttrs)) asciidocAttrs.doctype = 'book'
30
+ asciidocAttrs.revdate = new Date().toISOString().split('T')[0]
31
+ asciidocAttrs['page-partial'] = null
35
32
  if (config.componentVersions == null) {
36
- config.componentVersions = ['**']
33
+ config.componentVersions = ['*']
37
34
  } else if (typeof config.componentVersions === 'string') {
38
35
  config.componentVersions = config.componentVersions.split(', ')
39
36
  }
@@ -59,4 +56,16 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
59
56
  })
60
57
  }
61
58
 
59
+ function camelCaseKeys (o, stopPaths = [], p) {
60
+ if (Array.isArray(o)) return o.map((it) => camelCaseKeys(it, stopPaths, p))
61
+ if (o == null || o.constructor !== Object) return o
62
+ const pathPrefix = p ? p + '.' : ''
63
+ const accum = {}
64
+ for (const [k, v] of Object.entries(o)) {
65
+ const camelKey = k.charAt() + k.substr(1).replace(/_([a-z])/g, (_, l) => l.toUpperCase())
66
+ accum[camelKey] = ~stopPaths.indexOf(pathPrefix + camelKey) ? v : camelCaseKeys(v, stopPaths, pathPrefix + camelKey)
67
+ }
68
+ return accum
69
+ }
70
+
62
71
  module.exports = loadConfig
@@ -8,6 +8,7 @@ function produceAggregateDocument (
8
8
  contentCatalog,
9
9
  componentVersion,
10
10
  outline,
11
+ doctype,
11
12
  pages,
12
13
  asciidocConfig,
13
14
  mutableAttributes,
@@ -16,7 +17,7 @@ function produceAggregateDocument (
16
17
  const pagesInOutline = selectPagesInOutline(outline, pages)
17
18
  const navtitle = outline.content
18
19
  const stem = generateStem(componentVersion, navtitle)
19
- const header = buildAsciiDocHeader(componentVersion, navtitle)
20
+ const header = buildAsciiDocHeader(componentVersion, navtitle, doctype)
20
21
  const body = aggregateAsciiDoc(
21
22
  loadAsciiDoc,
22
23
  contentCatalog,
@@ -44,13 +45,13 @@ function produceAggregateDocument (
44
45
  })
45
46
  }
46
47
 
47
- function buildAsciiDocHeader (componentVersion, navtitle) {
48
+ function buildAsciiDocHeader (componentVersion, navtitle, doctype = 'book') {
48
49
  const doctitle = navtitle === componentVersion.title ? navtitle : `${componentVersion.title}: ${navtitle}`
49
- const version = componentVersion.version && componentVersion.version !== 'master' ? componentVersion.version : ''
50
+ const version = componentVersion.version === 'master' ? '' : componentVersion.version
50
51
  return [
51
52
  `= ${doctitle}`,
52
53
  ...(version ? [`v${version}`] : []),
53
- ':doctype: book', // for debugging only; set via CLI
54
+ ...(doctype === 'article' ? [] : [`:doctype: ${doctype}`]),
54
55
  // Q: should we pass these via the CLI so they cannot be modified?
55
56
  `:page-component-name: ${componentVersion.name}`,
56
57
  `:page-component-version:${version ? ' ' + version : ''}`,
@@ -166,14 +167,18 @@ function aggregateAsciiDoc (
166
167
  buffer.push(`${'='.repeat(hlevel)} ${overviewTitle}`)
167
168
  if (toggleSectids) buffer.push(':sectids:')
168
169
  }
169
- const siteUrl = doc.getAttribute('site-url')
170
+ const siteUrl = ((val) => {
171
+ if (!val || val === '/') return ''
172
+ return val.charAt(val.length - 1) === '/' ? val.substr(0, val.length - 1) : val
173
+ })(doc.getAttribute('site-url'))
170
174
  const lines = doc.getSourceLines()
171
175
  const ignoreLines = []
172
176
  // TODO: think more about when multipart is allowed; perhaps configurable
173
177
  if (doc.hasSections()) fixSectionLevels(doc.getSections(), level === 0)
174
- const allBlocks = doc.findBy(
175
- { traverse_documents: true },
176
- (it) => it.getContext() !== 'document' && !(it.getContext() === 'table_cell' && it.getStyle() === 'asciidoc')
178
+ const allBlocks = doc.findBy({ traverse_documents: true }, (it) =>
179
+ it.getContext() === 'document'
180
+ ? it.getDocument().isNested()
181
+ : !(it.getContext() === 'table_cell' && it.getStyle() === 'asciidoc')
177
182
  )
178
183
  allBlocks.forEach((block) => {
179
184
  const contentModel = block.content_model
@@ -204,6 +209,11 @@ function aggregateAsciiDoc (
204
209
  for (let idx = 0, len = lines.length; idx < len; idx++) {
205
210
  if (~ignoreLines.indexOf(idx)) continue
206
211
  let line = lines[idx]
212
+ if (line.charAt() === ':' && /^:(?:leveloffset: .*|!leveloffset:|leveloffset!:)$/.test(line)) {
213
+ if (lines[idx - 1] === '') lines[idx - 1] = undefined
214
+ lines[idx] = undefined
215
+ continue
216
+ }
207
217
  if (~line.indexOf('<<')) {
208
218
  line = line.replace(/(?<![\\+])<<#?([\p{Alpha}0-9_/.:{][^>,]*?)(?:|, *([^>]+?))?>>/gu, (m, refid, text) => {
209
219
  // support natural xref
@@ -219,7 +229,7 @@ function aggregateAsciiDoc (
219
229
  }
220
230
  if (~line.indexOf('xref:')) {
221
231
  // Q: should we allow : as first character of target?
222
- line = line.replace(/xref:([\p{Alpha}0-9_/.{#].*?)\[(|.*?[^\\])\]/gu, (m, target, text) => {
232
+ line = line.replace(/(?<![\\+])xref:([\p{Alpha}0-9_/.{#].*?)\[(|.*?[^\\])\]/gu, (m, target, text) => {
223
233
  let pagePart, fragment, targetPage
224
234
  const hashIdx = target.indexOf('#')
225
235
  if (~hashIdx) {
@@ -298,22 +308,20 @@ function aggregateAsciiDoc (
298
308
  // NOTE: lineno is not defined for preamble
299
309
  if (typeof lineno !== 'number') return
300
310
  const context = block.getContext()
301
- const idx = lineno - 1
302
- if (context === 'section') {
311
+ let idx = lineno - 1
312
+ if (context === 'section' && !block.getDocument().isNested()) {
303
313
  if (block.getSectionName() === 'header') {
304
- lines.splice(idx, 1)
314
+ lines[idx] = undefined
305
315
  return
306
316
  }
307
317
  let blockStyle = sectionMergeStrategy === 'discrete' ? 'discrete' : undefined
308
- // FIXME: quick fix; needs more thorough review
309
- const leveloffset = Number(doc.getAttribute('leveloffset') || 0)
310
- lines[idx] = lines[idx].replace(/^=+( .+)/, (_, rest) => {
311
- let targetMarkerLength = block.level + (1 - leveloffset) + level + (enclosed ? 1 : 0)
318
+ lines[idx] = lines[idx].replace(/^=+ (.+)/, (_, rest) => {
319
+ let targetMarkerLength = block.level + 1 + level + (enclosed ? 1 : 0)
312
320
  if (targetMarkerLength > 6) {
313
321
  targetMarkerLength = 6
314
322
  blockStyle = 'discrete'
315
323
  }
316
- return '='.repeat(targetMarkerLength) + rest
324
+ return '='.repeat(targetMarkerLength) + ' ' + rest
317
325
  })
318
326
  // NOTE: ID will be undefined if sectids are turned off
319
327
  if (block.getId()) rewriteStyleAttribute(block, lines, idx, idprefix, blockStyle)
@@ -323,7 +331,7 @@ function aggregateAsciiDoc (
323
331
  let prefix = ''
324
332
  // Q: can we use startsWith('image::') in certain cases?
325
333
  let imageMacroOffset = (
326
- lastImageMacroAt && lastImageMacroAt[0] === idx ? line.substr(0, lastImageMacroAt[1]) : line
334
+ lastImageMacroAt?.[0] === idx ? line.substr(0, lastImageMacroAt[1]) : line
327
335
  ).lastIndexOf('image::')
328
336
  if (imageMacroOffset > 0) {
329
337
  if (
@@ -347,11 +355,14 @@ function aggregateAsciiDoc (
347
355
  }
348
356
  lastImageMacroAt = [idx, imageMacroOffset]
349
357
  }
358
+ } else if (context === 'document' && block.hasHeader()) {
359
+ // nested document
360
+ idx = (block.getHeader().getLineNumber() || idx + 1) - 1
350
361
  }
351
362
  if (block.getId()) rewriteStyleAttribute(block, lines, idx, idprefix)
352
363
  }
353
364
  })
354
- buffer.push(...lines)
365
+ buffer.push(...lines.filter((it) => it !== undefined))
355
366
  const attributeEntries = Object.entries(doc.attributes_defined_in_header || {})
356
367
  if (attributeEntries.length) {
357
368
  const resolvedAttributeEntries = attributeEntries.reduce(
@@ -364,7 +375,7 @@ function aggregateAsciiDoc (
364
375
  } else if (val !== initialVal) {
365
376
  accum.push(`:${name}:${initialVal ? ' ' + initialVal : ''}`)
366
377
  }
367
- } else if (val != null && !doc.isAttributeLocked(name)) {
378
+ } else if (val != null && !(doc.isAttributeLocked(name) || name === 'doctype' || name === 'leveloffset')) {
368
379
  accum.push(`:!${name}:`)
369
380
  }
370
381
  return accum
@@ -431,7 +442,8 @@ function aggregateAsciiDoc (
431
442
 
432
443
  function generateStem (componentVersion, title) {
433
444
  const { name, version } = componentVersion
434
- const segments = [name]
445
+ const segments = []
446
+ if (name !== 'ROOT') segments.push(name)
435
447
  if (version && version !== 'master') segments.push(version)
436
448
  segments.push(
437
449
  title
@@ -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,13 +32,15 @@ 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,
37
40
  contentCatalog,
38
41
  componentVersion,
39
42
  outline,
43
+ doctype,
40
44
  contentCatalog.getPages((page) => page.out),
41
45
  mergedAsciiDocConfig,
42
46
  mutableAttributes,
@@ -44,6 +48,11 @@ function produceAggregateDocuments (loadAsciiDoc, contentCatalog, assemblerConfi
44
48
  )
45
49
  )
46
50
  )
51
+ mergedAsciiDocConfig.attributes.doctype = doctype
52
+ sourceHighlighter
53
+ ? (mergedAsciiDocConfig.attributes['source-highlighter'] = sourceHighlighter)
54
+ : delete mergedAsciiDocConfig.attributes['source-highlighter']
55
+ return accum
47
56
  },
48
57
  []
49
58
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antora/assembler",
3
- "version": "1.0.0-alpha.4",
3
+ "version": "1.0.0-alpha.6",
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,12 +25,12 @@
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": {
31
32
  "@antora/expand-path-helper": "~2.0",
32
33
  "braces": "~3.0",
33
- "camelcase-keys": "~7.0",
34
34
  "picomatch": "~2.3",
35
35
  "vinyl": "~2.2",
36
36
  "js-yaml": "~4.1"