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

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.
@@ -31,7 +31,7 @@ async function assembleContent (playbook, contentCatalog, converter, { siteCatal
31
31
  })
32
32
  }
33
33
 
34
- // TODO: if no workspace dir is defined; we shouldn't continue
34
+ // TODO: if no workspace dir is defined, we shouldn't continue
35
35
  function prepareWorkspace (publishFiles, aggregateDocuments, contentCatalog, buildConfig) {
36
36
  const { dir, clean, keepAggregateSource } = buildConfig
37
37
  const files = contentCatalog.findBy({ family: 'image' }).filter(({ out }) => out?.assembled)
@@ -1,8 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const expandPath = require('@antora/expand-path-helper')
4
- const { promises: fsp } = require('fs')
5
- const os = require('os')
4
+ const fsp = require('node:fs/promises')
5
+ const os = require('node:os')
6
6
  const yaml = require('js-yaml')
7
7
 
8
8
  function loadConfig (playbook, configSource = './antora-assembler.yml') {
@@ -27,7 +27,7 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
27
27
  config.asciidoc.attributes = asciidocAttrs = {}
28
28
  }
29
29
  if (!('doctype' in asciidocAttrs)) asciidocAttrs.doctype = 'book'
30
- asciidocAttrs.revdate = new Date().toISOString().split('T')[0]
30
+ if (!('revdate' in asciidocAttrs)) asciidocAttrs.revdate = getLocalDate()
31
31
  asciidocAttrs['page-partial'] = null
32
32
  if (config.componentVersions == null) {
33
33
  config.componentVersions = ['*']
@@ -68,4 +68,8 @@ function camelCaseKeys (o, stopPaths = [], p) {
68
68
  return accum
69
69
  }
70
70
 
71
+ function getLocalDate (now = new Date()) {
72
+ return new Date(now - now.getTimezoneOffset() * 60000).toISOString().split('T')[0]
73
+ }
74
+
71
75
  module.exports = loadConfig
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const File = require('vinyl')
4
- const { posix: path } = require('path')
4
+ const path = require('node:path/posix')
5
5
 
6
6
  function produceAggregateDocument (
7
7
  loadAsciiDoc,
@@ -14,7 +14,10 @@ function produceAggregateDocument (
14
14
  mutableAttributes,
15
15
  sectionMergeStrategy = 'discrete'
16
16
  ) {
17
- const pagesInOutline = selectPagesInOutline(outline, pages)
17
+ const pagesInOutline = selectPagesInOutline(
18
+ outline,
19
+ pages.filter((it) => it.out)
20
+ )
18
21
  const navtitle = outline.content
19
22
  const stem = generateStem(componentVersion, navtitle)
20
23
  const header = buildAsciiDocHeader(componentVersion, navtitle, doctype)
@@ -50,8 +53,9 @@ function buildAsciiDocHeader (componentVersion, navtitle, doctype = 'book') {
50
53
  const version = componentVersion.version === 'master' ? '' : componentVersion.version
51
54
  return [
52
55
  `= ${doctitle}`,
53
- ...(version ? [`v${version}`] : []),
56
+ ...(version ? [`:revnumber: ${version}`] : []),
54
57
  ...(doctype === 'article' ? [] : [`:doctype: ${doctype}`]),
58
+ ':underscore: _',
55
59
  // Q: should we pass these via the CLI so they cannot be modified?
56
60
  `:page-component-name: ${componentVersion.name}`,
57
61
  `:page-component-version:${version ? ' ' + version : ''}`,
@@ -109,9 +113,8 @@ function aggregateAsciiDoc (
109
113
  const refs = doc.getCatalog().refs
110
114
  // NOTE: in Antora, docname is relative src path from module without file extension
111
115
  const docname = doc.getAttribute('docname')
112
- //const idprefix = `${module_ === 'ROOT' ? '' : module_ + ':'}${docname}//`
113
- const idprefix = `${module_ === 'ROOT' ? '' : module_ + ':'}${docname.replace(/[/]/g, '::')}:::`
114
- //const idprefix = `${module_ === 'ROOT' ? '' : module_ + ':'}${docname.replace(/[/]/g, ':-:')}:--:`
116
+ const docnameForId = docname.replace(/[/]/g, '::').replace(/[.]/g, '-')
117
+ const idprefix = `${module_ === 'ROOT' ? '' : module_ + ':'}${docnameForId}:::`
115
118
  buffer.push('')
116
119
  buffer.push(`:docname: ${docname}`)
117
120
  buffer.push(`:page-module: ${module_}`)
@@ -206,9 +209,23 @@ function aggregateAsciiDoc (
206
209
  for (let i = idx; i < block.lines.length + (delimited ? idx + 2 : idx); i++) ignoreLines.push(i)
207
210
  }
208
211
  })
212
+ let skipping
209
213
  for (let idx = 0, len = lines.length; idx < len; idx++) {
210
214
  if (~ignoreLines.indexOf(idx)) continue
211
215
  let line = lines[idx]
216
+ if (line.startsWith('//')) {
217
+ if (line[2] !== '/') continue
218
+ if (line.length > 3 && line === '/'.repeat(line.length)) {
219
+ if (skipping) {
220
+ if (line === skipping) skipping = undefined
221
+ } else {
222
+ skipping = line
223
+ }
224
+ continue
225
+ }
226
+ } else if (skipping) {
227
+ continue
228
+ }
212
229
  if (line.charAt() === ':' && /^:(?:leveloffset: .*|!leveloffset:|leveloffset!:)$/.test(line)) {
213
230
  if (lines[idx - 1] === '') lines[idx - 1] = undefined
214
231
  lines[idx] = undefined
@@ -251,7 +268,7 @@ function aggregateAsciiDoc (
251
268
  }
252
269
  if (~pagePart.indexOf('@') || /:.*:/.test(pagePart)) {
253
270
  // TODO: handle unresolved page better
254
- return siteUrl && (targetPage = contentCatalog.resolvePage(pagePart, page.src))
271
+ return siteUrl && (targetPage = contentCatalog.resolvePage(pagePart, page.src)) && targetPage.out
255
272
  ? `${siteUrl}${targetPage.pub.url}${fragment && '#' + fragment}[${text}]`
256
273
  : m
257
274
  } else if (pagePart.indexOf(':') < 0) {
@@ -261,11 +278,14 @@ function aggregateAsciiDoc (
261
278
  }
262
279
  if (!(targetPage = pagesInOutline.get(pagePart))) {
263
280
  // TODO: handle unresolved page better
264
- return siteUrl && (targetPage = contentCatalog.resolvePage(target, page.src))
281
+ return siteUrl && (targetPage = contentCatalog.resolvePage(target, page.src)) && targetPage.out
265
282
  ? `${siteUrl}${targetPage.pub.url}${fragment && '#' + fragment}[${text}]`
266
283
  : m
267
284
  }
268
- pagePart = pagePart.replace(/[/]/g, '::').replace(/\.adoc$/, '')
285
+ pagePart = pagePart
286
+ .replace(/[/]/g, '::')
287
+ .replace(/\.adoc$/, '')
288
+ .replace(/[.]/g, '-')
269
289
  const refid = `${pagePart}:::${fragment}`
270
290
  return `<<${refid}${text && text !== targetPage.title ? ',' + text.replace(/\\]/g, ']') : ''}>>`
271
291
  })
@@ -281,7 +301,9 @@ function aggregateAsciiDoc (
281
301
  family: 'attachment',
282
302
  relative,
283
303
  })
284
- return attachment ? `${siteUrl}${attachment.pub.url}[${text}]` : m
304
+ return attachment && attachment.out
305
+ ? `${siteUrl}${attachment.pub.url.replace(/_/g, '{underscore}')}[${text}]`
306
+ : m
285
307
  })
286
308
  }
287
309
  if (~line.indexOf('image:') && !line.startsWith('image::')) {
@@ -289,9 +311,9 @@ function aggregateAsciiDoc (
289
311
  if (isResourceSpec(target)) {
290
312
  const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
291
313
  // TODO: handle (or report) unresolved image better
292
- if (image) {
314
+ if (image && image.out) {
293
315
  image.out.assembled = true
294
- return `image:${image.pub.url.substr(1)}[${attrlist}]`
316
+ return `image:${image.out.path.replace(/_/g, '{underscore}')}[${attrlist}]`
295
317
  }
296
318
  }
297
319
  return m
@@ -348,9 +370,10 @@ function aggregateAsciiDoc (
348
370
  if (isResourceSpec(target)) {
349
371
  const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
350
372
  // FIXME: handle (or report) case when image is not resolved
351
- if (image) {
352
- lines[idx] = `${prefix}image::${image.pub.url.substr(1)}${line.substr(line.indexOf('['))}`
373
+ if (image && image.out) {
374
+ const boxedAttrlist = line.substr(line.indexOf('['))
353
375
  image.out.assembled = true
376
+ lines[idx] = `${prefix}image::${image.out.path}${boxedAttrlist}`
354
377
  }
355
378
  }
356
379
  lastImageMacroAt = [idx, imageMacroOffset]
@@ -500,7 +523,7 @@ function rewriteStyleAttribute (block, lines, idx, idprefix, replacementStyle =
500
523
  if (replacementStyle) prevLine = prevLine.replace(/^[^#.%,\]]+/, `[${replacementStyle}`)
501
524
  } else {
502
525
  prevLine = `[${
503
- replacementStyle ? rawStyle.replace(/^[^.%]*]/, replacementStyle) : rawStyle
526
+ replacementStyle ? rawStyle.replace(/^[^.%]*/, replacementStyle) : rawStyle
504
527
  }#${idprefix}${block.getId()}${prevLine.substr(rawStyle.length + 1)}`
505
528
  }
506
529
  } else {
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
+ const File = require('vinyl')
3
4
  const filterComponentVersions = require('./filter-component-versions')
4
5
  const produceAggregateDocument = require('./produce-aggregate-document')
5
6
  const selectMutableAttributes = require('./select-mutable-attributes')
@@ -7,8 +8,9 @@ const selectMutableAttributes = require('./select-mutable-attributes')
7
8
  function produceAggregateDocuments (loadAsciiDoc, contentCatalog, assemblerConfig) {
8
9
  const { insertStartPage, rootLevel, sectionMergeStrategy, asciidoc: assemblerAsciiDocConfig } = assemblerConfig
9
10
  const assemblerAsciiDocAttributes = Object.assign({}, assemblerAsciiDocConfig.attributes)
10
- const { doctype, 'source-highlighter': sourceHighlighter } = assemblerAsciiDocAttributes
11
+ const { doctype, revdate, 'source-highlighter': sourceHighlighter } = assemblerAsciiDocAttributes
11
12
  delete assemblerAsciiDocAttributes.doctype
13
+ delete assemblerAsciiDocAttributes.revdate
12
14
  delete assemblerAsciiDocAttributes['source-highlighter']
13
15
  return filterComponentVersions(contentCatalog.getComponents(), assemblerConfig.componentVersions).reduce(
14
16
  (accum, componentVersion) => {
@@ -16,7 +18,7 @@ function produceAggregateDocuments (loadAsciiDoc, contentCatalog, assemblerConfi
16
18
  if (!navigation) return accum
17
19
  const componentVersionAsciiDocConfig = getAsciiDocConfigWithAsciidoctorReducerExtension(componentVersion)
18
20
  const mergedAsciiDocConfig = Object.assign({}, componentVersionAsciiDocConfig, {
19
- attributes: Object.assign({}, componentVersionAsciiDocConfig.attributes, assemblerAsciiDocAttributes),
21
+ attributes: Object.assign({ revdate }, componentVersionAsciiDocConfig.attributes, assemblerAsciiDocAttributes),
20
22
  })
21
23
  const rootEntry = { content: title }
22
24
  const startPage = contentCatalog.getComponentVersionStartPage(componentName, version)
@@ -28,10 +30,16 @@ function produceAggregateDocuments (loadAsciiDoc, contentCatalog, assemblerConfi
28
30
  Object.assign(rootEntry, { url: startPageUrl, urlType: 'internal' })
29
31
  }
30
32
  }
31
- // Q: should we use an artificial page instead or as fallback?
32
- const mutableAttributes = startPage
33
- ? selectMutableAttributes(loadAsciiDoc, contentCatalog, startPage, mergedAsciiDocConfig)
34
- : {}
33
+ // Q: should we always use a reference page here?
34
+ const startPage_ =
35
+ startPage ??
36
+ createFile({
37
+ component: componentVersion.name,
38
+ version: componentVersion.version,
39
+ relative: '.reference-page.adoc',
40
+ origin: (componentVersion.origins || [])[0],
41
+ })
42
+ const mutableAttributes = selectMutableAttributes(loadAsciiDoc, contentCatalog, startPage_, mergedAsciiDocConfig)
35
43
  delete mutableAttributes.doctype
36
44
  accum = accum.concat(
37
45
  prepareOutlines(navigation, rootEntry, rootLevel).map((outline) =>
@@ -58,6 +66,30 @@ function produceAggregateDocuments (loadAsciiDoc, contentCatalog, assemblerConfi
58
66
  )
59
67
  }
60
68
 
69
+ function createFile (src) {
70
+ const familySegment = (src.family ??= 'page') + 's'
71
+ const path = `modules/${(src.module ??= 'ROOT')}/${familySegment}/${src.relative}`
72
+ const moduleRootPath = Array(src.relative.split('/').length - 1)
73
+ .fill('..')
74
+ .join('/')
75
+ const outPath = [
76
+ src.component === 'ROOT' ? '' : src.component,
77
+ src.version,
78
+ src.module === 'ROOT' ? '' : src.module,
79
+ src.family === 'page' ? '' : '_' + familySegment,
80
+ src.family === 'page' ? src.relative.replace(/\.adoc$/, '.html') : src.relative,
81
+ ]
82
+ .filter((it) => it)
83
+ .join('/')
84
+ return new File({
85
+ path,
86
+ contents: src.contents ?? Buffer.alloc(0),
87
+ src,
88
+ out: { path: outPath },
89
+ pub: { url: '/' + outPath, moduleRootPath },
90
+ })
91
+ }
92
+
61
93
  function getAsciiDocConfigWithAsciidoctorReducerExtension (componentVersion) {
62
94
  const asciidoctorReducerExtension = require('./asciidoctor/reducer-extension') // NOTE: must be required lazily
63
95
  const asciidocConfig = componentVersion.asciidoc
@@ -81,13 +113,16 @@ function includedInNav (items, url) {
81
113
  return items.find((it) => it.url === url || includedInNav(it.items || [], url))
82
114
  }
83
115
 
116
+ // when root level is 0, merge the navigation into the rootEntry
117
+ // when root level is 1, create navigation per navigation menu
118
+ // in this case, if there's only a single navigation menu with no title, promote each top-level item to a menu
84
119
  function prepareOutlines (navigation, rootEntry, rootLevel) {
85
120
  if (rootLevel === 0 || navigation.length === 1) {
86
121
  const navBranch =
87
122
  navigation.length === 1
88
123
  ? navigation[0]
89
124
  : { items: navigation.reduce((navTree, it) => navTree.concat(it.content ? it : it.items), []) }
90
- return [Object.assign(rootEntry, navBranch)]
125
+ return rootLevel === 0 || navBranch.content ? [Object.assign(rootEntry, navBranch)] : navBranch.items
91
126
  }
92
127
  return navigation.reduce((navTree, it) => navTree.concat(it.content ? it : it.items), [rootEntry])
93
128
  }
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { PassThrough } = require('stream')
3
+ const { PassThrough } = require('node:stream')
4
4
 
5
5
  // adapted from https://github.com/jpommerening/node-lazystream/blob/master/lib/lazystream.js | license: MIT
6
6
  class LazyReadable extends PassThrough {
@@ -1,8 +1,8 @@
1
1
  'use strict'
2
2
 
3
- const fs = require('fs')
3
+ const fs = require('node:fs')
4
4
  const LazyReadable = require('./lazy-readable')
5
- const { spawn } = require('child_process')
5
+ const { spawn } = require('node:child_process')
6
6
 
7
7
  const IS_WIN = process.platform === 'win32'
8
8
  const DBL_QUOTE_RX = /"/g
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antora/assembler",
3
- "version": "1.0.0-alpha.6",
3
+ "version": "1.0.0-alpha.7",
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)",
@@ -31,7 +31,7 @@
31
31
  "dependencies": {
32
32
  "@antora/expand-path-helper": "~2.0",
33
33
  "braces": "~3.0",
34
- "picomatch": "~2.3",
34
+ "picomatch": "~3.0",
35
35
  "vinyl": "~2.2",
36
36
  "js-yaml": "~4.1"
37
37
  },