@redpanda-data/docs-extensions-and-macros 4.13.1 → 4.13.3

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.
Files changed (37) hide show
  1. package/bin/doc-tools-mcp.js +16 -4
  2. package/bin/doc-tools.js +768 -2089
  3. package/bin/mcp-tools/generated-docs-review.js +2 -2
  4. package/bin/mcp-tools/mcp-validation.js +1 -1
  5. package/bin/mcp-tools/openapi.js +2 -2
  6. package/bin/mcp-tools/property-docs.js +18 -0
  7. package/bin/mcp-tools/rpcn-docs.js +28 -3
  8. package/cli-utils/antora-utils.js +53 -2
  9. package/cli-utils/dependencies.js +313 -0
  10. package/cli-utils/diff-utils.js +273 -0
  11. package/cli-utils/doc-tools-utils.js +54 -0
  12. package/extensions/algolia-indexer/generate-index.js +134 -102
  13. package/extensions/algolia-indexer/index.js +70 -38
  14. package/extensions/collect-bloblang-samples.js +2 -1
  15. package/extensions/generate-rp-connect-categories.js +125 -67
  16. package/extensions/generate-rp-connect-info.js +291 -137
  17. package/macros/rp-connect-components.js +34 -5
  18. package/package.json +4 -3
  19. package/tools/add-commercial-names.js +207 -0
  20. package/tools/bundle-openapi.js +1 -1
  21. package/tools/generate-cli-docs.js +6 -2
  22. package/tools/get-console-version.js +5 -0
  23. package/tools/get-redpanda-version.js +5 -0
  24. package/tools/property-extractor/compare-properties.js +3 -3
  25. package/tools/property-extractor/generate-handlebars-docs.js +14 -14
  26. package/tools/property-extractor/generate-pr-summary.js +46 -0
  27. package/tools/property-extractor/pr-summary-formatter.js +375 -0
  28. package/tools/redpanda-connect/README.adoc +403 -38
  29. package/tools/redpanda-connect/connector-binary-analyzer.js +588 -0
  30. package/tools/redpanda-connect/generate-rpcn-connector-docs.js +97 -34
  31. package/tools/redpanda-connect/parse-csv-connectors.js +1 -1
  32. package/tools/redpanda-connect/pr-summary-formatter.js +663 -0
  33. package/tools/redpanda-connect/report-delta.js +70 -2
  34. package/tools/redpanda-connect/rpcn-connector-docs-handler.js +1279 -0
  35. package/tools/redpanda-connect/templates/connector.hbs +38 -0
  36. package/tools/redpanda-connect/templates/intro.hbs +0 -20
  37. package/tools/redpanda-connect/update-nav.js +216 -0
@@ -1,36 +1,54 @@
1
1
  'use strict'
2
2
 
