@redpanda-data/docs-extensions-and-macros 4.13.1 → 4.13.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.
Files changed (33) hide show
  1. package/bin/doc-tools-mcp.js +15 -3
  2. package/bin/doc-tools.js +767 -2088
  3. package/bin/mcp-tools/property-docs.js +18 -0
  4. package/bin/mcp-tools/rpcn-docs.js +28 -3
  5. package/cli-utils/antora-utils.js +53 -2
  6. package/cli-utils/dependencies.js +313 -0
  7. package/cli-utils/diff-utils.js +273 -0
  8. package/cli-utils/doc-tools-utils.js +54 -0
  9. package/extensions/algolia-indexer/generate-index.js +134 -102
  10. package/extensions/algolia-indexer/index.js +70 -38
  11. package/extensions/collect-bloblang-samples.js +2 -1
  12. package/extensions/generate-rp-connect-categories.js +126 -67
  13. package/extensions/generate-rp-connect-info.js +291 -137
  14. package/macros/rp-connect-components.js +34 -5
  15. package/package.json +4 -3
  16. package/tools/add-commercial-names.js +207 -0
  17. package/tools/generate-cli-docs.js +6 -2
  18. package/tools/get-console-version.js +5 -0
  19. package/tools/get-redpanda-version.js +5 -0
  20. package/tools/property-extractor/compare-properties.js +3 -3
  21. package/tools/property-extractor/generate-handlebars-docs.js +14 -14
  22. package/tools/property-extractor/generate-pr-summary.js +46 -0
  23. package/tools/property-extractor/pr-summary-formatter.js +375 -0
  24. package/tools/redpanda-connect/README.adoc +403 -38
  25. package/tools/redpanda-connect/connector-binary-analyzer.js +588 -0
  26. package/tools/redpanda-connect/generate-rpcn-connector-docs.js +97 -34
  27. package/tools/redpanda-connect/parse-csv-connectors.js +1 -1
  28. package/tools/redpanda-connect/pr-summary-formatter.js +601 -0
  29. package/tools/redpanda-connect/report-delta.js +69 -2
  30. package/tools/redpanda-connect/rpcn-connector-docs-handler.js +1180 -0
  31. package/tools/redpanda-connect/templates/connector.hbs +38 -0
  32. package/tools/redpanda-connect/templates/intro.hbs +0 -20
  33. package/tools/redpanda-connect/update-nav.js +205 -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,129 @@ 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
+ // FIXED: Use correct attribute name (page-commercial-names, not commercial-names)
77
+ const commercialNames = attrs['page-commercial-names'] || extractAttribute(file, 'page-commercial-names')
78
+
55
79
  const pubUrl = file.pub.url
56
80
  const name = file.src.stem
57
81
 
58
- if (typeMatch) {
59
- const fileType = typeMatch[1]
60
-
61
- let status = statusMatch ? statusMatch[1] : 'community'
82
+ if (!fileType) continue
62
83
 
63
- // Skip deprecated components
64
- if (status === 'deprecated') return
84
+ let componentStatus = status || 'community'
65
85
 
66
- const isCertified = certifiedConnectors.some(connector => connector.name === name)
86
+ // Skip deprecated components
87
+ if (componentStatus === 'deprecated') continue
67
88
 
68
- const isEnterprise = enterpriseConnectors.some(connector => connector === name)
89
+ // Get support level from CSV data
90
+ const csvInfo = supportLookup.get(name)
91
+ const isEnterprise = csvInfo?.isEnterprise || false
69
92
 
70
- // Override status to "certified" if in the lookup table
71
- if (isCertified || isEnterprise) {
72
- status = 'certified'
93
+ // Determine status from CSV support level (CSV takes precedence)
94
+ if (csvInfo) {
95
+ if (csvInfo.supportLevel === 'certified' || csvInfo.supportLevel === 'enterprise') {
96
+ componentStatus = 'certified'
73
97
  } else {
74
- status = 'community'
98
+ componentStatus = csvInfo.supportLevel
75
99
  }
100
+ }
76
101
 
77
- // Find the common name
78
- const componentNameEntry = componentNameMap.find(component => component.key === name)
79
- const commonName = componentNameEntry ? componentNameEntry.name : name
102
+ // Parse commercial names and use first as display name
103
+ let commonName = name // Default to connector key name
104
+ if (commercialNames) {
105
+ const names = commercialNames.split(',').map(n => n.trim()).filter(n => n)
106
+ if (names.length > 0) {
107
+ commonName = names[0]
108
+ }
109
+ }
80
110
 
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)
111
+ // Populate connectCategoriesData
112
+ if (types.includes(fileType) && categories) {
113
+ const categoryList = categories.replace(/[\[\]"]/g, '').split(',').map(cat => cat.trim())
86
114
 
87
- if (!categoryObj) {
88
- categoryObj = descriptions[fileType].find(desc => desc.name === category) || { name: category, description: "" }
89
- categoryObj.items = []
90
- connectCategoriesData[fileType].push(categoryObj)
91
- }
115
+ for (const category of categoryList) {
116
+ let categoryObj = connectCategoriesData[fileType].find(cat => cat.name === category)
92
117
 
93
- categoryObj.items.push({ name: commonName, url: pubUrl, status: status })
94
- })
95
- }
118
+ if (!categoryObj) {
119
+ categoryObj = descriptions[fileType].find(desc => desc.name === category) || { name: category, description: '' }
120
+ categoryObj.items = []
121
+ connectCategoriesData[fileType].push(categoryObj)
122
+ }
96
123
 
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)
124
+ categoryObj.items.push({ name: commonName, url: pubUrl, status: componentStatus })
102
125
  }
