@redpanda-data/docs-extensions-and-macros 4.5.0 → 4.6.1

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 (32) hide show
  1. package/README.adoc +0 -163
  2. package/bin/doc-tools.js +492 -283
  3. package/cli-utils/antora-utils.js +127 -0
  4. package/cli-utils/generate-cluster-docs.sh +41 -29
  5. package/cli-utils/self-managed-docs-branch.js +2 -1
  6. package/cli-utils/start-cluster.sh +70 -30
  7. package/extensions/generate-rp-connect-info.js +14 -9
  8. package/package.json +6 -5
  9. package/tools/redpanda-connect/generate-rpcn-connector-docs.js +233 -0
  10. package/tools/redpanda-connect/helpers/advancedConfig.js +17 -0
  11. package/tools/redpanda-connect/helpers/buildConfigYaml.js +53 -0
  12. package/tools/redpanda-connect/helpers/commonConfig.js +31 -0
  13. package/tools/redpanda-connect/helpers/eq.js +10 -0
  14. package/tools/redpanda-connect/helpers/index.js +19 -0
  15. package/tools/redpanda-connect/helpers/isObject.js +1 -0
  16. package/tools/redpanda-connect/helpers/join.js +6 -0
  17. package/tools/redpanda-connect/helpers/ne.js +10 -0
  18. package/tools/redpanda-connect/helpers/or.js +4 -0
  19. package/tools/redpanda-connect/helpers/renderConnectExamples.js +37 -0
  20. package/tools/redpanda-connect/helpers/renderConnectFields.js +148 -0
  21. package/tools/redpanda-connect/helpers/renderLeafField.js +64 -0
  22. package/tools/redpanda-connect/helpers/renderObjectField.js +41 -0
  23. package/tools/redpanda-connect/helpers/renderYamlList.js +24 -0
  24. package/tools/redpanda-connect/helpers/toYaml.js +11 -0
  25. package/tools/redpanda-connect/helpers/uppercase.js +9 -0
  26. package/tools/redpanda-connect/parse-csv-connectors.js +63 -0
  27. package/tools/redpanda-connect/report-delta.js +152 -0
  28. package/tools/redpanda-connect/templates/connector.hbs +20 -0
  29. package/tools/redpanda-connect/templates/examples-partials.hbs +7 -0
  30. package/tools/redpanda-connect/templates/fields-partials.hbs +13 -0
  31. package/tools/redpanda-connect/templates/intro.hbs +33 -0
  32. package/macros/data-template.js +0 -591