3
+ /**
4
+ * Redpanda Connect Category Aggregation Extension
5
+ *
6
+ * IMPORTANT: This extension depends on generate-rp-connect-info running first.
7
+ * Both extensions use 'contentClassified' event. The generate-rp-connect-info
8
+ * extension returns a Promise, so Antora will wait for it to complete before
9
+ * running this extension (extensions are processed in playbook order).
10
+ *
11
+ * Ensure generate-rp-connect-info is listed BEFORE this extension in your playbook.
12
+ */
3
13
  module.exports.register = function ({ config }) {
4
14
  const logger = this.getLogger('redpanda-connect-category-aggregation-extension')
5
15
 
6
- this.once('contentClassified', ({ siteCatalog, contentCatalog }) => {
16
+ this.on('contentClassified', ({ contentCatalog }) => {
7
17
  const redpandaConnect = contentCatalog.getComponents().find(component => component.name === 'redpanda-connect')
18
+
8
19
  if (!redpandaConnect || !redpandaConnect.latest) {
9
20
  logger.warn('Could not find the redpanda-connect component. Skipping category creation.')
10
21
  return
11
22
  }
12
23
 
13
24
  const descriptions = redpandaConnect.latest.asciidoc.attributes.categories
14
- const componentNameMap = redpandaConnect.latest.asciidoc.attributes.components
15
- const certifiedConnectors = redpandaConnect.latest.asciidoc.attributes['certified-components']
16
- const enterpriseConnectors = redpandaConnect.latest.asciidoc.attributes['enterprise-components']
25
+ const csvData = redpandaConnect.latest.asciidoc.attributes.csvData
17
26
 
18
- if (!descriptions || !componentNameMap || !certifiedConnectors || !enterpriseConnectors) {
19
- if (!descriptions) {
20
- logger.error('No categories attribute found in redpanda-connect component')
21
- }
22
- if (!componentNameMap) {
23
- logger.error('No components attribute found in redpanda-connect component')
24
- }
25
- if (!certifiedConnectors) {
26
- logger.error('No certified-components attribute found in redpanda-connect component')
27
- }
28
- if (!enterpriseConnectors) {
29
- logger.error('No enterprise-components attribute found in redpanda-connect component')
30
- }
27
+ if (!descriptions) {
28
+ logger.error('No categories attribute found in redpanda-connect component')
29
+ return
30
+ }
31
+
32
+ if (!csvData || !csvData.data) {
33
+ logger.error('No csvData attribute found in redpanda-connect component.')
34
+ logger.error('Ensure generate-rp-connect-info extension is listed BEFORE this extension in your playbook.')
31
35
  return
32
36
  }
33
37
 
38
+ // Build lookup maps from CSV data
39
+ const supportLookup = new Map()
40
+ for (const row of csvData.data) {
41
+ const connector = row.connector
42
+ if (connector) {
43
+ supportLookup.set(connector, {
44
+ supportLevel: (row.support_level || 'community').toLowerCase(),
45
+ isEnterprise: row.is_licensed === 'Yes'
46
+ })
47
+ }
48
+ }
49
+
50
+ logger.info(`Loaded support data for ${supportLookup.size} connectors from CSV`)
51
+
34
52
  const connectCategoriesData = {}
35
53
  const flatComponentsData = []
36
54
  const driverSupportData = {}
@@ -38,88 +56,128 @@ module.exports.register = function ({ config }) {
38
56
  const types = Object.keys(descriptions)
39
57
 
40
58
  // Initialize connectCategoriesData for each type
41
- types.forEach(type => {
59
+ for (const type of types) {
42
60
  connectCategoriesData[type] = []
43
- })
61
+ }
44
62
 
45
63
  try {
46
64
  const files = contentCatalog.findBy({ component: 'redpanda-connect', family: 'page' })
47
65
 
48
- files.forEach(file => {
49
- let content = file.contents.toString('utf8')
50
- const categoryMatch = /:categories: (.*)/.exec(content)
51
- const typeMatch = /:type: (.*)/.exec(content)
52
- const statusMatch = /:status: (.*)/.exec(content)
53
- const driverSupportMatch = /:driver-support: (.*)/.exec(content)
54
- const cacheSupportMatch = /:cache-support: (.*)/.exec(content)
66
+ for (const file of files) {
67
+ // Prefer using page.asciidoc.attributes when available
68
+ const attrs = file.asciidoc?.attributes || {}
69
+
70
+ // Get attributes - prefer API, fallback to content parsing
71
+ const fileType = attrs.type || extractAttribute(file, 'type')
72
+ const categories = attrs.categories || extractAttribute(file, 'categories')
73
+ const status = attrs.status || extractAttribute(file, 'status')
74
+ const driverSupport = attrs['driver-support'] || extractAttribute(file, 'driver-support')
75
+ const cacheSupport = attrs['cache-support'] || extractAttribute(file, 'cache-support')
76
+ const commercialNames = attrs['page-commercial-names'] || extractAttribute(file, 'page-commercial-names')
77
+
55
78
  const pubUrl = file.pub.url
56
79
  const name = file.src.stem
57
80
 
58
- if (typeMatch) {
59
- const fileType = typeMatch[1]
60
-
61
- let status = statusMatch ? statusMatch[1] : 'community'
81
+ if (!fileType) continue
62
82
 
63
- // Skip deprecated components
64
- if (status === 'deprecated') return
83
+ let componentStatus = status || 'community'
65
84
 
66
- const isCertified = certifiedConnectors.some(connector => connector.name === name)
85
+ // Skip deprecated components
86
+ if (componentStatus === 'deprecated') continue
67
87
 
68
- const isEnterprise = enterpriseConnectors.some(connector => connector === name)
88
+ // Get support level from CSV data
89
+ const csvInfo = supportLookup.get(name)
90
+ const isEnterprise = csvInfo?.isEnterprise || false
69
91
 
70
- // Override status to "certified" if in the lookup table
71
- if (isCertified || isEnterprise) {
72
- status = 'certified'
92
+ // Determine status from CSV support level (CSV takes precedence)
93
+ if (csvInfo) {
94
+ if (csvInfo.supportLevel === 'certified' || csvInfo.supportLevel === 'enterprise') {
95
+ componentStatus = 'certified'
73
96
  } else {
74
- status = 'community'
97
+ componentStatus = csvInfo.supportLevel
75
98
  }
99
+ }
76
100
 
77
- // Find the common name
78
- const componentNameEntry = componentNameMap.find(component => component.key === name)
79
- const commonName = componentNameEntry ? componentNameEntry.name : name
101
+ // Parse commercial names and use first as display name
102
+ let commonName = name // Default to connector key name
103
+ if (commercialNames) {
104
+ const names = commercialNames.split(',').map(n => n.trim()).filter(n => n)
105
+ if (names.length > 0) {
106
+ commonName = names[0]
107
+ }
108
+ }
80
109
 
81
- // Populate connectCategoriesData
82
- if (types.includes(fileType) && categoryMatch) {
83
- const categories = categoryMatch[1].replace(/[\[\]"]/g, '').split(',').map(category => category.trim())
84
- categories.forEach(category => {
85
- let categoryObj = connectCategoriesData[fileType].find(cat => cat.name === category)
110
+ // Populate connectCategoriesData
111
+ if (types.includes(fileType) && categories) {
112
+ const categoryList = categories.replace(/[\[\]"]/g, '').split(',').map(cat => cat.trim())
86
113
 
87
- if (!categoryObj) {
88
- categoryObj = descriptions[fileType].find(desc => desc.name === category) || { name: category, description: "" }
89
- categoryObj.items = []
90
- connectCategoriesData[fileType].push(categoryObj)
91
- }
114
+ for (const category of categoryList) {
115
+ let categoryObj = connectCategoriesData[fileType].find(cat => cat.name === category)
92
116
 
93
- categoryObj.items.push({ name: commonName, url: pubUrl, status: status })
94
- })
95
- }
117
+ if (!categoryObj) {
118
+ categoryObj = descriptions[fileType].find(desc => desc.name === category) || { name: category, description: '' }
119
+ categoryObj.items = []
120
+ connectCategoriesData[fileType].push(categoryObj)
121
+ }
96
122
 
97
- // Populate flatComponentsData
98
- let flatItem = flatComponentsData.find(item => item.name === commonName)
99
- if (!flatItem) {
100
- flatItem = { name: commonName, originalName: name, support: status, types: [], enterprise: isEnterprise ? true : false}
101
- flatComponentsData.push(flatItem)
123
+ categoryObj.items.push({ name: commonName, url: pubUrl, status: componentStatus })
102
124
  }
125
+ }
103
126
 
104
- if (!flatItem.types.some(type => type.type === fileType)) {
105
- flatItem.types.push({ type: fileType, url: pubUrl, enterprise: isEnterprise? true : false, support: status})
127
+ // Populate flatComponentsData
128
+ let flatItem = flatComponentsData.find(item => item.name === commonName)
129
+ if (!flatItem) {
130
+ flatItem = {
131
+ name: commonName,
132
+ originalName: name,
133
+ support: componentStatus,
134
+ types: [],
135
+ enterprise: isEnterprise
106
136
  }
137
+ flatComponentsData.push(flatItem)
138
+ }
107
139
 
108
- // Populate support data
109
- if (driverSupportMatch) driverSupportData[name] = driverSupportMatch[1]
110
- if (cacheSupportMatch) cacheSupportData[name] = cacheSupportMatch[1]
140
+ if (!flatItem.types.some(t => t.type === fileType)) {
141
+ flatItem.types.push({
142
+ type: fileType,
143
+ url: pubUrl,
144
+ enterprise: isEnterprise,
145
+ support: componentStatus
146
+ })
111
147
  }
112
- })
148
+
149
+ // Populate support data
150
+ if (driverSupport) driverSupportData[name] = driverSupport
151
+ if (cacheSupport) cacheSupportData[name] = cacheSupport
152
+ }
113
153
 
114
154
  redpandaConnect.latest.asciidoc.attributes.connectCategoriesData = connectCategoriesData
115
155
  redpandaConnect.latest.asciidoc.attributes.flatComponentsData = flatComponentsData
116
156
  redpandaConnect.latest.asciidoc.attributes.driverSupportData = driverSupportData
117
157
  redpandaConnect.latest.asciidoc.attributes.cacheSupportData = cacheSupportData
118
158
 
119
- logger.debug(`Added Redpanda Connect data to latest Asciidoc object.`)
120
- logger.debug(`${JSON.stringify({ connectCategoriesData, flatComponentsData }, null, 2)}`)
159
+ logger.info(`Processed ${flatComponentsData.length} components across ${types.length} types`)
160
+ logger.debug(`Categories data: ${JSON.stringify(connectCategoriesData, null, 2)}`)
121
161
  } catch (error) {
122
162
  logger.error(`Error processing Redpanda Connect files: ${error.message}`)
163
+ logger.error(error.stack)
123
164
  }
124
165
  })
166
+
167
+ /**
168
+ * Extract attribute from file contents when page.asciidoc.attributes is not available.
169
+ * This is a fallback for when attributes haven't been parsed yet.
170
+ */
171
+ function extractAttribute (file, attrName) {
172
+ if (!file.contents) return null
173
+
174
+ try {
175
+ const content = file.contents.toString('utf8')
176
+ const regex = new RegExp(`:${attrName}:\\s*(.*)`)
177
+ const match = regex.exec(content)
178
+ return match ? match[1].trim() : null
179
+ } catch {
180
+ return null
181
+ }
182
+ }
125
183
  }