@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.
@@ -1,71 +1,34 @@
1
1
  'use strict'
2
2
 
3
- const { compile: bracesToGroup } = require('braces')
4
- const { makeRe: makePicomatchRx } = require('picomatch')
3
+ const { filterCollection, compilePattern } = require('./util/matcher')
5
4
 
6
- const PICOMATCH_OPTS = {
7
- bash: true,
8
- expandRange: (begin, end, step, opts) => bracesToGroup(opts ? `{${begin}..${end}..${step}}` : `{${begin}..${end}}`),
9
- fastpaths: false,
10
- nobracket: true,
11
- noglobstar: true,
12
- nonegate: true,
13
- noquantifiers: true,
14
- regex: false,
15
- strictSlashes: true,
16
- }
17
-
18
- const VERSION_SEPARATOR_RX = /@(?!\()/
19
-
20
- function compilePatterns (patterns) {
21
- if (patterns[0].charAt() === '!') patterns = ['**', ...patterns]
22
- return patterns.map((pattern) => {
23
- const negated = pattern.charAt() === '!'
24
- if (negated) pattern = pattern.slice(1)
25
- let version
26
- const separatorIdx = pattern.search(VERSION_SEPARATOR_RX)
27
- if (~separatorIdx) {
28
- pattern = (version = true) && `${pattern.slice(0, separatorIdx)}%${pattern.slice(separatorIdx + 1) || '*'}`
29
- }
30
- return Object.assign(makePicomatchRx(pattern, PICOMATCH_OPTS), {
31
- globstar: pattern === '**',
32
- negated,
33
- star: pattern === '*',
34
- version,
35
- })
36
- })
37
- }
5
+ const VERSION_SEPARATOR_RX = /@(?!\()(?=(\w)?)/g
6
+ const US = '\x1f'
38
7
 
39
- function filterComponentVersions (components, patterns) {
8
+ function filterComponentVersions ({ names: patterns, prereleases = true }, components) {
40
9
  if (!patterns.length) return []
41
- const rxs = compilePatterns(patterns)
42
- return components.reduce((accum, { latest, versions }) => {
43
- accum.push(
44
- ...versions.filter((version) => {
45
- let matched
46
- for (const rx of rxs) {
47
- let voteIfMatched
48
- if (matched) {
49
- if (rx.negated) voteIfMatched = false
50
- } else if (!rx.negated) {
51
- voteIfMatched = true
52
- }
53
- if (voteIfMatched == null) continue
54
- if (rx.globstar) {
55
- matched = voteIfMatched
56
- } else if (rx.star) {
57
- if (version === latest) matched = voteIfMatched
58
- } else if (rx.version) {
59
- if (rx.test(`${version.version}%${version.name}`)) matched = voteIfMatched
60
- } else if (version === latest && rx.test(version.name)) {
61
- matched = voteIfMatched
62
- }
63
- }
64
- return matched
65
- })
66
- )
10
+ const candidateMap = components.reduce((accum, { name: componentName, latest, versions }) => {
11
+ for (const version of versions) {
12
+ if (!prereleases && version.prerelease && version !== latest) continue
13
+ accum[`${version.version}${US}${componentName}`] = { componentName, latest: version === latest, value: version }
14
+ }
67
15
  return accum
68
- }, [])
16
+ }, {})
17
+ if (patterns[0].charAt() === '!') patterns = ['**', ...patterns]
18
+ const result = filterCollection(Object.keys(candidateMap), patterns, {
19
+ compilePattern: new Proxy(compilePattern, {
20
+ apply: (target, self, [pattern]) => {
21
+ if (~pattern.indexOf('@')) pattern = pattern.replace(VERSION_SEPARATOR_RX, (_, c) => US + (c ? '' : '*'))
22
+ return target.call(self, pattern)
23
+ },
24
+ }),
25
+ test: (rx, candidate) => {
26
+ if (rx.globstar) return true
27
+ if (rx.star) return candidateMap[candidate].latest
28
+ return rx.test(candidate) || (candidateMap[candidate].latest && rx.test(candidateMap[candidate].componentName))
29
+ },
30
+ })
31
+ return result.map((it) => candidateMap[it].value)
69
32
  }
70
33
 
71
34
  module.exports = filterComponentVersions
package/lib/index.js CHANGED
@@ -3,4 +3,4 @@
3
3
  const assembleContent = require('./assemble-content')
4
4
  const configure = require('./configure')
5
5
 
6
- module.exports = { assembleContent, configure, configureAssembler: configure }
6
+ module.exports = { assembleContent, configure }
@@ -1,45 +1,50 @@
1
1
  'use strict'
2
2
 
3
+ const deepClone = require('./util/deep-clone')
3
4
  const expandPath = require('@antora/expand-path-helper')
4
5
  const fsp = require('node:fs/promises')
5
6
  const os = require('node:os')
7
+ const ospath = require('node:path')
6
8
  const yaml = require('js-yaml')
7
9
 
8
- const ASSEMBLY_KEYS = [
9
- 'rootLevel',
10
- 'insertStartPage',
11
- 'sectionMergeStrategy',
12
- 'linkReferenceStyle',
13
- 'dropExplicitXrefText',
14
- ]
10
+ const { CAMEL_CASE_STOP_KEYS, LEGACY_ASSEMBLY_KEYS } = require('./constants')
11
+ const PACKAGE_NAME = require('../package.json').name
12
+ const YAML_SCHEMA = yaml.CORE_SCHEMA.withTags(yaml.mergeTag)
15
13
 
16
- function loadConfig (playbook, configSource = './antora-assembler.yml') {
14
+ function loadConfig (configSource, playbook, preferredQualifier = '') {
15
+ let resolvedConfigSource
17
16
  return (
18
- configSource.constructor === Object
19
- ? Promise.resolve(configSource)
20
- : fsp
21
- .access((configSource = expandPath(configSource, { dot: playbook.dir })))
22
- .then(
23
- () => true,
24
- () => false
25
- )
26
- .then((exists) =>
27
- exists
28
- ? fsp
29
- .readFile(configSource)
30
- .then((data) => Object.assign(camelCaseKeys(yaml.load(data), ['asciidoc']), { file: configSource }))
31
- : {}
32
- )
17
+ configSource?.constructor === Object
18
+ ? Promise.resolve(deepClone(configSource))
19
+ : fileExists(
20
+ (resolvedConfigSource = expandPath(configSource ?? `./antora-assembler${preferredQualifier}.yml`, {
21
+ dot: playbook.dir,
22
+ }))
23
+ )
24
+ .then((exists) => {
25
+ if (exists || configSource || !preferredQualifier) return exists
26
+ return fileExists(
27
+ (resolvedConfigSource =
28
+ resolvedConfigSource.substring(0, resolvedConfigSource.length - 4 - preferredQualifier.length) + '.yml')
29
+ )
30
+ })
31
+ .then((exists) => {
32
+ if (!exists) {
33
+ let logger
34
+ if (configSource && (logger = this?.getLogger?.(PACKAGE_NAME))) {
35
+ const ctx = { file: { path: configSource } }
36
+ logger.warn(ctx, 'Could not resolve config file; reverting to default settings')
37
+ }
38
+ return {}
39
+ }
40
+ return fsp.readFile(resolvedConfigSource).then((data) =>
41
+ Object.assign(camelCaseKeys(yaml.load(data, { schema: YAML_SCHEMA }), CAMEL_CASE_STOP_KEYS), {
42
+ file: resolvedConfigSource,
43
+ })
44
+ )
45
+ })
33
46
  ).then((config) => {
34
- if (config.enabled === false) return undefined
35
- let asciidocAttrs
36
- if (!config.asciidoc) {
37
- config.asciidoc = { attributes: (asciidocAttrs = {}) }
38
- } else if (!(asciidocAttrs = config.asciidoc.attributes)) {
39
- config.asciidoc.attributes = asciidocAttrs = {}
40
- }
41
- if (!('revdate' in asciidocAttrs)) asciidocAttrs.revdate = getLocalDate()
42
- asciidocAttrs['page-partial'] = null
47
+ if (config.enabled === false) return config
43
48
  const remapComponentVersionsKey = !('componentVersionFilter' in config)
44
49
  const componentVersionFilter = (config.componentVersionFilter ??= {})
45
50
  if (remapComponentVersionsKey && 'componentVersions' in config) {
@@ -48,20 +53,36 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
48
53
  }
49
54
  if (componentVersionFilter.names == null) {
50
55
  componentVersionFilter.names = ['*']
51
- } else if (typeof componentVersionFilter.names === 'string') {
56
+ } else if (componentVersionFilter.names.constructor === String) {
52
57
  componentVersionFilter.names = componentVersionFilter.names.split(', ')
53
58
  }
54
59
  const remapAssemblyKeys = !('assembly' in config)
55
60
  const assembly = (config.assembly ??= {})
56
61
  if (remapAssemblyKeys) {
57
- for (const key of ASSEMBLY_KEYS) {
62
+ for (const key of LEGACY_ASSEMBLY_KEYS) {
58
63
  if (!(key in config)) continue
59
64
  assembly[key] = config[key]
60
65
  delete config[key]
61
66
  }
62
67
  }
63
- if (!('doctype' in assembly)) assembly.doctype = 'doctype' in asciidocAttrs ? asciidocAttrs.doctype : 'book'
64
- delete asciidocAttrs.doctype
68
+ let assemblyAttrs
69
+ if ('attributes' in assembly) {
70
+ assemblyAttrs = assembly.attributes ??= {}
71
+ if ('asciidoc' in config) delete config.asciidoc
72
+ } else if ('asciidoc' in config) {
73
+ assemblyAttrs = assembly.attributes = config.asciidoc?.attributes ?? {}
74
+ delete config.asciidoc
75
+ } else {
76
+ assemblyAttrs = assembly.attributes = {}
77
+ }
78
+ assemblyAttrs['page-partial'] = null
79
+ config.asciidoc = Object.defineProperty({}, 'attributes', {
80
+ get: function () {
81
+ return this.assembly.attributes
82
+ }.bind(config),
83
+ })
84
+ if (!('doctype' in assembly)) assembly.doctype = 'doctype' in assemblyAttrs ? assemblyAttrs.doctype : 'book'
85
+ delete assemblyAttrs.doctype
65
86
  if (!('rootLevel' in assembly)) assembly.rootLevel = 0
66
87
  if (!('insertStartPage' in assembly)) assembly.insertStartPage = true
67
88
  if (['discrete', 'fuse', 'enclose'].indexOf(assembly.sectionMergeStrategy) < 0) {
@@ -73,12 +94,25 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
73
94
  if (['always', 'if-redundant', 'never'].indexOf(assembly.dropExplicitXrefText) < 0) {
74
95
  assembly.dropExplicitXrefText = 'never'
75
96
  }
97
+ assembly.revdate = getLocalDate()
76
98
  const build = (config.build ??= {})
77
- if (build.dir === '$' + '{playbook.output.dir}') {
78
- throw new Error('Not implemented')
79
- }
80
99
  build.dir &&= expandPath(build.dir, { dot: playbook.dir })
81
- build.cwd = playbook.dir // use playbook.dir for finding and loading require scripts
100
+ // used as cwd of command (and any scripts it requires)
101
+ build.cwd = build.cwd == null ? playbook.dir : expandPath(build.cwd, { dot: playbook.dir })
102
+ const command = build.command
103
+ if (command) {
104
+ if (command.constructor !== String) {
105
+ build.command = String(command)
106
+ } else if (~command.indexOf('$')) {
107
+ const vars = {
108
+ $NODE: process.execPath,
109
+ $NPM: ospath.join(process.execPath, '../npm'),
110
+ $NPX: ospath.join(process.execPath, '../npx'),
111
+ $PWD: build.cwd,
112
+ }
113
+ build.command = build.command.replace(/^\$(?:NODE|NP[MX])(?= )|\$PWD\b/g, (ref) => vars[ref])
114
+ }
115
+ }
82
116
  if (!('clean' in build) && 'output' in playbook) build.clean = playbook.output.clean
83
117
  if (!('publish' in build)) build.publish = true
84
118
  if ('keepAggregateSource' in build) {
@@ -100,18 +134,25 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
100
134
  })
101
135
  }
102
136
 
103
- function camelCaseKeys (o, stopPaths = [], p = undefined) {
137
+ function camelCaseKeys (o, stopPaths, p = undefined) {
104
138
  if (Array.isArray(o)) return o.map((it) => camelCaseKeys(it, stopPaths, p))
105
139
  if (o == null || o.constructor !== Object) return o
106
140
  const pathPrefix = p ? p + '.' : ''
107
141
  const accum = {}
108
142
  for (const [k, v] of Object.entries(o)) {
109
- const camelKey = k.charAt() + k.slice(1).replace(/_([a-z])/g, (_, l) => l.toUpperCase())
110
- accum[camelKey] = ~stopPaths.indexOf(pathPrefix + camelKey) ? v : camelCaseKeys(v, stopPaths, pathPrefix + camelKey)
143
+ const camelKey = k.charAt() + k.substring(1).replace(/_([a-z])/g, (_, l) => l.toUpperCase())
144
+ accum[camelKey] = stopPaths.includes(pathPrefix + camelKey) ? v : camelCaseKeys(v, stopPaths, pathPrefix + camelKey)
111
145
  }
112
146
  return accum
113
147
  }
114
148
 
149
+ function fileExists (path) {
150
+ return fsp.access(path).then(
151
+ () => true,
152
+ () => false
153
+ )
154
+ }
155
+
115
156
  function getLocalDate (now = new Date()) {
116
157
  return new Date(now - now.getTimezoneOffset() * 60000).toISOString().split('T')[0]
117
158
  }
@@ -0,0 +1,18 @@
1
+ 'use strict'
2
+
3
+ function logCommand (logger, command, extraArgsOrAttributeOptionFlag, file, attributes) {
4
+ if (!logger?.isLevelEnabled('debug')) return
5
+ const args = (
6
+ Array.isArray(extraArgsOrAttributeOptionFlag)
7
+ ? extraArgsOrAttributeOptionFlag
8
+ : extraArgsOrAttributeOptionFlag
9
+ ? attributes.toArgs(extraArgsOrAttributeOptionFlag)
10
+ : []
11
+ ).map((it) => (~it.indexOf(' ') ? `'${it}'` : it))
12
+ const ctx = { command: [command].concat(args).join(' '), file: file.src }
13
+ const msg = `Running external command to export assembly in %s to %s: %s`
14
+ const componentVersionStr = file.src.version ? `${file.src.version}@${file.src.component}` : file.src.component
15
+ logger.debug(ctx, msg, componentVersionStr, attributes['assembler-filetype'], file.src.relative)
16
+ }
17
+
18
+ module.exports = logCommand