@@ -0,0 +1,41 @@
1
+ const renderLeafField = require('./renderLeafField');
2
+
3
+ /**
4
+ * Recursively renders an object‐typed field ( has .children[]) at the given indentation.
5
+ * Skips any sub-field with is_deprecated=true. Even if the parent has no default,
6
+ * the parent node itself is printed without a value (just a “key:”), then children.
7
+ *
8
+ * @param {Object} field – one field object of type==="object"
9
+ * @param {number} indentLevel – number of spaces to indent the “key:”
10
+ * @returns {string[]} – an array of lines (parent + children)
11
+ */
12
+ module.exports = function renderObjectField(field, indentLevel) {
13
+ if (!field || !field.name || !Array.isArray(field.children)) {
14
+ throw new Error('renderObjectField requires a field object with name and children array');
15
+ }
16
+ if (typeof indentLevel !== 'number' || indentLevel < 0) {
17
+ throw new Error('indentLevel must be a non-negative number');
18
+ }
19
+ const lines = [];
20
+ const indent = ' '.repeat(indentLevel);
21
+
22
+ // Print the parent heading (no default comment here, because parent is just a namespace)
23
+ lines.push(`${indent}${field.name}:`);
24
+
25
+ // Recurse into children at indentLevel + 2
26
+ const childIndent = indentLevel + 2;
27
+ field.children.forEach(child => {
28
+ if (child.is_deprecated) {
29
+ return; // skip entirely
30
+ }
31
+ if (Array.isArray(child.children) && child.children.length > 0) {
32
+ // Nested object → recurse
33
+ lines.push(...renderObjectField(child, childIndent));
34
+ } else {
35
+ // Leaf → render via renderLeafField
36
+ lines.push(renderLeafField(child, childIndent));
37
+ }
38
+ });
39
+
40
+ return lines;
41
+ }
@@ -0,0 +1,24 @@
1
+ const yaml = require('yaml');
2
+
3
+ /**
4
+ * Renders a list of objects or scalar items as a YAML list
5
+ *
6
+ * @param {string} name - The field name to use as the YAML key.
7
+ * @param {Array<Object|string|Array>} exampleGroups - An array of example groups.
8
+ * @returns {string} The rendered YAML list string.
9
+ */
10
+ module.exports = function renderYamlList(name, exampleGroups) {
11
+ let out = `${name}:\n`;
12
+ exampleGroups.forEach(group => {
13
+ const items = Array.isArray(group) ? group : [group];
14
+ items.forEach(item => {
15
+ const snippet = yaml.stringify(item).trim();
16
+ const lines = snippet.split('\n');
17
+ out += lines
18
+ .map((line, idx) => (idx === 0 ? ` - ${line}` : ` ${line}`))
19
+ .join('\n') + '\n';
20
+ });
21
+ out += '\n';
22
+ });
23
+ return out;
24
+ }
@@ -0,0 +1,11 @@
1
+ const yaml = require('yaml');
2
+
3
+ /**
4
+ * Converts an object to a YAML string.
5
+ *
6
+ * @param {Object} obj - The object to convert.
7
+ * @returns {string} The YAML representation of the object, trimmed.
8
+ */
9
+ module.exports = function toYaml(obj) {
10
+ return yaml.stringify(obj).trim();
11
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Converts a string to uppercase.
3
+ *
4
+ * @param {string} str - The string to convert.
5
+ * @returns {string} The uppercase version of the input string.
6
+ */
7
+ module.exports = function uppercase(str) {
8
+ return String(str).toUpperCase();
9
+ }
@@ -0,0 +1,63 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const Papa = require('papaparse');
5
+ const fetchFromGithub = require('../fetch-from-github.js');
6
+
7
+ const CSV_PATH = 'internal/plugins/info.csv';
8
+ const GITHUB = { owner: 'redpanda-data', repo: 'connect', remotePath: CSV_PATH };
9
+
10
+ async function parseCSVConnectors(localCsvPath, logger) {
11
+ logger = logger || console;
12
+ let csvText;
13
+
14
+ try {
15
+ if (localCsvPath && fs.existsSync(localCsvPath) && path.extname(localCsvPath) === '.csv') {
16
+ logger.info(`📄 Loading CSV from local file: ${localCsvPath}`);
17
+ csvText = fs.readFileSync(localCsvPath, 'utf8');
18
+ } else {
19
+ const tmpDir = path.join(os.tmpdir(), 'redpanda-connect-csv');
20
+ await fetchFromGithub(GITHUB.owner, GITHUB.repo, GITHUB.remotePath, tmpDir);
21
+ const downloaded = path.join(tmpDir, path.basename(GITHUB.remotePath));
22
+ if (!fs.existsSync(downloaded)) {
23
+ throw new Error(`Expected CSV at ${downloaded} but did not find it`);
24
+ }
25
+ logger.info(`📥 Loaded CSV from GitHub into: ${downloaded}`);
26
+ csvText = fs.readFileSync(downloaded, 'utf8');
27
+ fs.rmSync(tmpDir, { recursive: true, force: true });
28
+ }
29
+
30
+ const parsed = Papa.parse(csvText, {
31
+ header: true,
32
+ skipEmptyLines: true,
33
+ transformHeader: h => h.trim()
34
+ });
35
+
36
+ if (!parsed.meta.fields.includes('name') || !parsed.meta.fields.includes('type')) {
37
+ throw new Error('CSV is missing required headers: name and type');
38
+ }
39
+
40
+ const cleaned = parsed.data
41
+ .map(row => {
42
+ const trimmed = Object.fromEntries(
43
+ Object.entries(row).map(([k, v]) => [k.trim(), (v || '').trim()])
44
+ );
45
+
46
+ if (!trimmed.name || !trimmed.type) return null;
47
+
48
+ return {
49
+ name: trimmed.name,
50
+ type: trimmed.type,
51
+ is_cloud_supported: (trimmed.cloud || '').toLowerCase() === 'y' ? 'y' : 'n'
52
+ };
53
+ })
54
+ .filter(Boolean);
55
+
56
+ logger.info(`✅ Parsed ${cleaned.length} connector records.`);
57
+ return cleaned;
58
+ } catch (err) {
59
+ throw new Error(`CSV parsing failed: ${err.message}`);
60
+ }
61
+ }
62
+
63
+ module.exports = parseCSVConnectors;
@@ -0,0 +1,152 @@
1
+ // tools/redpanda-connect/report-delta.js
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const yaml = require('yaml');
7
+ const { execSync } = require('child_process');
8
+
9
+ function discoverComponentKeys(obj) {
10
+ return Object.keys(obj).filter(key => Array.isArray(obj[key]));
11
+ }
12
+
13
+ function buildComponentMap(indexObj) {
14
+ const map = {};
15
+ const types = discoverComponentKeys(indexObj);
16
+
17
+ types.forEach(type => {
18
+ (indexObj[type] || []).forEach(component => {
19
+ const name = component.name;
20
+ if (!name) return;
21
+
22
+ const lookupKey = `${type}:${name}`;
23
+ let childArray = [];
24
+
25
+ if (type === 'config') {
26
+ if (Array.isArray(component.children)) {
27
+ childArray = component.children;
28
+ }
29
+ } else {
30
+ if (component.config && Array.isArray(component.config.children)) {
31
+ childArray = component.config.children;
32
+ }
33
+ }
34
+
35
+ const fieldNames = childArray.map(f => f.name);
36
+ map[lookupKey] = { raw: component, fields: fieldNames };
37
+ });
38
+ });
39
+
40
+ return map;
41
+ }
42
+
43
+ function getRpkConnectVersion() {
44
+ try {
45
+ // Make sure the connect plugin is upgraded first (silent)
46
+ execSync('rpk connect upgrade', { stdio: 'ignore' });
47
+
48
+ // Now capture the --version output
49
+ const raw = execSync('rpk connect --version', {
50
+ stdio: ['ignore', 'pipe', 'ignore'],
51
+ })
52
+ .toString()
53
+ .trim();
54
+
55
+ // raw looks like:
56
+ // Version: 4.53.0
57
+ // Date: 2025-04-18T17:49:53Z
58
+ // We want to extract “4.53.0”
59
+ const match = raw.match(/^Version:\s*(.+)$/m);
60
+ if (!match) {
61
+ throw new Error(`Unexpected format from "rpk connect --version":\n${raw}`);
62
+ }
63
+ return match[1];
64
+ } catch (err) {
65
+ throw new Error(`Unable to run "rpk connect --version": ${err.message}`);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Given two “index objects” (parsed from connect.json), produce a console summary of:
71
+ * • which connectors/components are brand-new
72
+ * • which new fields appeared under existing connectors (including “config” entries)
73
+ * • for each new component/field, if the raw object contains “version” or “introducedInVersion” or “requiresVersion” metadata, print it
74
+ */
75
+ function printDeltaReport(oldIndex, newIndex) {
76
+ const oldMap = buildComponentMap(oldIndex);
77
+ const newMap = buildComponentMap(newIndex);
78
+
79
+ // 1) brand-new components
80
+ const newComponentKeys = Object.keys(newMap).filter(k => !(k in oldMap));
81
+
82
+ // 2) brand-new fields under shared components
83
+ const newFields = [];
84
+ Object.keys(newMap).forEach(cKey => {
85
+ if (!(cKey in oldMap)) return; // skip brand-new components here
86
+ const oldFields = new Set(oldMap[cKey].fields || []);
87
+ const newFieldsArr = newMap[cKey].fields || [];
88
+ newFieldsArr.forEach(fName => {
89
+ if (!oldFields.has(fName)) {
90
+ // fetch raw field metadata if available
91
+ const [type, compName] = cKey.split(':');
92
+ let rawFieldObj = null;
93
+ if (type === 'config') {
94
+ rawFieldObj = (newMap[cKey].raw.children || []).find(f => f.name === fName);
95
+ } else {
96
+ rawFieldObj = (newMap[cKey].raw.config?.children || []).find(f => f.name === fName);
97
+ }
98
+
99
+ let introducedIn = rawFieldObj && (rawFieldObj.introducedInVersion || rawFieldObj.version);
100
+ let requiresVer = rawFieldObj && rawFieldObj.requiresVersion;
101
+
102
+ newFields.push({
103
+ component: cKey,
104
+ field: fName,
105
+ introducedIn,
106
+ requiresVersion: requiresVer,
107
+ });
108
+ }
109
+ });
110
+ });
111
+
112
+ console.log('\n📋 RPCN Connector Delta Report\n');
113
+
114
+ if (newComponentKeys.length) {
115
+ console.log('➤ Newly added components:');
116
+ newComponentKeys.forEach(key => {
117
+ const [type, name] = key.split(':');
118
+ const raw = newMap[key].raw;
119
+ const status = raw.status || raw.type || '';
120
+ const version = raw.version || raw.introducedInVersion || '';
121
+ console.log(
122
+ ` • ${type}/${name}${
123
+ status ? ` (${status})` : ''
124
+ }${version ? ` — introduced in ${version}` : ''}`
125
+ );
126
+ });
127
+ console.log('');
128
+ } else {
129
+ console.log('➤ No newly added components.\n');
130
+ }
131
+
132
+ if (newFields.length) {
133
+ console.log('➤ Newly added fields:');
134
+ newFields.forEach(entry => {
135
+ const { component, field, introducedIn, requiresVersion } = entry;
136
+ process.stdout.write(` • ${component} → ${field}`);
137
+ if (introducedIn) process.stdout.write(` (introducedIn: ${introducedIn})`);
138
+ if (requiresVersion) process.stdout.write(` (requiresVersion: ${requiresVersion})`);
139
+ console.log('');
140
+ });
141
+ console.log('');
142
+ } else {
143
+ console.log('➤ No newly added fields.\n');
144
+ }
145
+ }
146
+
147
+ module.exports = {
148
+ discoverComponentKeys,
149
+ buildComponentMap,
150
+ getRpkConnectVersion,
151
+ printDeltaReport,
152
+ };
@@ -0,0 +1,20 @@
1
+ = {{{name}}}
2
+ // tag::single-source[]
3
+ :type: {{type}}
4
+ :status: {{status}}
5
+ :categories: [{{join categories ", "}}]
6
+ :description: {{#if summary}}{{{summary}}}{{else if description}}{{description}}{{else}}TODO: Missing description. Add a description in the overrides file or source data.{{/if}}
7
+
8
+ component_type_dropdown::[]
9
+
10
+ {{> intro}}
11
+
12
+ {{#if (or this.config.children children)}}
13
+ include::redpanda-connect:components:partial$fields/{{type}}s/{{name}}.adoc[]
14
+ {{/if}}
15
+
16
+ {{#if this.examples}}
17
+ include::redpanda-connect:components:partial$examples/{{type}}s/{{name}}.adoc[]
18
+ {{/if}}
19
+
20
+ // end::single-source[]
@@ -0,0 +1,7 @@
1
+ {{#with examples}}
2
+ // This content is autogenerated. Do not edit manually.
3
+
4
+ == Examples
5
+
6
+ {{{renderConnectExamples this}}}
7
+ {{/with}}
@@ -0,0 +1,13 @@
1
+ {{#if this.config.children}}
2
+ // This content is autogenerated. Do not edit manually. To override descriptions, use the doc-tools CLI with the --overrides option: https://redpandadata.atlassian.net/wiki/spaces/DOC/pages/1247543314/Generate+reference+docs+for+Redpanda+Connect
3
+
4
+ == Fields
5
+
6
+ {{{renderConnectFields this.config.children}}}
7
+ {{else if this.children}}
8
+ // This content is autogenerated. Do not edit manually. To override descriptions, use the doc-tools CLI with the --overrides option: https://redpandadata.atlassian.net/wiki/spaces/DOC/pages/1247543314/Generate+reference+docs+for+Redpanda+Connect
9
+
10
+ == Fields
11
+
12
+ {{{renderConnectFields this.children}}}
13
+ {{/if}}
@@ -0,0 +1,33 @@
1
+ {{{summary}}}
2
+
3
+ {{#if version}}
4
+ ifndef::env-cloud[]
5
+ Introduced in version {{version}}.
6
+ endif::[]
7
+ {{/if}}
8
+
9
+ {{#if this.config.children}}
10
+ [tabs]
11
+ ======
12
+ Common::
13
+ +
14
+ --
15
+ ```yaml
16
+ {{{commonConfig this.type this.name this.config.children}}}
17
+ ```
18
+ --
19
+ Advanced::
20
+ +
21
+ --
22
+ ```yaml
23
+ {{{advancedConfig this.type this.name this.config.children}}}
24
+ ```
25
+ --
26
+ ======
27
+ {{/if}}
28
+
29
+ {{#if description}}
30
+ {{{description}}}
31
+ {{else}}
32
+ TODO: Missing description. Add a description in the overrides file or source data.
33
+ {{/if}}