@antora/site-generator-default 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
@@ -1,10 +1,10 @@
1
1
  # Antora Default Site Generator
2
2
 
3
- This is the default site generator pipeline for Antora.
4
- This pipeline is invoked by the `generate` command of Antora's CLI to produce and publish static documentation sites.
3
+ This is the default site generator for Antora.
4
+ This generator is invoked by the `generate` command of Antora's CLI to produce and publish static documentation sites.
5
5
 
6
6
  [Antora](https://antora.org) is a modular static site generator designed for creating documentation sites from AsciiDoc documents.
7
- Its site generator pipeline aggregates documents from versioned content repositories and processes them using [Asciidoctor](https://asciidoctor.org).
7
+ Its site generator aggregates documents from versioned content repositories and processes them using [Asciidoctor](https://asciidoctor.org).
8
8
 
9
9
  ## Copyright and License
10
10
 
@@ -1,40 +1,65 @@
1
1
  'use strict'
2
2
 
3
- const aggregateContent = require('@antora/content-aggregator')
4
- const buildNavigation = require('@antora/navigation-builder')
5
- const buildPlaybook = require('@antora/playbook-builder')
6
- const classifyContent = require('@antora/content-classifier')
7
- const convertDocuments = require('@antora/document-converter')
8
- const createPageComposer = require('@antora/page-composer')
9
- const loadUi = require('@antora/ui-loader')
10
- const mapSite = require('@antora/site-mapper')
11
- const produceRedirects = require('@antora/redirect-producer')
12
- const publishSite = require('@antora/site-publisher')
13
- const { resolveAsciiDocConfig } = require('@antora/asciidoc-loader')
3
+ const GeneratorContext = require('./generator-context')
4
+ const SiteCatalog = require('./site-catalog')
14
5
 
15
- async function generateSite (args, env) {
16
- const playbook = buildPlaybook(args, env)
17
- const asciidocConfig = resolveAsciiDocConfig(playbook)
18
- const [contentCatalog, uiCatalog] = await Promise.all([
19
- aggregateContent(playbook).then((contentAggregate) => classifyContent(playbook, contentAggregate, asciidocConfig)),
20
- loadUi(playbook),
21
- ])
22
- const pages = convertDocuments(contentCatalog, asciidocConfig)
23
- const navigationCatalog = buildNavigation(contentCatalog, asciidocConfig)
24
- const composePage = createPageComposer(playbook, contentCatalog, uiCatalog, env)
25
- pages.forEach((page) => composePage(page, contentCatalog, navigationCatalog))
26
- const siteFiles = [...mapSite(playbook, pages), ...produceRedirects(playbook, contentCatalog)]
27
- if (playbook.site.url) siteFiles.push(composePage(create404Page()))
28
- const siteCatalog = { getFiles: () => siteFiles }
29
- return publishSite(playbook, [contentCatalog, uiCatalog, siteCatalog]).then((publications) => {
30
- if (!playbook.runtime.quiet && process.stdout.isTTY) {
31
- process.stdout.write('Site generation complete!\n')
32
- publications.forEach(
33
- ({ fileUri }) => fileUri && process.stdout.write(`View the site by visiting ${fileUri} in a browser.\n`)
34
- )
6
+ async function generateSite (playbook) {
7
+ const context = new GeneratorContext(module)
8
+ try {
9
+ const { fxns, vars } = await GeneratorContext.start(context, playbook)
10
+ await context.notify('playbookBuilt')
11
+ playbook = vars.lock('playbook')
12
+ vars.asciidocConfig = fxns.resolveAsciiDocConfig(playbook)
13
+ vars.siteCatalog = new SiteCatalog()
14
+ await context.notify('beforeProcess')
15
+ const asciidocConfig = vars.lock('asciidocConfig')
16
+ await Promise.all([
17
+ fxns.aggregateContent(playbook).then((contentAggregate) =>
18
+ context.notify('contentAggregated', Object.assign(vars, { contentAggregate })).then(() => {
19
+ vars.contentCatalog = fxns.classifyContent(playbook, vars.remove('contentAggregate'), asciidocConfig)
20
+ })
21
+ ),
22
+ fxns.loadUi(playbook).then((uiCatalog) => context.notify('uiLoaded', Object.assign(vars, { uiCatalog }))),
23
+ ])
24
+ await context.notify('contentClassified')
25
+ const contentCatalog = vars.lock('contentCatalog')
26
+ const uiCatalog = vars.lock('uiCatalog')
27
+ fxns.convertDocuments(contentCatalog, asciidocConfig)
28
+ await context.notify('documentsConverted')
29
+ vars.navigationCatalog = fxns.buildNavigation(contentCatalog, asciidocConfig)
30
+ await context.notify('navigationBuilt')
31
+ ;(() => {
32
+ const navigationCatalog = vars.remove('navigationCatalog')
33
+ const composePage = fxns.createPageComposer(playbook, contentCatalog, uiCatalog, playbook.env)
34
+ contentCatalog.getPages((page) => page.out && composePage(page, contentCatalog, navigationCatalog))
35
+ if (playbook.site.url) vars.siteCatalog.addFile(composePage(create404Page()))
36
+ })()
37
+ await context.notify('pagesComposed')
38
+ vars.siteCatalog.addFiles(fxns.produceRedirects(playbook, contentCatalog))
39
+ await context.notify('redirectsProduced')
40
+ if (playbook.site.url) {
41
+ const publishablePages = contentCatalog.getPages((page) => page.out)
42
+ vars.siteCatalog.addFiles(fxns.mapSite(playbook, publishablePages))
43
+ await context.notify('siteMapped')
35
44
  }
36
- return publications
37
- })
45
+ await context.notify('beforePublish')
46
+ return fxns.publishSite(playbook, [contentCatalog, uiCatalog, vars.lock('siteCatalog')]).then((publications) => {
47
+ if (!playbook.runtime.quiet && process.stdout.isTTY) {
48
+ process.stdout.write('Site generation complete!\n')
49
+ publications.forEach(
50
+ ({ fileUri }) => fileUri && process.stdout.write(`View the site by visiting ${fileUri} in a browser.\n`)
51
+ )
52
+ }
53
+ return context
54
+ .notify('sitePublished', Object.assign(vars, { publications }))
55
+ .then(() => vars.remove('publications'))
56
+ })
57
+ } catch (err) {
58
+ if (!GeneratorContext.isStopSignal(err)) throw err
59
+ await err.notify()
60
+ } finally {
61
+ await GeneratorContext.close(context)
62
+ }
38
63
  }
39
64
 
40
65
  function create404Page () {
@@ -0,0 +1,166 @@
1
+ 'use strict'
2
+
3
+ const EventEmitter = require('events')
4
+ const getLogger = require('@antora/logger')
5
+ const userRequire = require('@antora/user-require-helper')
6
+
7
+ const FUNCTION_PROVIDERS = {
8
+ aggregateContent: 'content-aggregator',
9
+ buildNavigation: 'navigation-builder',
10
+ classifyContent: 'content-classifier',
11
+ convertDocument: 'document-converter',
12
+ convertDocuments: 'document-converter',
13
+ createPageComposer: 'page-composer',
14
+ extractAsciiDocMetadata: 'asciidoc-loader',
15
+ loadAsciiDoc: 'asciidoc-loader',
16
+ loadUi: 'ui-loader',
17
+ mapSite: 'site-mapper',
18
+ produceRedirects: 'redirect-producer',
19
+ publishSite: 'site-publisher',
20
+ resolveAsciiDocConfig: 'asciidoc-loader',
21
+ }
22
+
23
+ const FUNCTION_WITH_POSITIONAL_PARAMETER_RX = /^(?:function *)?(?:\w+ *)?\( *\w|^\w+(?: *, *\w+)* *=>/
24
+ const NEWLINES_RX = /\r?\n/g
25
+
26
+ class StopSignal extends Error {}
27
+
28
+ class GeneratorContext extends EventEmitter {
29
+ #fxns
30
+ #vars
31
+
32
+ constructor (module_) {
33
+ super()
34
+ // deprecated method aliases - remove for Antora 3.0.0
35
+ Object.defineProperties(this, { halt: { value: this.stop }, updateVars: { value: this.updateVariables } })
36
+ if (!('path' in (this.module = module_))) module_.path = require('path').dirname(module_.filename)
37
+ }
38
+
39
+ getFunctions () {
40
+ return arguments.length ? this.#fxns : Object.assign({}, this.#fxns)
41
+ }
42
+
43
+ getLogger (name = 'antora') {
44
+ return getLogger(name)
45
+ }
46
+
47
+ getVariables () {
48
+ return Object.assign({}, this.#vars)
49
+ }
50
+
51
+ async notify (eventName) {
52
+ if (!this.listenerCount(eventName)) return
53
+ for (const listener of this.rawListeners(eventName)) {
54
+ const outcome = listener.length === 1 ? listener.call(this, this.getVariables()) : listener.call(this)
55
+ if (outcome instanceof Promise) await outcome
56
+ }
57
+ }
58
+
59
+ replaceFunctions (updates) {
60
+ const fxns = this.#fxns
61
+ Object.entries(updates).forEach(([name, fxn]) => {
62
+ if (name in fxns) fxns[name] = fxn.bind(this)
63
+ })
64
+ }
65
+
66
+ require (request) {
67
+ return this.module.require(request)
68
+ }
69
+
70
+ stop (code) {
71
+ if (code != null) process.exitCode = code
72
+ throw Object.assign(new StopSignal(), { notify: this.notify.bind(this, 'contextStopped') })
73
+ }
74
+
75
+ updateVariables (updates) {
76
+ try {
77
+ Object.assign(this.#vars, updates)
78
+ } catch (err) {
79
+ if (err instanceof TypeError) {
80
+ err.message = err.message.replace(/ assign to read.only property '(.+)' .*/, " update read-only var '$1'")
81
+ }
82
+ throw err
83
+ }
84
+ }
85
+
86
+ static async close (instance) {
87
+ await instance.notify('contextClosed').catch(() => undefined)
88
+ }
89
+
90
+ static isStopSignal (err) {
91
+ return err instanceof StopSignal
92
+ }
93
+
94
+ static async start (instance, playbook) {
95
+ const returnValue = instance._init(playbook)
96
+ await instance.notify('contextStarted')
97
+ return returnValue
98
+ }
99
+
100
+ _init (playbook) {
101
+ this._registerFunctions()
102
+ this._registerExtensions(playbook, this._initVariables(playbook))
103
+ Object.defineProperties(this, { _init: {}, _initVariables: {}, _registerExtensions: {}, _registerFunctions: {} })
104
+ return { fxns: this.#fxns, vars: this.#vars }
105
+ }
106
+
107
+ _initVariables (playbook) {
108
+ return (this.#vars = Object.setPrototypeOf(
109
+ { playbook },
110
+ {
111
+ lock (name) {
112
+ return Object.defineProperty(this, name, { configurable: false, writable: false })[name]
113
+ },
114
+ remove (name) {
115
+ const currentValue = this[name]
116
+ delete this[name]
117
+ return currentValue
118
+ },
119
+ }
120
+ ))
121
+ }
122
+
123
+ _registerExtensions (playbook, vars) {
124
+ const extensions = (playbook.antora || {}).extensions || []
125
+ if (extensions.length) {
126
+ const requireContext = { dot: playbook.dir, paths: [playbook.dir || '', this.module.path] }
127
+ extensions.forEach((ext) => {
128
+ const { enabled = true, id, require: request, ...config } = ext.constructor === String ? { require: ext } : ext
129
+ if (!enabled) return
130
+ const { register } = userRequire(request, requireContext)
131
+ if (typeof register !== 'function') return
132
+ if (register.length) {
133
+ if (FUNCTION_WITH_POSITIONAL_PARAMETER_RX.test(register.toString().replace(NEWLINES_RX, ' '))) {
134
+ register.length === 1 ? register(this) : register(this, Object.assign({ config }, vars))
135
+ } else {
136
+ register.call(this, Object.assign({ config }, vars))
137
+ }
138
+ } else {
139
+ register.call(this)
140
+ }
141
+ })
142
+ }
143
+ if (this.eventNames().length) return
144
+ const notify = async () => undefined
145
+ Object.defineProperty(this, 'notify', { value: notify })
146
+ }
147
+
148
+ _registerFunctions () {
149
+ this.#fxns = Object.entries(
150
+ Object.entries(FUNCTION_PROVIDERS).reduce((accum, [fxnName, moduleKey]) => {
151
+ accum[moduleKey] = (accum[moduleKey] || []).concat(fxnName)
152
+ return accum
153
+ }, {})
154
+ ).reduce((accum, [moduleKey, fxnNames]) => {
155
+ const defaultExport = this.require('@antora/' + moduleKey)
156
+ const defaultExportName = defaultExport.name
157
+ fxnNames.forEach((fxnName) => {
158
+ const fxn = fxnName === defaultExportName ? defaultExport : defaultExport[fxnName]
159
+ accum[fxnName] = fxn.bind(this)
160
+ })
161
+ return accum
162
+ }, {})
163
+ }
164
+ }
165
+
166
+ module.exports = GeneratorContext
package/lib/index.js CHANGED
@@ -3,10 +3,9 @@
3
3
  /**
4
4
  * Default Site Generator component for Antora
5
5
  *
6
- * Runs an Antora pipeline using the default set of components with a focus on
7
- * producing and publishing a documentation site. This component represents
8
- * just one way the Antora components can be organized into a site generation
9
- * pipeline.
6
+ * Coordinates a default set of software components to produce and publish a
7
+ * documentation site. This component represents just one way the Antora
8
+ * components can be organized to make a documentation generator.
10
9
  *
11
10
  * @namespace site-generator-default
12
11
  */
@@ -0,0 +1,28 @@
1
+ 'use strict'
2
+
3
+ const $files = Symbol('files')
4
+
5
+ class SiteCatalog {
6
+ constructor () {
7
+ this[$files] = []
8
+ }
9
+
10
+ addFile (file) {
11
+ this[$files].push(file)
12
+ }
13
+
14
+ addFiles (files) {
15
+ this[$files].push(...files)
16
+ }
17
+
18
+ getFiles () {
19
+ return this[$files].slice()
20
+ }
21
+ }
22
+
23
+ /**
24
+ * @deprecated superceded by getFiles(); scheduled to be removed in Antora 4
25
+ */
26
+ SiteCatalog.prototype.getAll = SiteCatalog.prototype.getFiles
27
+
28
+ module.exports = SiteCatalog
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@antora/site-generator-default",
3
- "version": "3.0.0-alpha.7",
4
- "description": "The default site generator pipeline for producing and publishing static documentation sites with Antora.",
3
+ "version": "3.0.0-beta.2",
4
+ "description": "The default site generator for producing and publishing static documentation sites with Antora.",
5
5
  "license": "MPL-2.0",
6
6
  "author": "OpenDevise Inc. (https://opendevise.com)",
7
7
  "contributors": [
@@ -15,20 +15,24 @@
15
15
  },
16
16
  "main": "lib/index.js",
17
17
  "dependencies": {
18
- "@antora/asciidoc-loader": "3.0.0-alpha.7",
19
- "@antora/content-aggregator": "3.0.0-alpha.7",
20
- "@antora/content-classifier": "3.0.0-alpha.7",
21
- "@antora/document-converter": "3.0.0-alpha.7",
22
- "@antora/navigation-builder": "3.0.0-alpha.7",
23
- "@antora/page-composer": "3.0.0-alpha.7",
24
- "@antora/playbook-builder": "3.0.0-alpha.7",
25
- "@antora/redirect-producer": "3.0.0-alpha.7",
26
- "@antora/site-mapper": "3.0.0-alpha.7",
27
- "@antora/site-publisher": "3.0.0-alpha.7",
28
- "@antora/ui-loader": "3.0.0-alpha.7"
18
+ "@antora/asciidoc-loader": "3.0.0-beta.2",
19
+ "@antora/content-aggregator": "3.0.0-beta.2",
20
+ "@antora/content-classifier": "3.0.0-beta.2",
21
+ "@antora/document-converter": "3.0.0-beta.2",
22
+ "@antora/logger": "3.0.0-beta.2",
23
+ "@antora/navigation-builder": "3.0.0-beta.2",
24
+ "@antora/page-composer": "3.0.0-beta.2",
25
+ "@antora/redirect-producer": "3.0.0-beta.2",
26
+ "@antora/site-mapper": "3.0.0-beta.2",
27
+ "@antora/site-publisher": "3.0.0-beta.2",
28
+ "@antora/ui-loader": "3.0.0-beta.2",
29
+ "@antora/user-require-helper": "~2.0"
30
+ },
31
+ "devDependencies": {
32
+ "@antora/playbook-builder": "3.0.0-beta.2"
29
33
  },
30
34
  "engines": {
31
- "node": ">=10.17.0"
35
+ "node": ">=12.21.0"
32
36
  },
33
37
  "files": [
34
38
  "lib/"
@@ -42,5 +46,5 @@
42
46
  "static site",
43
47
  "web publishing"
44
48
  ],
45
- "gitHead": "fbd597b3680474f2083cda8a7facf1e2848c08e0"
49
+ "gitHead": "5cd3f9cc70622e465cb44daf1aa2035ed5a35f54"
46
50
  }