@redpanda-data/docs-extensions-and-macros 2.5.1 → 3.0.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.adoc +38 -69
- package/extensions/add-global-attributes.js +36 -67
- package/extensions/aggregate-terms.js +59 -0
- package/extensions/replace-attributes-in-attachments.js +6 -10
- package/extensions/version-fetcher/set-latest-version.js +8 -1
- package/macros/glossary.js +52 -75
- package/package.json +20 -4
package/README.adoc
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
= Antora Extensions and Macros for Redpanda Docs
|
|
2
|
-
:url-
|
|
2
|
+
:url-org: https://github.com/redpanda-data
|
|
3
|
+
:url-project: {url-org}/docs-extensions-and-macros
|
|
4
|
+
:url-playbook: {url-org}/docs-site
|
|
3
5
|
:url-git: https://git-scm.com
|
|
4
6
|
:url-git-dl: {url-git}/downloads
|
|
5
7
|
:url-nodejs: https://nodejs.org
|
|
@@ -33,7 +35,7 @@ When you have Node.js installed, use the following command to install the `antor
|
|
|
33
35
|
|
|
34
36
|
[,bash]
|
|
35
37
|
----
|
|
36
|
-
npm i
|
|
38
|
+
npm i @redpanda-data/docs-extensions-and-macros
|
|
37
39
|
----
|
|
38
40
|
|
|
39
41
|
To use the development version instead, refer to the <<development-quickstart,Development Quickstart>>.
|
|
@@ -70,7 +72,7 @@ Whether to index all versions or just the latest version of a component.
|
|
|
70
72
|
```yaml
|
|
71
73
|
antora:
|
|
72
74
|
extensions:
|
|
73
|
-
- require: '
|
|
75
|
+
- require: '@redpanda-data/docs-extensions-and-macros/extensions/algolia-indexer/index'
|
|
74
76
|
excludes: ['.thumbs','script', '.page-versions','.feedback-section','.banner-container']
|
|
75
77
|
index-latest-only: true
|
|
76
78
|
```
|
|
@@ -92,48 +94,19 @@ NOTE: If you don't set the environment variable, the latest versions may not be
|
|
|
92
94
|
```yaml
|
|
93
95
|
antora:
|
|
94
96
|
extensions:
|
|
95
|
-
- '
|
|
97
|
+
- '@redpanda-data/docs-extensions-and-macros/extensions/version-fetcher/set-latest-version'
|
|
96
98
|
```
|
|
97
99
|
|
|
98
100
|
=== Global attributes
|
|
99
101
|
|
|
100
|
-
This extension
|
|
101
|
-
|
|
102
|
-
[IMPORTANT]
|
|
103
|
-
====
|
|
104
|
-
- The GitHub directory that contains the global attributes must be named `global-attributes`.
|
|
105
|
-
- Only YAML files are supported. Other types of files are ignored.
|
|
106
|
-
- If a key is present in both the global attributes and the playbook's `asciidoc.attributes`, the value in the playbook takes precedence.
|
|
107
|
-
====
|
|
108
|
-
|
|
109
|
-
==== Environment variables
|
|
110
|
-
|
|
111
|
-
- `GLOBAL_ATTRIBUTES_GITHUB_TOKEN` (optional): A Personal access token (PAT) that has `repo` permissions for the GitHub organization defined in `org`.
|
|
112
|
-
|
|
113
|
-
NOTE: If you don't set the environment variable, global attributes may not be made available to your build. When the environment variable is not set, the extension sends unauthenticated requests to GitHub. Unauthenticated requests may result in hitting the API rate limit and cause GitHub to reject the request.
|
|
114
|
-
|
|
115
|
-
==== Configuration options
|
|
116
|
-
|
|
117
|
-
The extension accepts the following configuration options:
|
|
118
|
-
|
|
119
|
-
org (required)::
|
|
120
|
-
The GitHub organization that owns the repository.
|
|
121
|
-
|
|
122
|
-
repo (required)::
|
|
123
|
-
The name of the repository.
|
|
124
|
-
|
|
125
|
-
branch (required)::
|
|
126
|
-
The branch in the repository where the global attributes are located.
|
|
102
|
+
This extension collects Asciidoc attributes from the {url-playbook}[`shared` component] and makes them available to all component versions. Having global attributes is useful for consistent configuration of local and production builds.
|
|
127
103
|
|
|
128
104
|
==== Registration example
|
|
129
105
|
|
|
130
106
|
```yaml
|
|
131
107
|
antora:
|
|
132
108
|
extensions:
|
|
133
|
-
- require: '
|
|
134
|
-
org: example
|
|
135
|
-
repo: test
|
|
136
|
-
branch: main
|
|
109
|
+
- require: '@redpanda-data/docs-extensions-and-macros/extensions/add-global-attributes'
|
|
137
110
|
```
|
|
138
111
|
|
|
139
112
|
=== Replace attributes in attachments
|
|
@@ -142,7 +115,7 @@ This extension replaces AsciiDoc attribute placeholders with their respective va
|
|
|
142
115
|
|
|
143
116
|
[IMPORTANT]
|
|
144
117
|
====
|
|
145
|
-
-
|
|
118
|
+
- This extension processes attachments only if the component version includes the attribute `replace-attributes-in-attachments: true`.
|
|
146
119
|
- The `@` character is removed from attribute values to prevent potential issues with CSS or HTML syntax.
|
|
147
120
|
- If the same attribute placeholder is used multiple times within a file, all instances will be replaced with the attribute's value.
|
|
148
121
|
====
|
|
@@ -152,7 +125,23 @@ This extension replaces AsciiDoc attribute placeholders with their respective va
|
|
|
152
125
|
```yaml
|
|
153
126
|
antora:
|
|
154
127
|
extensions:
|
|
155
|
-
- '
|
|
128
|
+
- '@redpanda-data/docs-extensions-and-macros/extensions/replace-attributes-in-attachments'
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
=== Aggregate terms
|
|
132
|
+
|
|
133
|
+
This extension aggregates all term pages from the {url-playbook}[`shared` component] and does the following:
|
|
134
|
+
|
|
135
|
+
- Makes all `term-name` and `hover-text` attributes available to the <<glossterm-macro,`glossterm` macro>>.
|
|
136
|
+
- Looks for glossary pages named `reference:glossary.adoc` in all versions of all components and appends the contents of each term file to the glossary in alphabetical order.
|
|
137
|
+
- If a glossary page is found, sets the `glossary-page` attribute of the <<glossterm, `glossterm` macro>> to `reference:glossary.adoc`.
|
|
138
|
+
|
|
139
|
+
==== Registration example
|
|
140
|
+
|
|
141
|
+
```yaml
|
|
142
|
+
antora:
|
|
143
|
+
extensions:
|
|
144
|
+
- '@redpanda-data/docs-extensions-and-macros/extensions/aggregate-terms'
|
|
156
145
|
```
|
|
157
146
|
|
|
158
147
|
=== Unlisted pages
|
|
@@ -176,7 +165,7 @@ The heading under which to list the unlisted pages in the navigation. The defaul
|
|
|
176
165
|
```yaml
|
|
177
166
|
antora:
|
|
178
167
|
extensions:
|
|
179
|
-
- require: '
|
|
168
|
+
- require: '@redpanda-data/docs-extensions-and-macros/extensions/unlisted-pages'
|
|
180
169
|
addToNavigation: true
|
|
181
170
|
unlistedPagesHeading: 'Additional Resources'
|
|
182
171
|
```
|
|
@@ -228,33 +217,22 @@ config_ref:example_config,true,tunable-properties[]
|
|
|
228
217
|
----
|
|
229
218
|
asciidoc:
|
|
230
219
|
extensions:
|
|
231
|
-
- '
|
|
220
|
+
- '@redpanda-data/docs-extensions-and-macros/macros/config-ref'
|
|
232
221
|
----
|
|
233
222
|
|
|
234
|
-
===
|
|
235
|
-
|
|
236
|
-
The glossary module provides a way to define and reference glossary terms in your AsciiDoc documents.
|
|
223
|
+
=== glossterm
|
|
237
224
|
|
|
238
|
-
|
|
225
|
+
The `glossterm` inline macro provides a way to define and reference glossary terms in your AsciiDoc documents.
|
|
239
226
|
|
|
240
|
-
NOTE: This macro is a customized version of https://gitlab.com/djencks/asciidoctor-glossary[`asciidoctor-glossary`].
|
|
227
|
+
NOTE: This macro is a customized version of https://gitlab.com/djencks/asciidoctor-glossary[`asciidoctor-glossary`].
|
|
241
228
|
|
|
242
229
|
==== Usage
|
|
243
230
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
[,asciidoc]
|
|
247
|
-
----
|
|
248
|
-
glossary::[]
|
|
249
|
-
----
|
|
250
|
-
|
|
251
|
-
Glossary terms defined in the `glossterm` inline macro before the `glossary` macro is used appear as a definition list, sorted by term.
|
|
252
|
-
|
|
253
|
-
The `glossterm` inline macro is used to reference a term within the text of the document:
|
|
231
|
+
Use the `glossterm` inline macro to reference a term within the text of the document:
|
|
254
232
|
|
|
255
233
|
[,asciidoc]
|
|
256
234
|
----
|
|
257
|
-
glossterm:
|
|
235
|
+
glossterm:my term[myDefinition]
|
|
258
236
|
----
|
|
259
237
|
|
|
260
238
|
It takes two parameters:
|
|
@@ -263,7 +241,7 @@ term::
|
|
|
263
241
|
The term to be defined.
|
|
264
242
|
|
|
265
243
|
definition (optional)::
|
|
266
|
-
The definition of the term. If the term is defined in the
|
|
244
|
+
The definition of the term. If the term is defined in the {url-playbook}[`shared` component] or the `local-terms` object of the `antora.yml` file, you can omit the definition as it will always be replaced by those definitions.
|
|
267
245
|
|
|
268
246
|
==== Configuration options
|
|
269
247
|
|
|
@@ -290,22 +268,13 @@ Whether to enable tooltips for the defined terms. Valid values are:
|
|
|
290
268
|
|
|
291
269
|
The last two options are intended to support js/css tooltip solutions such as tippy.js.
|
|
292
270
|
|
|
293
|
-
[IMPORTANT]
|
|
294
|
-
.Multi-page use
|
|
295
|
-
====
|
|
296
|
-
In Antora, a glossary is constructed for each component-version.
|
|
297
|
-
When the `glossary` block macro is evaluated, only terms known as of the rendering can be included.
|
|
298
|
-
Therefore, it is necessary that the page containing this macro in a component-version be rendered last.
|
|
299
|
-
It may be possible to arrange this by naming the page starting with a lot of 'z’s, such as `zzzzzz-glossary.adoc`.
|
|
300
|
-
====
|
|
301
|
-
|
|
302
271
|
==== Registration example
|
|
303
272
|
|
|
304
273
|
[,yaml]
|
|
305
274
|
----
|
|
306
275
|
asciidoc:
|
|
307
276
|
extensions:
|
|
308
|
-
- '
|
|
277
|
+
- '@redpanda-data/docs-extensions-and-macros/macros/glossary'
|
|
309
278
|
----
|
|
310
279
|
|
|
311
280
|
=== helm_ref
|
|
@@ -347,7 +316,7 @@ If you do not specify a Helm reference value, the macro generates a link without
|
|
|
347
316
|
----
|
|
348
317
|
asciidoc:
|
|
349
318
|
extensions:
|
|
350
|
-
- '
|
|
319
|
+
- '@redpanda-data/docs-extensions-and-macros/macros/helm-ref'
|
|
351
320
|
----
|
|
352
321
|
|
|
353
322
|
== Development quickstart
|
|
@@ -418,5 +387,5 @@ If you want to use the project locally before it is published, you can specify t
|
|
|
418
387
|
asciidoc:
|
|
419
388
|
attributes:
|
|
420
389
|
extensions:
|
|
421
|
-
- '<path-to-local-project>/
|
|
422
|
-
----
|
|
390
|
+
- '<path-to-local-project>/docs-extensions-and-macros/extensions/<extension-name>'
|
|
391
|
+
----
|
|
@@ -2,76 +2,45 @@
|
|
|
2
2
|
* antora:
|
|
3
3
|
extensions:
|
|
4
4
|
* - require: ./extensions/add-global-attributes.js
|
|
5
|
-
org: example
|
|
6
|
-
repo: test
|
|
7
|
-
branch: main
|
|
8
5
|
*/
|
|
9
6
|
|
|
10
|
-
const yaml = require('js-yaml');
|
|
11
|
-
const { Octokit } = require("@octokit/rest");
|
|
12
|
-
const { retry } = require("@octokit/plugin-retry");
|
|
13
|
-
const OctokitWithRetries = Octokit.plugin(retry);
|
|
14
|
-
const chalk = require('chalk')
|
|
15
|
-
|
|
16
|
-
|
|
17
7
|
module.exports.register = function ({ config }) {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
this
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const github = new OctokitWithRetries(githubOptions);
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
// Request the contents of the directory from the GitHub API
|
|
40
|
-
const response = await github.request('GET ' + globalAttributesUrl);
|
|
41
|
-
|
|
42
|
-
if (response.status === 200) {
|
|
43
|
-
const directoryContents = response.data;
|
|
44
|
-
|
|
45
|
-
// Filter out only YAML files
|
|
46
|
-
const yamlFiles = directoryContents.filter(file => file.name.endsWith('.yaml') || file.name.endsWith('.yml'));
|
|
47
|
-
|
|
48
|
-
let globalAttributes = {};
|
|
49
|
-
for (let file of yamlFiles) {
|
|
50
|
-
const fileResponse = await github.request('GET ' + file.download_url);
|
|
51
|
-
const fileData = yaml.load(fileResponse.data);
|
|
52
|
-
globalAttributes = {...globalAttributes, ...fileData};
|
|
8
|
+
const yaml = require('js-yaml');
|
|
9
|
+
const _ = require('lodash');
|
|
10
|
+
const logger = this.getLogger('global-attributes-extension');
|
|
11
|
+
const chalk = require('chalk')
|
|
12
|
+
|
|
13
|
+
this.on('contentAggregated', ({ siteCatalog, contentAggregate }) => {
|
|
14
|
+
let attributeFile;
|
|
15
|
+
try {
|
|
16
|
+
for (const component of contentAggregate) {
|
|
17
|
+
if (component.name === 'shared') {
|
|
18
|
+
attributeFile = component.files.find(file => file.path.includes('attributes.yml'));
|
|
19
|
+
if (!attributeFile) {
|
|
20
|
+
logger.warn("No attributes.yml file found in 'shared' component.");
|
|
21
|
+
} else {
|
|
22
|
+
siteCatalog.attributeFile = yaml.load(attributeFile.contents.toString('utf8'));
|
|
23
|
+
console.log(chalk.green('Loaded attributes from shared component.'));
|
|
53
24
|
}
|
|
54
|
-
|
|
55
|
-
let mergedAttributes = {
|
|
56
|
-
...globalAttributes,
|
|
57
|
-
...playbook.asciidoc.attributes
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
playbook.asciidoc.attributes = mergedAttributes;
|
|
61
|
-
|
|
62
|
-
console.log(chalk.green('Merged global attributes into playbook'));
|
|
63
|
-
|
|
64
|
-
} else {
|
|
65
|
-
logger.warn(`Could not fetch global attributes: ${response.statusText}`);
|
|
66
|
-
return null
|
|
67
|
-
}
|
|
68
|
-
} catch(error) {
|
|
69
|
-
if (error.response.url) {
|
|
70
|
-
logger.warn(error.status + ' Could not get ' + error.response.url)
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
logger.warn(error)
|
|
25
|
+
break
|
|
74
26
|
}
|
|
75
27
|
}
|
|
76
|
-
})
|
|
77
|
-
}
|
|
28
|
+
} catch (error) {
|
|
29
|
+
logger.error(`Error loading attributes: ${error.message}`);
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
.on('contentClassified', ({ siteCatalog, contentCatalog }) => {
|
|
34
|
+
const components = contentCatalog.getComponents()
|
|
35
|
+
try {
|
|
36
|
+
components.forEach(({versions}) => {
|
|
37
|
+
versions.forEach(({asciidoc}) => {
|
|
38
|
+
asciidoc.attributes = _.merge(asciidoc.attributes, siteCatalog.attributeFile);
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
console.log(chalk.green('Merged global attributes into each component version.'));
|
|
42
|
+
} catch (error) {
|
|
43
|
+
logger.error(`Error merging attributes: ${error.message}`);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/* Example use in the playbook
|
|
2
|
+
* antora:
|
|
3
|
+
extensions:
|
|
4
|
+
* - require: ./extensions/aggregate-terms.js
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
module.exports.register = function ({ config }) {
|
|
8
|
+
const logger = this.getLogger('term-aggregation-extension');
|
|
9
|
+
const chalk = require('chalk')
|
|
10
|
+
|
|
11
|
+
this.on('contentAggregated', ({ siteCatalog, contentAggregate }) => {
|
|
12
|
+
try {
|
|
13
|
+
for (const component of contentAggregate) {
|
|
14
|
+
if (component.name === 'shared') {
|
|
15
|
+
const termFiles = component.files.filter(file => file.path.includes('modules/terms/partials/'));
|
|
16
|
+
siteCatalog.terms = {}
|
|
17
|
+
termFiles.forEach(file => {
|
|
18
|
+
const termContent = file.contents.toString('utf8');
|
|
19
|
+
siteCatalog.terms[file.basename] = termContent;
|
|
20
|
+
});
|
|
21
|
+
console.log(chalk.green('Loaded terms from shared component.'));
|
|
22
|
+
break
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
} catch (error) {
|
|
26
|
+
logger.error(`Error loading terms: ${error.message}`);
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
.on('contentClassified', ({ siteCatalog, contentCatalog }) => {
|
|
31
|
+
const components = contentCatalog.getComponents()
|
|
32
|
+
try {
|
|
33
|
+
for (const { versions } of components) {
|
|
34
|
+
for (const { name: component, version, asciidoc, title } of versions) {
|
|
35
|
+
if (component == 'shared') continue;
|
|
36
|
+
const glossaryPage = contentCatalog.resolvePage(`${version?version +'@':''}${component}:reference:glossary.adoc`);
|
|
37
|
+
if (glossaryPage) {
|
|
38
|
+
asciidoc.attributes['glossary-page'] = 'reference:glossary.adoc'
|
|
39
|
+
const glossaryContent = glossaryPage.contents.toString('utf8');
|
|
40
|
+
let newContent = glossaryContent;
|
|
41
|
+
const sortedTerms = Object.keys(siteCatalog.terms).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
42
|
+
for (i = 0; i < sortedTerms.length; i++) {
|
|
43
|
+
const termName = sortedTerms[i];
|
|
44
|
+
const termContent = siteCatalog.terms[termName];
|
|
45
|
+
newContent += '\n\n' + termContent;
|
|
46
|
+
}
|
|
47
|
+
glossaryPage.contents = Buffer.from(newContent, 'utf8');
|
|
48
|
+
|
|
49
|
+
console.log(chalk.green(`Transposed terms into glossary for ${component}.`));
|
|
50
|
+
} else {
|
|
51
|
+
logger.warn(`Skipping ${title} ${version} - No glossary page (reference:glossary.adoc) found`)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
logger.error(`Error transposing terms: ${error.message}`);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -4,21 +4,17 @@ module.exports.register = function ({ config }) {
|
|
|
4
4
|
|
|
5
5
|
const sanitizeAttributeValue = (value) => String(value).replace("@", "");
|
|
6
6
|
|
|
7
|
-
this.on('documentsConverted', ({
|
|
7
|
+
this.on('documentsConverted', ({contentCatalog}) => {
|
|
8
8
|
for (const { versions } of contentCatalog.getComponents()) {
|
|
9
|
-
for (const { name: component, version } of versions) {
|
|
10
|
-
if (component !== 'ROOT') continue;
|
|
9
|
+
for (const { name: component, version, asciidoc } of versions) {
|
|
11
10
|
const attachments = contentCatalog.findBy({ component, version, family });
|
|
12
11
|
for (const attachment of attachments) {
|
|
13
12
|
let contentString = String.fromCharCode(...attachment['_contents']);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
...attributes
|
|
18
|
-
};
|
|
19
|
-
for (const key in mergedAttributes) {
|
|
13
|
+
if (!asciidoc.attributes) continue
|
|
14
|
+
if (!asciidoc.attributes.hasOwnProperty('replace-attributes-in-attachments')) continue;
|
|
15
|
+
for (const key in asciidoc.attributes) {
|
|
20
16
|
const placeholder = "{" + key + "}";
|
|
21
|
-
const sanitizedValue = sanitizeAttributeValue(
|
|
17
|
+
const sanitizedValue = sanitizeAttributeValue(asciidoc.attributes[key]);
|
|
22
18
|
contentString = contentString.replace(new RegExp(placeholder, 'g'), sanitizedValue);
|
|
23
19
|
}
|
|
24
20
|
attachment['_contents'] = Buffer.from(contentString, "utf-8");
|
|
@@ -18,6 +18,10 @@ module.exports.register = function ({ config }) {
|
|
|
18
18
|
.on('playbookBuilt', async ({ playbook }) => {
|
|
19
19
|
try {
|
|
20
20
|
const LatestConsoleVersion = await GetLatestConsoleVersion();
|
|
21
|
+
if (!LatestConsoleVersion) {
|
|
22
|
+
logger.warn(`Failed to get latest Console version from GitHub`)
|
|
23
|
+
return
|
|
24
|
+
}
|
|
21
25
|
if (!playbook.asciidoc) {
|
|
22
26
|
playbook.asciidoc = {};
|
|
23
27
|
}
|
|
@@ -37,7 +41,10 @@ module.exports.register = function ({ config }) {
|
|
|
37
41
|
const components = await contentCatalog.getComponents();
|
|
38
42
|
for (let i = 0; i < components.length; i++) {
|
|
39
43
|
let component = components[i];
|
|
40
|
-
if (LatestRedpandaVersion.length !== 2
|
|
44
|
+
if (LatestRedpandaVersion.length !== 2 || !LatestRedpandaVersion[0]) {
|
|
45
|
+
logger.warn('Failed to get the latest Redpanda versions - using defaults');
|
|
46
|
+
return
|
|
47
|
+
}
|
|
41
48
|
if (component.name === 'ROOT') {
|
|
42
49
|
|
|
43
50
|
if (!component.latest.asciidoc) {
|
package/macros/glossary.js
CHANGED
|
@@ -12,16 +12,47 @@ module.exports.register = function (registry, config = {}) {
|
|
|
12
12
|
function getKey (src) {
|
|
13
13
|
return `${src.version}@${src.component}`
|
|
14
14
|
}
|
|
15
|
-
|
|
16
15
|
const contentCatalog = config.contentCatalog
|
|
17
16
|
if (!contentCatalog[$glossaryContexts]) contentCatalog[$glossaryContexts] = {}
|
|
18
17
|
const glossaryContexts = contentCatalog[$glossaryContexts]
|
|
18
|
+
// Check if the terms have been cached
|
|
19
|
+
const sharedKey = 'sharedTerms'
|
|
20
|
+
if (!glossaryContexts[sharedKey]) {
|
|
21
|
+
// Get the term files from the 'shared' component
|
|
22
|
+
const termFiles = contentCatalog.findBy({ component: 'shared', module: 'terms', family: 'partial' })
|
|
23
|
+
// Extract the term definitions from the files
|
|
24
|
+
const ATTRIBUTE_REGEX = /^:([a-zA-Z0-9_-]+):[ \t]*(.*)$/gm
|
|
25
|
+
|
|
26
|
+
const terms = termFiles.map(file => {
|
|
27
|
+
const content = file.contents.toString()
|
|
28
|
+
const attributes = {}
|
|
29
|
+
|
|
30
|
+
let match
|
|
31
|
+
while ((match = ATTRIBUTE_REGEX.exec(content)) !== null) {
|
|
32
|
+
const [ , name, value ] = match
|
|
33
|
+
attributes[name] = value
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!attributes['term-name'] || !attributes['hover-text']) {
|
|
37
|
+
console.warn(`Skipping file ${file.path} due to missing 'term-name' and/or 'hover-text'.`)
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
term: attributes['term-name'],
|
|
43
|
+
def: attributes['hover-text'],
|
|
44
|
+
content
|
|
45
|
+
}
|
|
46
|
+
}).filter(Boolean)
|
|
47
|
+
|
|
48
|
+
// Store the terms in the cache
|
|
49
|
+
glossaryContexts[sharedKey] = terms
|
|
50
|
+
}
|
|
19
51
|
const key = getKey(config.file.src)
|
|
20
52
|
if (!glossaryContexts[key]) {
|
|
21
53
|
glossaryContexts[key] = {
|
|
22
|
-
gloss: [],
|
|
54
|
+
gloss: glossaryContexts[sharedKey],
|
|
23
55
|
self: undefined,
|
|
24
|
-
dlist: undefined,
|
|
25
56
|
}
|
|
26
57
|
}
|
|
27
58
|
const context = glossaryContexts[key]
|
|
@@ -34,35 +65,7 @@ module.exports.register = function (registry, config = {}) {
|
|
|
34
65
|
const IDRX = /[/ _.-]+/g
|
|
35
66
|
|
|
36
67
|
function termId (term) {
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function dlistItem (context, term, def) {
|
|
41
|
-
const id = termId(term)
|
|
42
|
-
term = `anchor:${id}[${term}]${term}`
|
|
43
|
-
const termItem = context.self.createListItem(context.dlist, term)
|
|
44
|
-
const defItem = context.self.createListItem(context.dlist, def)
|
|
45
|
-
return [[termItem], defItem]
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function glossaryBlockMacro () {
|
|
49
|
-
return function () {
|
|
50
|
-
const self = this
|
|
51
|
-
self.named('glossary')
|
|
52
|
-
self.$option('format', 'short') //no target between glossary:: and [params]
|
|
53
|
-
// self.positionalAttributes(['name', 'parameters'])
|
|
54
|
-
self.process(function (parent, target, attributes) {
|
|
55
|
-
const context = vfs.getContext()
|
|
56
|
-
const dlist = self.createList(parent, 'dlist')
|
|
57
|
-
context.self = self
|
|
58
|
-
context.dlist = dlist
|
|
59
|
-
context.gloss
|
|
60
|
-
.forEach(({ term, def }) => {
|
|
61
|
-
dlist.blocks.push(dlistItem(context, term, def))
|
|
62
|
-
})
|
|
63
|
-
return dlist
|
|
64
|
-
})
|
|
65
|
-
}
|
|
68
|
+
return term.toLowerCase().replace(IDRX, '-')
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
const TRX = /(<[a-z]+)([^>]*>.*)/
|
|
@@ -78,23 +81,9 @@ module.exports.register = function (registry, config = {}) {
|
|
|
78
81
|
const term = attributes.term || target
|
|
79
82
|
const document = parent.document
|
|
80
83
|
const context = vfs.getContext()
|
|
81
|
-
// See if a predefined list of terms is available
|
|
82
|
-
const globalTerms = document.getAttribute("terms") || [];
|
|
83
84
|
const localTerms = document.getAttribute("local-terms") || [];
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
// Convert globalTerms to a Map for fast look up
|
|
87
|
-
let globalTermsMap = new Map(globalTerms.map(i => [i.term, i]));
|
|
88
|
-
|
|
89
|
-
// Create a new array based on localTerms, but replace items if they exist in globalTerms
|
|
90
|
-
mergedTerms = localTerms.map(item => globalTermsMap.has(item.term) ? globalTermsMap.get(item.term) : item);
|
|
91
|
-
|
|
92
|
-
// Update localTerms with the mergedTerms
|
|
93
|
-
document.setAttribute("terms", mergedTerms);
|
|
94
|
-
console.log(chalk.green('Merged global terms with local terms'))
|
|
95
|
-
}
|
|
96
|
-
const termData = (mergedTerms || []).find((t) => t.term === term) || {};
|
|
97
|
-
const customLink = termData.link;
|
|
85
|
+
const localTermData = (localTerms || []).find((t) => t.term === term) || {};
|
|
86
|
+
const customLink = localTermData.link;
|
|
98
87
|
var tooltip = document.getAttribute('glossary-tooltip')
|
|
99
88
|
if (tooltip === 'true') tooltip = 'data-glossary-tooltip'
|
|
100
89
|
if (tooltip && tooltip !== 'title' && !tooltip.startsWith('data-')) {
|
|
@@ -102,13 +91,17 @@ module.exports.register = function (registry, config = {}) {
|
|
|
102
91
|
tooltip = undefined
|
|
103
92
|
}
|
|
104
93
|
const logTerms = document.hasAttribute('glossary-log-terms')
|
|
105
|
-
var definition
|
|
94
|
+
var definition;
|
|
95
|
+
const index = context.gloss.findIndex((candidate) => candidate.term === term)
|
|
96
|
+
if (index >= 0) {
|
|
97
|
+
definition = context.gloss[index].def
|
|
98
|
+
} else {
|
|
99
|
+
definition = localTermData.definition || attributes.definition;
|
|
100
|
+
}
|
|
106
101
|
if (definition) {
|
|
107
102
|
logTerms && console.log(`${term}:: ${definition}`)
|
|
108
|
-
addItem(context, term, definition)
|
|
109
103
|
} else if (tooltip) {
|
|
110
|
-
|
|
111
|
-
definition = ~index ? context.gloss[index].def : `${term} not yet defined`
|
|
104
|
+
definition = `${term} not yet defined`
|
|
112
105
|
}
|
|
113
106
|
const links = document.getAttribute('glossary-links', 'true') === 'true'
|
|
114
107
|
var glossaryPage = document.getAttribute('glossary-page', '')
|
|
@@ -120,11 +113,15 @@ module.exports.register = function (registry, config = {}) {
|
|
|
120
113
|
}
|
|
121
114
|
const glossaryTermRole = document.getAttribute('glossary-term-role', 'glossary-term')
|
|
122
115
|
const attrs = glossaryTermRole ? { role: glossaryTermRole } : {}
|
|
123
|
-
|
|
124
|
-
|
|
116
|
+
var inline;
|
|
117
|
+
const termExistsInContext = context.gloss.some((candidate) => candidate.term === term);
|
|
118
|
+
if ((termExistsInContext && links) || (links && customLink)) {
|
|
119
|
+
inline = customLink
|
|
125
120
|
? self.createInline(parent, 'anchor', target, { type: 'link', target: customLink, attributes: attrs })
|
|
126
121
|
: self.createInline(parent, 'anchor', target, { type: 'xref', target: `${glossaryPage}#${termId(term)}`, reftext: target, attributes: attrs })
|
|
127
|
-
|
|
122
|
+
} else {
|
|
123
|
+
inline = self.createInline(parent, 'quoted', target, { attributes: attrs })
|
|
124
|
+
}
|
|
128
125
|
if (tooltip) {
|
|
129
126
|
const a = inline.convert()
|
|
130
127
|
const matches = a.match(TRX)
|
|
@@ -139,27 +136,7 @@ module.exports.register = function (registry, config = {}) {
|
|
|
139
136
|
}
|
|
140
137
|
}
|
|
141
138
|
|
|
142
|
-
function addItem (context, term, def) {
|
|
143
|
-
let i = 0
|
|
144
|
-
let comp = -1
|
|
145
|
-
for (; i < context.gloss.length && ((comp = term.localeCompare(context.gloss[i].term)) > 0); i++) {
|
|
146
|
-
}
|
|
147
|
-
if (comp < 0) {
|
|
148
|
-
context.gloss.splice(i, 0, { term, def })
|
|
149
|
-
if (context.self && context.dlist) {
|
|
150
|
-
context.dlist.blocks.splice(i, 0, dlistItem(context, term, def))
|
|
151
|
-
}
|
|
152
|
-
} else {
|
|
153
|
-
console.log(`duplicate glossary term ${term}`)
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
139
|
function doRegister (registry) {
|
|
158
|
-
if (typeof registry.blockMacro === 'function') {
|
|
159
|
-
registry.blockMacro(glossaryBlockMacro())
|
|
160
|
-
} else {
|
|
161
|
-
console.warn('no \'blockMacro\' method on alleged registry')
|
|
162
|
-
}
|
|
163
140
|
if (typeof registry.inlineMacro === 'function') {
|
|
164
141
|
registry.inlineMacro(glossaryInlineMacro())
|
|
165
142
|
} else {
|
package/package.json
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redpanda-data/docs-extensions-and-macros",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "Antora extensions and macros developed for Redpanda documentation.",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"antora",
|
|
7
|
+
"extension",
|
|
8
|
+
"macro",
|
|
9
|
+
"documentation",
|
|
10
|
+
"redpanda"
|
|
11
|
+
],
|
|
6
12
|
"author": {
|
|
7
13
|
"name": "Redpanda Docs Team"
|
|
8
14
|
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "antora --to-dir docs --fetch local-antora-playbook.yml",
|
|
17
|
+
"serve": "wds --node-resolve --open preview/test.html --watch --root-dir docs"
|
|
18
|
+
},
|
|
9
19
|
"contributors": [
|
|
10
20
|
{
|
|
11
21
|
"name": "JakeSCahill",
|
|
@@ -20,6 +30,7 @@
|
|
|
20
30
|
"./extensions/add-global-attributes": "./extensions/add-global-attributes.js",
|
|
21
31
|
"./extensions/version-fetcher/set-latest-version": "./extensions/version-fetcher/set-latest-version.js",
|
|
22
32
|
"./extensions/algolia-indexer/index": "./extensions/algolia-indexer/index.js",
|
|
33
|
+
"./extensions/aggregate-terms": "./extensions/aggregate-terms.js",
|
|
23
34
|
"./macros/glossary": "./macros/glossary.js",
|
|
24
35
|
"./macros/config-ref": "./macros/config-ref.js",
|
|
25
36
|
"./macros/helm-ref": "./macros/helm-ref.js"
|
|
@@ -34,15 +45,20 @@
|
|
|
34
45
|
"url": "git+https://github.com/redpanda-data/docs-extensions-and-macros"
|
|
35
46
|
},
|
|
36
47
|
"dependencies": {
|
|
37
|
-
"@antora/site-generator": "3.1.2",
|
|
38
48
|
"@octokit/plugin-retry": "^4.1.3",
|
|
39
49
|
"@octokit/rest": "^19.0.7",
|
|
40
50
|
"algoliasearch": "^4.17.0",
|
|
41
51
|
"chalk": "4.1.2",
|
|
42
|
-
"js-yaml": "^4.1.0",
|
|
43
52
|
"gulp": "^4.0.2",
|
|
44
53
|
"gulp-connect": "^5.7.0",
|
|
45
54
|
"html-entities": "2.3",
|
|
55
|
+
"js-yaml": "^4.1.0",
|
|
56
|
+
"lodash": "^4.17.21",
|
|
46
57
|
"node-html-parser": "5.4.2-0"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@antora/cli": "3.1.4",
|
|
61
|
+
"@antora/site-generator": "3.1.4",
|
|
62
|
+
"@web/dev-server": "^0.2.5"
|
|
47
63
|
}
|
|
48
64
|
}
|