126
+ }
103
127
 
104
- if (!flatItem.types.some(type => type.type === fileType)) {
105
- flatItem.types.push({ type: fileType, url: pubUrl, enterprise: isEnterprise? true : false, support: status})
128
+ // Populate flatComponentsData
129
+ let flatItem = flatComponentsData.find(item => item.name === commonName)
130
+ if (!flatItem) {
131
+ flatItem = {
132
+ name: commonName,
133
+ originalName: name,
134
+ support: componentStatus,
135
+ types: [],
136
+ enterprise: isEnterprise
106
137
  }
138
+ flatComponentsData.push(flatItem)
139
+ }
107
140
 
108
- // Populate support data
109
- if (driverSupportMatch) driverSupportData[name] = driverSupportMatch[1]
110
- if (cacheSupportMatch) cacheSupportData[name] = cacheSupportMatch[1]
141
+ if (!flatItem.types.some(t => t.type === fileType)) {
142
+ flatItem.types.push({
143
+ type: fileType,
144
+ url: pubUrl,
145
+ enterprise: isEnterprise,
146
+ support: componentStatus
147
+ })
111
148
  }
112
- })
149
+
150
+ // Populate support data
151
+ if (driverSupport) driverSupportData[name] = driverSupport
152
+ if (cacheSupport) cacheSupportData[name] = cacheSupport
153
+ }
113
154
 
114
155
  redpandaConnect.latest.asciidoc.attributes.connectCategoriesData = connectCategoriesData
115
156
  redpandaConnect.latest.asciidoc.attributes.flatComponentsData = flatComponentsData
116
157
  redpandaConnect.latest.asciidoc.attributes.driverSupportData = driverSupportData
117
158
  redpandaConnect.latest.asciidoc.attributes.cacheSupportData = cacheSupportData
118
159
 
119
- logger.debug(`Added Redpanda Connect data to latest Asciidoc object.`)
120
- logger.debug(`${JSON.stringify({ connectCategoriesData, flatComponentsData }, null, 2)}`)
160
+ logger.info(`Processed ${flatComponentsData.length} components across ${types.length} types`)
161
+ logger.debug(`Categories data: ${JSON.stringify(connectCategoriesData, null, 2)}`)
121
162
  } catch (error) {
122
163
  logger.error(`Error processing Redpanda Connect files: ${error.message}`)
164
+ logger.error(error.stack)
123
165
  }
124
166
  })
167
+
168
+ /**
169
+ * Extract attribute from file contents when page.asciidoc.attributes is not available.
170
+ * This is a fallback for when attributes haven't been parsed yet.
171
+ */
172
+ function extractAttribute (file, attrName) {
173
+ if (!file.contents) return null
174
+
175
+ try {
176
+ const content = file.contents.toString('utf8')
177
+ const regex = new RegExp(`:${attrName}:\\s*(.*)`)
178
+ const match = regex.exec(content)
179
+ return match ? match[1].trim() : null
180
+ } catch {
181
+ return null
182
+ }
183
+ }
125
184
  }