@antora/playbook-builder 3.2.0-alpha.1 → 3.2.0-alpha.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/README.md +1 -1
- package/lib/build-playbook.js +47 -11
- package/lib/config/schema.js +33 -22
- package/lib/solitary-convict.js +65 -25
- package/package.json +11 -6
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Antora Playbook Builder
|
|
2
2
|
|
|
3
3
|
The Playbook Builder is the configuration component for Antora.
|
|
4
|
-
It
|
|
4
|
+
It’s responsible for building a playbook object from user input that’s then used for configuring components in an Antora generator pipeline.
|
|
5
5
|
|
|
6
6
|
[Antora](https://antora.org) is a modular static site generator designed for creating documentation sites from AsciiDoc documents.
|
|
7
7
|
Its site generator aggregates documents from versioned content repositories and processes them using [Asciidoctor](https://asciidoctor.org).
|
package/lib/build-playbook.js
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
const convict = require('./solitary-convict')
|
|
4
4
|
const defaultSchema = require('./config/schema')
|
|
5
|
-
const fs = require('fs')
|
|
6
|
-
const ospath = require('path')
|
|
5
|
+
const fs = require('node:fs')
|
|
6
|
+
const ospath = require('node:path')
|
|
7
|
+
const parseArgs = require('yargs-parser')
|
|
8
|
+
const yaml = require('js-yaml')
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Builds a playbook object according to the provided schema from the specified
|
|
@@ -11,7 +13,7 @@ const ospath = require('path')
|
|
|
11
13
|
*
|
|
12
14
|
* Accepts an array of command line arguments (in the form of option flags and
|
|
13
15
|
* switches) and a map of environment variables and translates this data into a
|
|
14
|
-
* playbook object according the
|
|
16
|
+
* playbook object according the specified schema. If no schema is
|
|
15
17
|
* specified, the default schema provided by this package is used.
|
|
16
18
|
*
|
|
17
19
|
* @memberof playbook-builder
|
|
@@ -28,8 +30,22 @@ const ospath = require('path')
|
|
|
28
30
|
* marked in the schema as preserve, all keys in the playbook are camelCased.
|
|
29
31
|
*/
|
|
30
32
|
function buildPlaybook (args = [], env = process.env, schema = defaultSchema, beforeValidate = undefined) {
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
+
const parsedArgs = parseArgs(args, { configuration: { 'dot-notation': false } })
|
|
34
|
+
const opts = { args: [], env: {} }
|
|
35
|
+
const config = Object.assign(convict(schema, opts), { getModel, getSchema })
|
|
36
|
+
Object.assign(opts, { args, env })
|
|
37
|
+
let playbook
|
|
38
|
+
if ('playbook' in schema) {
|
|
39
|
+
const playbookEnv = schema.playbook.env
|
|
40
|
+
if (playbookEnv != null) playbook = env[playbookEnv]
|
|
41
|
+
const playbookArg = schema.playbook.arg
|
|
42
|
+
if (playbookArg != null) playbook = parsedArgs[playbookArg] ?? playbook
|
|
43
|
+
if (playbook === undefined) {
|
|
44
|
+
if (config.has('playbook')) playbook = config.get('playbook')
|
|
45
|
+
} else {
|
|
46
|
+
config.set('playbook', playbook)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
33
49
|
let absPlaybookPath
|
|
34
50
|
if (playbook) {
|
|
35
51
|
if (ospath.extname((absPlaybookPath = ospath.resolve(playbook)))) {
|
|
@@ -50,10 +66,11 @@ function buildPlaybook (args = [], env = process.env, schema = defaultSchema, be
|
|
|
50
66
|
}
|
|
51
67
|
}
|
|
52
68
|
try {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
69
|
+
Object.assign(opts, { args: [] })
|
|
70
|
+
playbook ? config.loadFile(absPlaybookPath) : config.load({})
|
|
71
|
+
importArguments(config, parsedArgs)
|
|
72
|
+
Object.assign(opts, { args })
|
|
73
|
+
if (playbook && playbook !== absPlaybookPath) config.set('playbook', absPlaybookPath)
|
|
57
74
|
const beforeValidateFromSchema = config._def[Symbol.for('convict.beforeValidate')]
|
|
58
75
|
if (beforeValidateFromSchema) beforeValidateFromSchema(config)
|
|
59
76
|
if (beforeValidate) beforeValidate(config)
|
|
@@ -82,9 +99,8 @@ function camelCaseKeys (o, stopPaths = [], p = '') {
|
|
|
82
99
|
function getModel (name) {
|
|
83
100
|
let config = this
|
|
84
101
|
const data = config.get(name)
|
|
85
|
-
|
|
102
|
+
const schema = config.getSchema(name)
|
|
86
103
|
if (name) {
|
|
87
|
-
schema = name.split('.').reduce((accum, key) => accum._cvtProperties[key], schema)
|
|
88
104
|
config = Object.assign(convict(name.split('.').reduce((def, key) => def[key], config._def)), { _instance: data })
|
|
89
105
|
}
|
|
90
106
|
config.validate({ allowed: 'strict' })
|
|
@@ -97,6 +113,10 @@ function getModel (name) {
|
|
|
97
113
|
return model
|
|
98
114
|
}
|
|
99
115
|
|
|
116
|
+
function getSchema (name) {
|
|
117
|
+
return name ? name.split('.').reduce((accum, key) => accum._cvtProperties[key], this._schema) : this._schema
|
|
118
|
+
}
|
|
119
|
+
|
|
100
120
|
function getStopPaths (schemaProperties, schemaPath = [], stopPaths = []) {
|
|
101
121
|
for (const [key, { preserve, _cvtProperties }] of Object.entries(schemaProperties)) {
|
|
102
122
|
if (preserve) {
|
|
@@ -110,6 +130,22 @@ function getStopPaths (schemaProperties, schemaPath = [], stopPaths = []) {
|
|
|
110
130
|
return stopPaths
|
|
111
131
|
}
|
|
112
132
|
|
|
133
|
+
function importArguments (config, args) {
|
|
134
|
+
for (const [argName, configKey] of Object.entries(config._argv)) {
|
|
135
|
+
const argVal = args[argName]
|
|
136
|
+
if (argVal === undefined) continue
|
|
137
|
+
const argFormat = config.getSchema(configKey).format
|
|
138
|
+
let argValStr = argVal
|
|
139
|
+
if (argFormat === 'map' || argFormat === 'primitive-map') {
|
|
140
|
+
const dumpOpts = { condenseFlow: true, flowLevel: 0, noCompatMode: true, quotingType: '"' }
|
|
141
|
+
argValStr = yaml.dump(Array.isArray(argVal) ? argVal : [argVal], dumpOpts)
|
|
142
|
+
} else if (Array.isArray(argVal)) {
|
|
143
|
+
argValStr = argVal.join(',')
|
|
144
|
+
}
|
|
145
|
+
config.set(configKey, argValStr)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
113
149
|
function getDetails (playbook, absPlaybookPath) {
|
|
114
150
|
if (playbook === absPlaybookPath) return ''
|
|
115
151
|
return ` (${ospath.isAbsolute(playbook) ? '' : 'cwd: ' + process.cwd() + ', '}playbook: ${playbook})`
|
package/lib/config/schema.js
CHANGED
|
@@ -17,7 +17,8 @@ module.exports = {
|
|
|
17
17
|
extensions: {
|
|
18
18
|
doc:
|
|
19
19
|
'A list of extensions that listen for lifecycle events. ' +
|
|
20
|
-
'Each extension is specified as a require request string or an object with a require key.'
|
|
20
|
+
'Each extension is specified as a require request string or an object with a require key. ' +
|
|
21
|
+
'May be specified multiple times.',
|
|
21
22
|
format: 'require-array',
|
|
22
23
|
default: [],
|
|
23
24
|
arg: 'extension',
|
|
@@ -66,7 +67,7 @@ module.exports = {
|
|
|
66
67
|
content: {
|
|
67
68
|
branches: {
|
|
68
69
|
doc: 'The default branch pattern to use when no specific pattern is provided.',
|
|
69
|
-
format:
|
|
70
|
+
format: 'array-or-string',
|
|
70
71
|
default: ['HEAD', 'v{0..9}*'],
|
|
71
72
|
},
|
|
72
73
|
edit_url: {
|
|
@@ -82,9 +83,14 @@ module.exports = {
|
|
|
82
83
|
},
|
|
83
84
|
tags: {
|
|
84
85
|
doc: 'The default tag pattern to use when no specific pattern is provided.',
|
|
85
|
-
format:
|
|
86
|
+
format: 'array-or-string',
|
|
86
87
|
default: undefined,
|
|
87
88
|
},
|
|
89
|
+
worktrees: {
|
|
90
|
+
doc: 'The default worktrees pattern to use when no specific pattern is provided.',
|
|
91
|
+
format: 'boolean-or-array-or-string',
|
|
92
|
+
default: '.',
|
|
93
|
+
},
|
|
88
94
|
},
|
|
89
95
|
ui: {
|
|
90
96
|
bundle: {
|
|
@@ -167,7 +173,12 @@ module.exports = {
|
|
|
167
173
|
fetch_concurrency: {
|
|
168
174
|
doc: 'The maximum number of fetch or clone operations that are permitted to run at once. Use 0 for unlimited.',
|
|
169
175
|
format: 'int',
|
|
170
|
-
default:
|
|
176
|
+
default: 1,
|
|
177
|
+
},
|
|
178
|
+
fetch_depth: {
|
|
179
|
+
doc: 'Preferred number of commits to fetch from remote repository. 0 indicates full history.',
|
|
180
|
+
format: 'int',
|
|
181
|
+
default: 1,
|
|
171
182
|
},
|
|
172
183
|
plugins: {
|
|
173
184
|
credential_manager: {
|
|
@@ -181,6 +192,11 @@ module.exports = {
|
|
|
181
192
|
default: undefined,
|
|
182
193
|
},
|
|
183
194
|
},
|
|
195
|
+
read_concurrency: {
|
|
196
|
+
doc: 'The maximum number of git indexes that are read into memory at once. Use 0 for unlimited.',
|
|
197
|
+
format: 'int',
|
|
198
|
+
default: 0,
|
|
199
|
+
},
|
|
184
200
|
},
|
|
185
201
|
network: {
|
|
186
202
|
http_proxy: {
|
|
@@ -253,23 +269,13 @@ module.exports = {
|
|
|
253
269
|
arg: 'log-failure-level',
|
|
254
270
|
env: 'ANTORA_LOG_FAILURE_LEVEL',
|
|
255
271
|
},
|
|
256
|
-
format:
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
get (target, property) {
|
|
266
|
-
if (property !== 'default') return target[property]
|
|
267
|
-
return process.env.CI === 'true' || (process.env.IS_TTY || String(process.stdout.isTTY)) === 'true'
|
|
268
|
-
? 'pretty'
|
|
269
|
-
: 'json'
|
|
270
|
-
},
|
|
271
|
-
}
|
|
272
|
-
),
|
|
272
|
+
format: {
|
|
273
|
+
doc: 'Set the format of log messages. Defaults to pretty if CI=true or stdout is a TTY, json otherwise.',
|
|
274
|
+
format: ['json', 'pretty'],
|
|
275
|
+
default: 'auto',
|
|
276
|
+
arg: 'log-format',
|
|
277
|
+
env: 'ANTORA_LOG_FORMAT',
|
|
278
|
+
},
|
|
273
279
|
destination: {
|
|
274
280
|
file: {
|
|
275
281
|
doc: 'Write log messages to this file or stream. Defaults to stderr if format is pretty, stdout otherwise.',
|
|
@@ -344,8 +350,13 @@ module.exports = {
|
|
|
344
350
|
default: undefined,
|
|
345
351
|
},
|
|
346
352
|
},
|
|
347
|
-
[Symbol.for('convict.beforeValidate')]: ({
|
|
353
|
+
[Symbol.for('convict.beforeValidate')]: ({ getEnv, _instance: data, _schema: schema }) => {
|
|
348
354
|
const runtime = data.runtime
|
|
355
|
+
const log = runtime.log
|
|
356
|
+
if (log.format === 'auto') {
|
|
357
|
+
const env = getEnv()
|
|
358
|
+
log.format = env.CI === 'true' || (env.IS_TTY || String(process.stdout.isTTY)) === 'true' ? 'pretty' : 'json'
|
|
359
|
+
}
|
|
349
360
|
if (runtime.silent) {
|
|
350
361
|
if (runtime.quiet === false) runtime.quiet = true
|
|
351
362
|
if (runtime.log.level !== 'silent') runtime.log.level = 'silent'
|
package/lib/solitary-convict.js
CHANGED
|
@@ -5,9 +5,10 @@ const json = require('json5')
|
|
|
5
5
|
const toml = require('@iarna/toml')
|
|
6
6
|
const yaml = require('js-yaml')
|
|
7
7
|
|
|
8
|
-
const ARGS_SCANNER_RX = /(?:([^=,]+)|(?==))(?:,|$|=(|("|').*?\3|[^,]+)(?:,|$))/g
|
|
9
8
|
const PRIMITIVE_TYPES = [Boolean, Number, String]
|
|
9
|
+
const COERCE_SCHEMA = yaml.FAILSAFE_SCHEMA
|
|
10
10
|
const YAML_SCHEMA = yaml.CORE_SCHEMA.extend({ implicit: [yaml.types.merge] })
|
|
11
|
+
const YAML_PREFIX_RX = new RegExp('!!((?:auto|str|bool|int|float|seq|map)(?= )|null(?=$))(?: |$)')
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* A convict function wrapper that registers custom formats and parsers and
|
|
@@ -35,6 +36,22 @@ function registerParsers (convict) {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
function registerFormats (convict) {
|
|
39
|
+
convict.addFormat({
|
|
40
|
+
name: 'array-or-string',
|
|
41
|
+
validate: (val) => {
|
|
42
|
+
if (!(val == null || val.constructor === String || Array.isArray(val))) {
|
|
43
|
+
throw new Error('must be an array, string, or null')
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
convict.addFormat({
|
|
48
|
+
name: 'boolean-or-array-or-string',
|
|
49
|
+
validate: (val) => {
|
|
50
|
+
if (!(val == null || typeof val === 'boolean' || val.constructor === String || Array.isArray(val))) {
|
|
51
|
+
throw new Error('must be a boolean, array, string, or null')
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
})
|
|
38
55
|
convict.addFormat({
|
|
39
56
|
name: 'map',
|
|
40
57
|
validate: (val) => {
|
|
@@ -43,11 +60,25 @@ function registerFormats (convict) {
|
|
|
43
60
|
coerce: (val, config, name) => {
|
|
44
61
|
if (config == null) return val
|
|
45
62
|
const accum = config.has(name) ? config.get(name) : {}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
63
|
+
const entries = yaml.load(val, { schema: COERCE_SCHEMA })
|
|
64
|
+
for (const entry of Array.isArray(entries) ? entries : [entries]) {
|
|
65
|
+
let k = entry
|
|
66
|
+
let v = ''
|
|
67
|
+
const equalsIdx = entry.indexOf('=')
|
|
68
|
+
if (~equalsIdx) {
|
|
69
|
+
if (!(k = entry.slice(0, equalsIdx))) continue
|
|
70
|
+
v = entry.slice(equalsIdx + 1)
|
|
71
|
+
}
|
|
72
|
+
let parsed = v
|
|
73
|
+
if (v) {
|
|
74
|
+
const match = v.match(YAML_PREFIX_RX)
|
|
75
|
+
if (match) {
|
|
76
|
+
try {
|
|
77
|
+
parsed = yaml.load(match[1] === 'auto' ? v.slice(7) : v, { schema: yaml.CORE_SCHEMA })
|
|
78
|
+
} catch {}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
accum[k] = parsed
|
|
51
82
|
}
|
|
52
83
|
return accum
|
|
53
84
|
},
|
|
@@ -68,20 +99,26 @@ function registerFormats (convict) {
|
|
|
68
99
|
coerce: (val, config, name) => {
|
|
69
100
|
if (config == null) return val
|
|
70
101
|
const accum = config.has(name) ? config.get(name) : {}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
102
|
+
const entries = yaml.load(val, { schema: COERCE_SCHEMA })
|
|
103
|
+
for (const entry of Array.isArray(entries) ? entries : [entries]) {
|
|
104
|
+
let k = entry
|
|
105
|
+
let v = ''
|
|
106
|
+
const equalsIdx = entry.indexOf('=')
|
|
107
|
+
if (~equalsIdx) {
|
|
108
|
+
if (!(k = entry.slice(0, equalsIdx))) continue
|
|
109
|
+
v = entry.slice(equalsIdx + 1)
|
|
110
|
+
}
|
|
111
|
+
let parsed = v
|
|
112
|
+
if (v) {
|
|
113
|
+
const match = v.match(YAML_PREFIX_RX)
|
|
114
|
+
if (match) {
|
|
115
|
+
try {
|
|
116
|
+
parsed = yaml.load(match[1] === 'auto' ? v.slice(7) : v, { schema: yaml.CORE_SCHEMA })
|
|
117
|
+
if (parsed && !~PRIMITIVE_TYPES.indexOf(parsed.constructor)) parsed = v
|
|
118
|
+
} catch {}
|
|
82
119
|
}
|
|
83
|
-
accum[~k.indexOf('-') ? k.replace(/-/g, '_') : k] = parsed
|
|
84
120
|
}
|
|
121
|
+
accum[~k.indexOf('-') ? k.replace(/-/g, '_') : k] = parsed
|
|
85
122
|
}
|
|
86
123
|
return accum
|
|
87
124
|
},
|
|
@@ -89,19 +126,19 @@ function registerFormats (convict) {
|
|
|
89
126
|
convict.addFormat({
|
|
90
127
|
name: 'require-array',
|
|
91
128
|
validate: (val) => {
|
|
92
|
-
if (!Array.isArray(val)) throw new Error('must be
|
|
129
|
+
if (!Array.isArray(val)) throw new Error('must be an array')
|
|
93
130
|
},
|
|
94
131
|
coerce: (val, config, name) => {
|
|
95
|
-
const accum = config
|
|
96
|
-
val.split(',')
|
|
97
|
-
if (~accum.indexOf(v))
|
|
132
|
+
const accum = config?.has(name) ? config.get(name) : []
|
|
133
|
+
for (const v of val.split(',')) {
|
|
134
|
+
if (~accum.indexOf(v)) continue
|
|
98
135
|
const match = accum.find((it) => it.constructor === Object && it.id === v)
|
|
99
136
|
if (match) {
|
|
100
137
|
if (match.enabled === false) match.enabled = true
|
|
101
138
|
} else {
|
|
102
139
|
accum.push(v)
|
|
103
140
|
}
|
|
104
|
-
}
|
|
141
|
+
}
|
|
105
142
|
return accum
|
|
106
143
|
},
|
|
107
144
|
})
|
|
@@ -109,7 +146,7 @@ function registerFormats (convict) {
|
|
|
109
146
|
name: 'boolean-or-string',
|
|
110
147
|
validate: (val) => {
|
|
111
148
|
if (!(val == null || val.constructor === String || val.constructor === Boolean)) {
|
|
112
|
-
throw new Error('must be a boolean or
|
|
149
|
+
throw new Error('must be a boolean, string, or null')
|
|
113
150
|
}
|
|
114
151
|
},
|
|
115
152
|
})
|
|
@@ -151,7 +188,10 @@ function registerFormats (convict) {
|
|
|
151
188
|
}
|
|
152
189
|
if (~parsedUrl.pathname.indexOf('%20')) throw new Error('pathname segment must not contain spaces')
|
|
153
190
|
},
|
|
154
|
-
coerce: (val) =>
|
|
191
|
+
coerce: (val) => {
|
|
192
|
+
if (!val || val === '~') return null
|
|
193
|
+
return val.length > 1 && val.charAt(val.length - 1) === '/' ? val.slice(0, -1) : val
|
|
194
|
+
},
|
|
155
195
|
})
|
|
156
196
|
}
|
|
157
197
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antora/playbook-builder",
|
|
3
|
-
"version": "3.2.0-alpha.
|
|
3
|
+
"version": "3.2.0-alpha.10",
|
|
4
4
|
"description": "Builds a playbook object from user input for configuring successive documentation components in an Antora pipeline.",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"author": "OpenDevise Inc. (https://opendevise.com)",
|
|
@@ -10,7 +10,11 @@
|
|
|
10
10
|
"Hubert SABLONNIÈRE <hubert.sablonniere@gmail.com>"
|
|
11
11
|
],
|
|
12
12
|
"homepage": "https://antora.org",
|
|
13
|
-
"repository":
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://gitlab.com/antora/antora.git",
|
|
16
|
+
"directory": "packages/playbook-builder"
|
|
17
|
+
},
|
|
14
18
|
"bugs": {
|
|
15
19
|
"url": "https://gitlab.com/antora/antora/issues"
|
|
16
20
|
},
|
|
@@ -26,10 +30,11 @@
|
|
|
26
30
|
"@iarna/toml": "~2.2",
|
|
27
31
|
"convict": "~6.2",
|
|
28
32
|
"js-yaml": "~4.1",
|
|
29
|
-
"json5": "~2.2"
|
|
33
|
+
"json5": "~2.2",
|
|
34
|
+
"yargs-parser": "~20.2"
|
|
30
35
|
},
|
|
31
36
|
"engines": {
|
|
32
|
-
"node": ">=
|
|
37
|
+
"node": ">=18.0.0"
|
|
33
38
|
},
|
|
34
39
|
"files": [
|
|
35
40
|
"lib/"
|
|
@@ -44,7 +49,7 @@
|
|
|
44
49
|
],
|
|
45
50
|
"scripts": {
|
|
46
51
|
"test": "_mocha",
|
|
47
|
-
"prepublishOnly": "
|
|
48
|
-
"postpublish": "
|
|
52
|
+
"prepublishOnly": "npx -y downdoc@latest --prepublish",
|
|
53
|
+
"postpublish": "npx -y downdoc@latest --postpublish"
|
|
49
54
|
}
|
|
50
55
|
}
|