@redpanda-data/docs-extensions-and-macros 5.37.0 → 5.37.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.
|
@@ -32,7 +32,9 @@
|
|
|
32
32
|
|
|
33
33
|
const loadAsciiDoc = require('@antora/asciidoc-loader')
|
|
34
34
|
const bigIntJson = require('../cli-utils/big-int-json')
|
|
35
|
-
|
|
35
|
+
// compareTags is the one set-available-attachment-versions.js uses to pick the
|
|
36
|
+
// tooltip dataset, so the build-time choice and the runtime fetch cannot disagree.
|
|
37
|
+
const { buildPageIndex, propertyAnchor, compareTags } = require('../macros/prop')
|
|
36
38
|
const { raiseListenerLimit } = require('./util/raise-listener-limit')
|
|
37
39
|
|
|
38
40
|
const PROPERTIES_JSON_RX = /^redpanda-properties-(v\d+\.\d+\.\d+(?:-[\w.]+)?)\.json$/
|
|
@@ -57,7 +59,7 @@ module.exports.register = function () {
|
|
|
57
59
|
// without that every <<anchor>> silently degraded to plain text.
|
|
58
60
|
const anchorIndexes = new Map()
|
|
59
61
|
this.once('contentClassified', ({ contentCatalog }) => {
|
|
60
|
-
for (const attachment of
|
|
62
|
+
for (const attachment of newestPropertyAttachments(contentCatalog)) {
|
|
61
63
|
const properties = readProperties(attachment, logger)
|
|
62
64
|
if (!properties) continue
|
|
63
65
|
const key = `${attachment.src.component}@${attachment.src.version || ''}`
|
|
@@ -72,7 +74,7 @@ module.exports.register = function () {
|
|
|
72
74
|
// class="xref unresolved" while the same xref resolved fine on a real page.
|
|
73
75
|
// Attachments are still published after this event.
|
|
74
76
|
this.once('documentsConverted', ({ contentCatalog, siteAsciiDocConfig }) => {
|
|
75
|
-
const attachments =
|
|
77
|
+
const attachments = newestPropertyAttachments(contentCatalog)
|
|
76
78
|
if (!attachments.length) return
|
|
77
79
|
|
|
78
80
|
for (const attachment of attachments) {
|
|
@@ -135,13 +137,13 @@ module.exports.register = function () {
|
|
|
135
137
|
logger.info(`${where}: rendered ${rendered} property descriptions to HTML${failed ? `, ${failed} failed` : ''}`)
|
|
136
138
|
if (unresolved.size) {
|
|
137
139
|
logger.warn(
|
|
138
|
-
`${where}: ${unresolved.size} xref target(s) in property descriptions could not be resolved, so they stay as raw macros in the published attachment: ` +
|
|
140
|
+
`${where} (${basename(attachment)}): ${unresolved.size} xref target(s) in property descriptions could not be resolved, so they stay as raw macros in the published attachment: ` +
|
|
139
141
|
`${[...unresolved].sort().join(', ')}.`
|
|
140
142
|
)
|
|
141
143
|
}
|
|
142
144
|
if (brokenAnchors.size) {
|
|
143
145
|
logger.warn(
|
|
144
|
-
`${where}: ${brokenAnchors.size} <<anchor>> reference(s) in property descriptions name no documented property, so they render as plain text: ` +
|
|
146
|
+
`${where} (${basename(attachment)}): ${brokenAnchors.size} <<anchor>> reference(s) in property descriptions name no documented property, so they render as plain text: ` +
|
|
145
147
|
`${[...brokenAnchors].sort().join(', ')}. Property anchors replace dots with hyphens, so redpanda.storage.mode is <<redpanda-storage-mode>>. ` +
|
|
146
148
|
'Fix them in the description or in docs-data/property-overrides.json.'
|
|
147
149
|
)
|
|
@@ -160,6 +162,36 @@ function propertyAttachments (contentCatalog) {
|
|
|
160
162
|
)
|
|
161
163
|
}
|
|
162
164
|
|
|
165
|
+
function tagOf (file) {
|
|
166
|
+
return basename(file).match(PROPERTIES_JSON_RX)[1]
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* The newest property dataset per component version, and only that one.
|
|
172
|
+
*
|
|
173
|
+
* A branch can ship more than one: doc-tools retains the 2 newest property
|
|
174
|
+
* JSONs on purpose, because the next generation run needs the older one as its
|
|
175
|
+
* --diff baseline. Only the newest is ever read as a dataset -- the docs UI
|
|
176
|
+
* resolves tooltips against available-properties-tag, which
|
|
177
|
+
* set-available-attachment-versions sets to the newest -- so rendering the
|
|
178
|
+
* older one is not just wasted work. This extension rewrites
|
|
179
|
+
* entry.description to resolve xrefs and then replaces the attachment
|
|
180
|
+
* contents, so converting a baseline would make the next diff report a
|
|
181
|
+
* description change for every property in it, and every dead <<anchor>> in a
|
|
182
|
+
* superseded dataset would be reported a second time against a file nobody
|
|
183
|
+
* publishes.
|
|
184
|
+
*/
|
|
185
|
+
function newestPropertyAttachments (contentCatalog) {
|
|
186
|
+
const newest = new Map()
|
|
187
|
+
for (const file of propertyAttachments(contentCatalog)) {
|
|
188
|
+
const key = `${file.src.component}@${file.src.version || ''}`
|
|
189
|
+
const held = newest.get(key)
|
|
190
|
+
if (!held || compareTags(tagOf(file), tagOf(held)) > 0) newest.set(key, file)
|
|
191
|
+
}
|
|
192
|
+
return [...newest.values()]
|
|
193
|
+
}
|
|
194
|
+
|
|
163
195
|
/**
|
|
164
196
|
* Parse one dataset, or report why it is unusable and return undefined. Left
|
|
165
197
|
* as-is on failure: the prop macro reports an unusable dataset with the detail,
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
*
|
|
14
14
|
* A block closes only on a delimiter of the same length as the one that opened
|
|
15
15
|
* it; a longer run, such as the row of dashes in a rendered table, is content.
|
|
16
|
+
* - indented literal paragraphs: an indented line that opens a block is
|
|
17
|
+
* rendered verbatim, so a URL on it is text and not a link
|
|
16
18
|
* - line comments (//), which Asciidoctor drops entirely
|
|
17
19
|
* - inline code spans (`...`)
|
|
18
20
|
*
|
|
@@ -60,6 +62,23 @@ const ATTRIBUTE_REFERENCE_RX = /\{[a-zA-Z0-9_][a-zA-Z0-9_-]*\}/
|
|
|
60
62
|
// prose cannot swallow the rest of the document.
|
|
61
63
|
const MAX_WRAPPED_LABEL_LENGTH = 500
|
|
62
64
|
|
|
65
|
+
// An indented line that opens a block is a literal paragraph: Asciidoctor
|
|
66
|
+
// renders it verbatim, so any URL on it is text rather than a link. This is
|
|
67
|
+
// where connector descriptions put their shell examples, which is how the
|
|
68
|
+
// three comma-joined gcloud scope strings in google_drive_list_labels were
|
|
69
|
+
// read as one URL and reported broken.
|
|
70
|
+
const INDENTED_RX = /^[ \t]+\S/
|
|
71
|
+
// Indentation does not make a literal paragraph out of a list item, a table
|
|
72
|
+
// cell or a description list: Asciidoctor still parses those, so their links
|
|
73
|
+
// are real. Verified against @asciidoctor/core, marker by marker (-, *, **,
|
|
74
|
+
// ., .., 1., a., |, term::).
|
|
75
|
+
const INDENTED_PARSED_RX = /^[ \t]+(?:[*.-]+[ \t]|\d+\.[ \t]|[a-zA-Z]\.[ \t]|\||\S.*::(?:[ \t]|$))/
|
|
76
|
+
// Lines a block can start after, so an indented line that follows one opens a
|
|
77
|
+
// literal paragraph: a list continuation (+), a block attribute list
|
|
78
|
+
// ([source,yaml]) or a block title (.Title). A blank line and the close of a
|
|
79
|
+
// delimited block do the same and are handled in the scan loop.
|
|
80
|
+
const BLOCK_START_RX = /^(?:\+|\[.*\]|\.\S.*)$/
|
|
81
|
+
|
|
63
82
|
// A delimiter run, plus the info string a fence may carry. Comment blocks
|
|
64
83
|
// (////) come first in the alternation so a //// line is not mistaken for the
|
|
65
84
|
// start of a line comment.
|
|
@@ -108,22 +127,48 @@ function closesBlock (delimiter, open) {
|
|
|
108
127
|
return delimiter.char === open.char && delimiter.length === open.length && !delimiter.info
|
|
109
128
|
}
|
|
110
129
|
|
|
130
|
+
/**
|
|
131
|
+
* True when an indented line at the start of a block opens a literal
|
|
132
|
+
* paragraph, rather than continuing a construct Asciidoctor still parses.
|
|
133
|
+
*/
|
|
134
|
+
function opensLiteralParagraph (line) {
|
|
135
|
+
return INDENTED_RX.test(line) && !INDENTED_PARSED_RX.test(line)
|
|
136
|
+
}
|
|
137
|
+
|
|
111
138
|
function scanContentUrls (content) {
|
|
112
139
|
const matches = []
|
|
113
140
|
const lines = content.split('\n')
|
|
114
141
|
let offset = 0
|
|
115
142
|
let openDelimiter = null
|
|
143
|
+
// A literal paragraph runs from the indented line that opened it to the next
|
|
144
|
+
// blank line, and takes any unindented lines in between with it.
|
|
145
|
+
let inLiteralParagraph = false
|
|
146
|
+
// A block can start at the top of the file, and again after every line that
|
|
147
|
+
// ended the last one.
|
|
148
|
+
let atBlockStart = true
|
|
116
149
|
for (const rawLine of lines) {
|
|
117
150
|
const line = rawLine.endsWith('\r') ? rawLine.slice(0, -1) : rawLine
|
|
118
151
|
const delimiter = blockDelimiter(line)
|
|
119
152
|
if (openDelimiter) {
|
|
120
153
|
// Inside a block, only its own matching delimiter gets out. Anything
|
|
121
154
|
// else on this line is content and is never scanned.
|
|
122
|
-
if (delimiter && closesBlock(delimiter, openDelimiter))
|
|
155
|
+
if (delimiter && closesBlock(delimiter, openDelimiter)) {
|
|
156
|
+
openDelimiter = null
|
|
157
|
+
atBlockStart = true
|
|
158
|
+
}
|
|
123
159
|
} else if (delimiter) {
|
|
124
160
|
openDelimiter = delimiter
|
|
125
|
-
|
|
126
|
-
|
|
161
|
+
inLiteralParagraph = false
|
|
162
|
+
} else {
|
|
163
|
+
const blank = !line.trim()
|
|
164
|
+
if (inLiteralParagraph) {
|
|
165
|
+
if (blank) inLiteralParagraph = false
|
|
166
|
+
} else if (atBlockStart && opensLiteralParagraph(line)) {
|
|
167
|
+
inLiteralParagraph = true
|
|
168
|
+
} else if (!LINE_COMMENT_RX.test(line)) {
|
|
169
|
+
scanLine(line, offset, matches, content)
|
|
170
|
+
}
|
|
171
|
+
atBlockStart = blank || BLOCK_START_RX.test(line)
|
|
127
172
|
}
|
|
128
173
|
offset += rawLine.length + 1
|
|
129
174
|
}
|