@antora/cli 3.0.0-alpha.7 → 3.0.0-beta.2

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 CHANGED
@@ -3,9 +3,9 @@
3
3
  The command line interface (CLI) for Antora.
4
4
 
5
5
  [Antora](https://antora.org) is a modular static site generator designed for creating documentation sites from AsciiDoc documents.
6
- Its site generator pipeline aggregates documents from versioned content repositories and processes them using [Asciidoctor](https://asciidoctor.org).
6
+ Its site generator aggregates documents from versioned content repositories and processes them using [Asciidoctor](https://asciidoctor.org).
7
7
 
8
- To run Antora, you need both the CLI and a site generator pipeline.
8
+ To run Antora, you need both the CLI and a site generator.
9
9
  Once these packages are installed, you can use the `antora` command to generate your site.
10
10
 
11
11
  ## How to Install
package/lib/cli.js CHANGED
@@ -1,15 +1,11 @@
1
- #!/usr/bin/env node
2
-
3
1
  'use strict'
4
2
 
3
+ const buildPlaybook = require('@antora/playbook-builder')
5
4
  const cli = require('./commander')
6
- // Q: can we ask the playbook builder for the config schema?
7
- const configSchema = require('@antora/playbook-builder/lib/config/schema')
8
5
  const convict = require('@antora/playbook-builder/lib/solitary-convict')
9
- const { finalizeLogger } = require('@antora/logger')
10
6
  const ospath = require('path')
7
+ const userRequire = require('@antora/user-require-helper')
11
8
 
12
- const DOT_RELATIVE_RX = new RegExp(`^\\.{1,2}[/${ospath.sep.replace('/', '').replace('\\', '\\\\')}]`)
13
9
  const { version: VERSION } = require('../package.json')
14
10
 
15
11
  async function run (argv = process.argv) {
@@ -17,112 +13,160 @@ async function run (argv = process.argv) {
17
13
  return cli.parseAsync(args.length ? args : ['help'], { from: 'user' })
18
14
  }
19
15
 
20
- function exitWithError (err, showStack, msg = undefined) {
16
+ function exitWithError (err, opts, msg = undefined) {
17
+ const { getLogger, configureLogger } = requireLogger()
21
18
  if (!msg) msg = err.message || err
22
- if (showStack) {
23
- let stack
19
+ const name = msg.startsWith('asciidoctor: FAILED: ') ? (msg = msg.slice(21)) && 'asciidoctor' : cli.name()
20
+ if (!getLogger(null)) {
21
+ configureLogger({ format: 'pretty', level: opts.silent ? 'silent' : 'fatal', failureLevel: 'fatal' })
22
+ }
23
+ const logger = getLogger(name)
24
+ if (opts.stacktrace) {
25
+ let loc, stack
24
26
  if ((stack = err.backtrace)) {
25
- msg = [`error: ${msg}`, ...stack.slice(1)].join('\n')
27
+ err = Object.assign(new Error(msg), { stack: ['Error', ...stack.slice(1)].join('\n') })
26
28
  } else if ((stack = err.stack)) {
27
- msg = stack.startsWith(`${err.name}: ${msg}\n`) ? stack : [msg, ...stack.split('\n').slice(1)].join('\n')
29
+ if (err instanceof SyntaxError && stack.includes('\nSyntaxError: ')) {
30
+ ;[loc, stack] = stack.split(/\n+SyntaxError: [^\n]+/)
31
+ err = Object.assign(new SyntaxError(msg), { stack: stack.replace('\n', `SyntaxError\n at ${loc}\n`) })
32
+ } else if (stack.startsWith(`${err.name}: ${msg}`)) {
33
+ stack = stack.replace(`${err.name}: ${msg}`, '').replace(/^\n/, '')
34
+ err = Object.assign(new err.constructor(msg), { stack: stack ? `${err.name}\n${stack}` : undefined })
35
+ }
28
36
  } else {
29
- msg = `error: ${msg} (no stack)`
37
+ err = Object.assign(new Error(msg), { stack: undefined })
30
38
  }
31
- console.error(msg)
39
+ if ({}.propertyIsEnumerable.call(err, 'name')) Object.defineProperty(err, 'name', { enumerable: false })
40
+ err.stack = `Cause: ${err.stack || '(no stacktrace)'}`
41
+ logger.fatal(err, msg)
32
42
  } else {
33
- console.error(`error: ${msg}\nAdd the --stacktrace option to see the cause.`)
43
+ logger.fatal(msg + '\nAdd the --stacktrace option to see the cause of the error.')
34
44
  }
35
- process.exit(1)
45
+ return exit()
46
+ }
47
+
48
+ function exit () {
49
+ return requireLogger()
50
+ .finalizeLogger()
51
+ .then((failOnExit) => process.exit(failOnExit ? 1 : process.exitCode))
36
52
  }
37
53
 
38
54
  function getTTYColumns () {
39
55
  return process.env.COLUMNS || process.stdout.columns || 80
40
56
  }
41
57
 
42
- function requireLibraries (requirePaths) {
43
- if (requirePaths) requirePaths.forEach((requirePath) => requireLibrary(requirePath))
58
+ function outputError (str, write) {
59
+ write(str.replace(/^error: /, cli.name() + ': '))
44
60
  }
45
61
 
46
- function requireLibrary (requirePath, cwd = process.cwd()) {
47
- if (requirePath.charAt() === '.' && DOT_RELATIVE_RX.test(requirePath)) {
48
- // NOTE require resolves a dot-relative path relative to current file; resolve relative to cwd instead
49
- requirePath = ospath.resolve(requirePath)
50
- } else if (!ospath.isAbsolute(requirePath)) {
51
- // NOTE appending node_modules prevents require from looking elsewhere before looking in these paths
52
- const paths = [cwd, ospath.dirname(__dirname)].map((start) => ospath.join(start, 'node_modules'))
53
- requirePath = require.resolve(requirePath, { paths })
54
- }
55
- return require(requirePath)
62
+ function requireLogger (fromPath = undefined, moduleName = '@antora/logger') {
63
+ try {
64
+ return (
65
+ requireLogger.cache ||
66
+ (requireLogger.cache = fromPath ? userRequire(moduleName, { paths: [fromPath] }) : require(moduleName))
67
+ )
68
+ } catch {}
69
+ return fromPath && (requireLogger.cache = require(moduleName))
56
70
  }
57
71
 
58
72
  cli
59
73
  .allowExcessArguments(false)
60
- .configureOutput({ getOutHelpWidth: getTTYColumns, getErrHelpWidth: getTTYColumns })
74
+ .configureOutput({ getOutHelpWidth: getTTYColumns, getErrHelpWidth: getTTYColumns, outputError })
61
75
  .storeOptionsAsProperties()
62
76
  .name('antora')
63
- .version(VERSION, '-v, --version', 'Output the version number.')
77
+ .version(
78
+ {
79
+ toString () {
80
+ const generator = cli._findCommand('generate').getOptionValue('generator')
81
+ const buffer = ['@antora/cli: ' + VERSION]
82
+ let generatorVersion
83
+ const generatorPackageJson = generator + '/package.json'
84
+ try {
85
+ generatorVersion = require(generatorPackageJson).version
86
+ } catch {
87
+ try {
88
+ generatorVersion = require(require.resolve(generatorPackageJson, { paths: [''] })).version
89
+ } catch {}
90
+ }
91
+ buffer.push(generator + ': ' + (generatorVersion || 'not installed'))
92
+ return buffer.join('\n')
93
+ },
94
+ },
95
+ '-v, --version',
96
+ 'Output the version of the CLI and default site generator.'
97
+ )
64
98
  .description('A modular, multi-repository documentation site generator for AsciiDoc.')
65
99
  .usage('[options] [[command] [args]]')
66
100
  .helpOption('-h, --help', 'Output usage information.')
67
- .addHelpText(
68
- 'after',
69
- function () {
70
- const name = this.name()
71
- return this.createHelp().wrap(
101
+ .addHelpText('after', () => {
102
+ const name = cli.name()
103
+ return cli
104
+ .createHelp()
105
+ .wrap(
72
106
  ` \nRun '${name} <command> --help' to see options and examples for a command (e.g., ${name} generate --help).`,
73
107
  getTTYColumns(),
74
108
  0
75
109
  )
76
- }.bind(cli)
77
- )
78
- .option('-r, --require <library>', 'Require library (aka node module) or script before executing command.')
79
- .on('option:require', (requirePath) => (cli.requirePaths = [...(cli.requirePaths || []), requirePath]))
110
+ })
111
+ .option('-r, --require <library>', 'Require library (aka node module) or script path before executing command.')
112
+ .on('option:require', (requireRequest) => (cli.requireRequests = cli.requireRequests || []).push(requireRequest))
80
113
  .option('--stacktrace', 'Print the stacktrace to the console if the application fails.')
81
114
 
82
115
  cli
83
116
  .command('generate <playbook>', { isDefault: true })
84
- .description('Generate a documentation site specified in <playbook>.')
85
- .optionsFromConvict(convict(configSchema), { exclude: 'playbook' })
86
- .addOption(
87
- cli
88
- .createOption('--generator <library>', 'The site generator library.')
89
- .default('@antora/site-generator-default', '@antora/site-generator-default')
90
- )
117
+ .description('Generate a documentation site as specified by <playbook>.')
118
+ .optionsFromConvict(convict(buildPlaybook.defaultSchema), { exclude: 'playbook' })
119
+ .trackOptions()
91
120
  .action(async (playbookFile, options, command) => {
121
+ const errorOpts = { stacktrace: cli.stacktrace, silent: command.silent }
122
+ const playbookDir = ospath.resolve(playbookFile, '..')
123
+ const userRequireContext = { dot: playbookDir, paths: [playbookDir, __dirname] }
124
+ if (cli.requireRequests) {
125
+ try {
126
+ cli.requireRequests.forEach((requireRequest) => userRequire(requireRequest, userRequireContext))
127
+ } catch (err) {
128
+ return exitWithError(err, errorOpts)
129
+ }
130
+ }
131
+ const args = command.optionArgs.concat('--playbook', playbookFile)
132
+ let generator, generatorPath, playbook
92
133
  try {
93
- requireLibraries(cli.requirePaths)
134
+ playbook = buildPlaybook(args, process.env, buildPlaybook.defaultSchema, (config) => {
135
+ try {
136
+ generatorPath = userRequire.resolve((generator = config.get('antora.generator')), userRequireContext)
137
+ } catch {}
138
+ try {
139
+ requireLogger(generatorPath).configureLogger(config.getModel('runtime.log'), playbookDir)
140
+ } catch {}
141
+ })
94
142
  } catch (err) {
95
- exitWithError(err, cli.stacktrace)
143
+ return exitWithError(err, errorOpts)
96
144
  }
97
- const generator = options.generator
98
145
  let generateSite
99
146
  try {
100
- generateSite = requireLibrary(generator, ospath.resolve(playbookFile, '..'))
147
+ generateSite =
148
+ (generateSite = require(generatorPath || userRequire.resolve(generator, userRequireContext))).length === 1
149
+ ? generateSite.bind(null, playbook)
150
+ : generateSite.bind(null, args, process.env)
101
151
  } catch (err) {
102
152
  let msg = 'Generator not found or failed to load.'
103
153
  if (generator && generator.charAt() !== '.') msg += ` Try installing the '${generator}' package.`
104
- exitWithError(err, cli.stacktrace, msg)
154
+ return exitWithError(err, errorOpts, msg)
105
155
  }
106
- const args = cli.rawArgs.slice(cli.rawArgs.indexOf(command.name()) + 1)
107
- args.splice(args.indexOf(playbookFile), 0, '--playbook')
108
- // TODO support passing a preloaded convict config as third option; gets new args and env
109
- return generateSite(args, process.env)
110
- .then(finalizeLogger)
111
- .then((failOnExit) => process.exit(failOnExit ? 1 : process.exitCode))
112
- .catch((err) => finalizeLogger().then(() => exitWithError(err, cli.stacktrace)))
156
+ return generateSite()
157
+ .then(exit)
158
+ .catch((err) => exitWithError(err, errorOpts))
113
159
  })
114
160
  .options.sort((a, b) => a.long.localeCompare(b.long))
115
161
 
116
162
  cli.command('help [command]', { hidden: true }).action((name, options, command) => {
117
163
  if (name) {
118
- const helpCommand = cli.commands.find((candidate) => candidate.name() === name)
164
+ const helpCommand = cli._findCommand(name)
119
165
  if (helpCommand) {
120
166
  helpCommand.help()
121
167
  } else {
122
- console.error(
123
- `'${name}' is not a valid command in ${cli.name()}. See '${cli.name()} --help' for a list of commands.`
124
- )
125
- process.exit(1)
168
+ const message = `error: unknown command '${name}'. See '${cli.name()} --help' for a list of commands.`
169
+ cli._displayError(1, 'commander.unknownCommand', message)
126
170
  }
127
171
  } else {
128
172
  cli.help()
@@ -32,7 +32,7 @@ function collectOptions (props, context = undefined) {
32
32
  const option = { name: arg, form: `--${arg}`, description: value.doc, format: format }
33
33
  if (Array.isArray(format)) {
34
34
  option.form += ' <choice>'
35
- option.choices = Object.defineProperty(format.slice(0), 'map', { value: () => format })
35
+ option.choices = Object.defineProperty(format.slice(), 'map', { value: () => format })
36
36
  } else if (format !== 'boolean') {
37
37
  option.form += ` <${arg.substr(arg.lastIndexOf('-') + 1, arg.length)}>`
38
38
  }
@@ -0,0 +1,14 @@
1
+ 'use strict'
2
+
3
+ const { Command } = require('commander')
4
+
5
+ Command.prototype.trackOptions = function () {
6
+ const optionArgs = (this.optionArgs = [])
7
+ for (const eventName of this.eventNames().filter((name) => name.startsWith('option:'))) {
8
+ this.on(eventName, function () {
9
+ optionArgs.push(`--${eventName.slice(7)}`)
10
+ if (arguments.length) optionArgs.push(arguments[0])
11
+ })
12
+ }
13
+ return this
14
+ }
package/lib/commander.js CHANGED
@@ -2,5 +2,6 @@
2
2
 
3
3
  const commander = require('commander')
4
4
  require('./commander/options-from-convict')
5
+ require('./commander/track-options')
5
6
 
6
7
  module.exports = commander
package/lib/index.js ADDED
@@ -0,0 +1,14 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * The command line interface (CLI) for Antora.
5
+ *
6
+ * Provides a built-in set of commands to run Antora. The default command is
7
+ * generate. The generate command builds the specified playbook, configures the
8
+ * logger, then requires and invokes the generator function. When the generator
9
+ * function completes or fails, the generate command finalizes the logger and
10
+ * exits with the specified exit code.
11
+ *
12
+ * @namespace cli
13
+ */
14
+ module.exports = require('./cli')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antora/cli",
3
- "version": "3.0.0-alpha.7",
3
+ "version": "3.0.0-beta.2",
4
4
  "description": "The command line interface for Antora.",
5
5
  "license": "MPL-2.0",
6
6
  "author": "OpenDevise Inc. (https://opendevise.com)",
@@ -13,22 +13,23 @@
13
13
  "bugs": {
14
14
  "url": "https://gitlab.com/antora/antora/issues"
15
15
  },
16
- "main": "lib/cli.js",
16
+ "main": "lib/index.js",
17
17
  "bin": {
18
18
  "antora": "bin/antora"
19
19
  },
20
20
  "dependencies": {
21
- "@antora/logger": "3.0.0-alpha.7",
22
- "@antora/playbook-builder": "3.0.0-alpha.7",
23
- "commander": "~7.2"
21
+ "@antora/logger": "3.0.0-beta.2",
22
+ "@antora/playbook-builder": "3.0.0-beta.2",
23
+ "@antora/user-require-helper": "~2.0",
24
+ "commander": "~8.3"
24
25
  },
25
26
  "devDependencies": {
26
- "@antora/site-publisher": "3.0.0-alpha.7",
27
- "convict": "~6.1",
27
+ "@antora/site-publisher": "3.0.0-beta.2",
28
+ "convict": "~6.2",
28
29
  "kapok-js": "~0.10"
29
30
  },
30
31
  "engines": {
31
- "node": ">=10.17.0"
32
+ "node": ">=12.21.0"
32
33
  },
33
34
  "files": [
34
35
  "bin/",
@@ -42,5 +43,5 @@
42
43
  "static site",
43
44
  "web publishing"
44
45
  ],
45
- "gitHead": "fbd597b3680474f2083cda8a7facf1e2848c08e0"
46
+ "gitHead": "5cd3f9cc70622e465cb44daf1aa2035ed5a35f54"
46
47
  }