@redpanda-data/docs-extensions-and-macros 4.5.0 → 4.6.0
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 +0 -163
- package/bin/doc-tools.js +491 -286
- package/cli-utils/antora-utils.js +127 -0
- package/cli-utils/self-managed-docs-branch.js +2 -1
- package/package.json +6 -5
- package/tools/redpanda-connect/generate-rpcn-connector-docs.js +205 -0
- package/tools/redpanda-connect/helpers/advancedConfig.js +17 -0
- package/tools/redpanda-connect/helpers/buildConfigYaml.js +53 -0
- package/tools/redpanda-connect/helpers/commonConfig.js +31 -0
- package/tools/redpanda-connect/helpers/eq.js +10 -0
- package/tools/redpanda-connect/helpers/index.js +19 -0
- package/tools/redpanda-connect/helpers/isObject.js +1 -0
- package/tools/redpanda-connect/helpers/join.js +6 -0
- package/tools/redpanda-connect/helpers/ne.js +10 -0
- package/tools/redpanda-connect/helpers/or.js +4 -0
- package/tools/redpanda-connect/helpers/renderConnectExamples.js +37 -0
- package/tools/redpanda-connect/helpers/renderConnectFields.js +146 -0
- package/tools/redpanda-connect/helpers/renderLeafField.js +64 -0
- package/tools/redpanda-connect/helpers/renderObjectField.js +41 -0
- package/tools/redpanda-connect/helpers/renderYamlList.js +24 -0
- package/tools/redpanda-connect/helpers/toYaml.js +11 -0
- package/tools/redpanda-connect/helpers/uppercase.js +9 -0
- package/tools/redpanda-connect/parse-csv-connectors.js +63 -0
- package/tools/redpanda-connect/report-delta.js +152 -0
- package/tools/redpanda-connect/templates/connector.hbs +20 -0
- package/tools/redpanda-connect/templates/examples-partials.hbs +7 -0
- package/tools/redpanda-connect/templates/fields-partials.hbs +13 -0
- package/tools/redpanda-connect/templates/intro.hbs +35 -0
- package/macros/data-template.js +0 -591
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const yaml = require('js-yaml');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Attempts to locate and parse `antora.yml` in the current working directory.
|
|
9
|
+
*
|
|
10
|
+
* @returns {Object|undefined} The parsed YAML as a JavaScript object, or undefined if not found or on error.
|
|
11
|
+
*/
|
|
12
|
+
function loadAntoraConfig() {
|
|
13
|
+
const antoraPath = path.join(process.cwd(), 'antora.yml');
|
|
14
|
+
if (!fs.existsSync(antoraPath)) {
|
|
15
|
+
// No antora.yml in project root
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const fileContents = fs.readFileSync(antoraPath, 'utf8');
|
|
21
|
+
const config = yaml.load(fileContents);
|
|
22
|
+
if (typeof config !== 'object' || config === null) {
|
|
23
|
+
console.error('Warning: antora.yml parsed to a non‐object value.');
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
return config;
|
|
27
|
+
} catch (err) {
|
|
28
|
+
console.error(`Error reading/parsing antora.yml: ${err.message}`);
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Safely retrieves a nested value from the Antora configuration, given a "dot path".
|
|
35
|
+
*
|
|
36
|
+
* Example usage:
|
|
37
|
+
* const latestVersion = getAntoraValue('asciidoc.attributes.latest-connect-version');
|
|
38
|
+
*
|
|
39
|
+
* @param {string} keyPath
|
|
40
|
+
* A dot-separated path into the Antora object (e.g. "asciidoc.attributes.foo").
|
|
41
|
+
* @returns {*}
|
|
42
|
+
* The value at that path, or undefined if the file is missing or the key does not exist.
|
|
43
|
+
*/
|
|
44
|
+
function getAntoraValue(keyPath) {
|
|
45
|
+
const config = loadAntoraConfig();
|
|
46
|
+
if (!config) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Split on dots, but ignore empty segments
|
|
51
|
+
const segments = keyPath.split('.').filter(Boolean);
|
|
52
|
+
let cursor = config;
|
|
53
|
+
|
|
54
|
+
for (const seg of segments) {
|
|
55
|
+
if (cursor && typeof cursor === 'object' && seg in cursor) {
|
|
56
|
+
cursor = cursor[seg];
|
|
57
|
+
} else {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return cursor;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Safely sets a nested value in the Antora configuration, given a "dot path".
|
|
66
|
+
* If the file or path does not exist, it will create intermediate objects as needed.
|
|
67
|
+
* After setting the value, writes the updated YAML back to `antora.yml`.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} keyPath
|
|
70
|
+
* A dot-separated path to set (e.g. "asciidoc.attributes.latest-connect-version").
|
|
71
|
+
* @param {*} newValue
|
|
72
|
+
* The new value to assign at that path.
|
|
73
|
+
* @returns {boolean}
|
|
74
|
+
* True if it succeeded, false otherwise.
|
|
75
|
+
*/
|
|
76
|
+
function setAntoraValue(keyPath, newValue) {
|
|
77
|
+
const antoraPath = path.join(process.cwd(), 'antora.yml');
|
|
78
|
+
if (!fs.existsSync(antoraPath)) {
|
|
79
|
+
console.error('Cannot update antora.yml: file not found in project root.');
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let config;
|
|
84
|
+
try {
|
|
85
|
+
const fileContents = fs.readFileSync(antoraPath, 'utf8');
|
|
86
|
+
config = yaml.load(fileContents) || {};
|
|
87
|
+
if (typeof config !== 'object' || config === null) {
|
|
88
|
+
config = {};
|
|
89
|
+
}
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error(`Error reading/parsing antora.yml: ${err.message}`);
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Traverse/construct nested objects
|
|
96
|
+
const segments = keyPath.split('.').filter(Boolean);
|
|
97
|
+
let cursor = config;
|
|
98
|
+
for (let i = 0; i < segments.length; i++) {
|
|
99
|
+
const seg = segments[i];
|
|
100
|
+
if (i === segments.length - 1) {
|
|
101
|
+
// Last segment: assign
|
|
102
|
+
cursor[seg] = newValue;
|
|
103
|
+
} else {
|
|
104
|
+
// Intermediate: ensure object
|
|
105
|
+
if (!(seg in cursor) || typeof cursor[seg] !== 'object' || cursor[seg] === null) {
|
|
106
|
+
cursor[seg] = {};
|
|
107
|
+
}
|
|
108
|
+
cursor = cursor[seg];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Serialize back to YAML and write
|
|
113
|
+
try {
|
|
114
|
+
const newYaml = yaml.dump(config, { lineWidth: 120 });
|
|
115
|
+
fs.writeFileSync(antoraPath, newYaml, 'utf8');
|
|
116
|
+
return true;
|
|
117
|
+
} catch (err) {
|
|
118
|
+
console.error(`Error writing antora.yml: ${err.message}`);
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = {
|
|
124
|
+
loadAntoraConfig,
|
|
125
|
+
getAntoraValue,
|
|
126
|
+
setAntoraValue,
|
|
127
|
+
};
|
|
@@ -56,7 +56,7 @@ async function determineDocsBranch(operatorTag) {
|
|
|
56
56
|
if (!filtered) {
|
|
57
57
|
throw new Error(`Could not parse major.minor from ${TAG}`)
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
// We started versioning the operator in line with Redpanda core versions. But when v2.4.x was the latest version, the docs were still on 25.1 and v25.1.x of the operator was still in beta. So we need to handle this special case.
|
|
60
60
|
let branch
|
|
61
61
|
if (filtered === 'v2.4') {
|
|
62
62
|
if (ANTORA === '25.1') {
|
|
@@ -64,6 +64,7 @@ async function determineDocsBranch(operatorTag) {
|
|
|
64
64
|
} else {
|
|
65
65
|
branch = 'v/24.3'
|
|
66
66
|
}
|
|
67
|
+
// For all other versions use the v<major>.<minor> branch unless it is the current version, in which case we use 'main'.
|
|
67
68
|
} else if (filtered === `v${ANTORA}`) {
|
|
68
69
|
branch = 'main'
|
|
69
70
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redpanda-data/docs-extensions-and-macros",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0",
|
|
4
4
|
"description": "Antora extensions and macros developed for Redpanda documentation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antora",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"get-redpanda-version": "doc-tools get-redpanda-version",
|
|
21
21
|
"get-console-version": "doc-tools get-console-version",
|
|
22
22
|
"build": "antora --to-dir docs --fetch local-antora-playbook.yml",
|
|
23
|
-
"serve": "wds --node-resolve --open preview/test/ --watch --root-dir docs"
|
|
23
|
+
"serve": "wds --node-resolve --open preview/test/ --watch --root-dir docs",
|
|
24
|
+
"test": "jest"
|
|
24
25
|
},
|
|
25
26
|
"contributors": [
|
|
26
27
|
{
|
|
@@ -56,8 +57,7 @@
|
|
|
56
57
|
"./macros/glossary": "./macros/glossary.js",
|
|
57
58
|
"./macros/rp-connect-components": "./macros/rp-connect-components.js",
|
|
58
59
|
"./macros/config-ref": "./macros/config-ref.js",
|
|
59
|
-
"./macros/helm-ref": "./macros/helm-ref.js"
|
|
60
|
-
"./macros/data-template": "./macros/data-template.js"
|
|
60
|
+
"./macros/helm-ref": "./macros/helm-ref.js"
|
|
61
61
|
},
|
|
62
62
|
"files": [
|
|
63
63
|
"extensions",
|
|
@@ -101,6 +101,7 @@
|
|
|
101
101
|
"devDependencies": {
|
|
102
102
|
"@antora/cli": "3.1.4",
|
|
103
103
|
"@antora/site-generator": "3.1.4",
|
|
104
|
-
"@web/dev-server": "^0.2.5"
|
|
104
|
+
"@web/dev-server": "^0.2.5",
|
|
105
|
+
"jest": "^29.7.0"
|
|
105
106
|
}
|
|
106
107
|
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const handlebars = require('handlebars');
|
|
6
|
+
const yaml = require('yaml');
|
|
7
|
+
const helpers = require('./helpers');
|
|
8
|
+
|
|
9
|
+
// Register each helper under handlebars, verifying that it’s a function
|
|
10
|
+
Object.entries(helpers).forEach(([name, fn]) => {
|
|
11
|
+
if (typeof fn !== 'function') {
|
|
12
|
+
console.error(`❌ Helper "${name}" is not a function`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
handlebars.registerHelper(name, fn);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Default “main” template (connector.hbs) which invokes partials {{> intro}}, {{> fields}}, {{> examples}}
|
|
19
|
+
const DEFAULT_TEMPLATE = path.resolve(__dirname, './templates/connector.hbs');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Reads a file at `filePath` and registers it as a Handlebars partial called `name`.
|
|
23
|
+
* Throws if the file cannot be read.
|
|
24
|
+
*/
|
|
25
|
+
function registerPartial(name, filePath) {
|
|
26
|
+
const resolved = path.resolve(filePath);
|
|
27
|
+
let source;
|
|
28
|
+
try {
|
|
29
|
+
source = fs.readFileSync(resolved, 'utf8');
|
|
30
|
+
} catch (err) {
|
|
31
|
+
throw new Error(`Unable to read "${name}" template at ${resolved}: ${err.message}`);
|
|
32
|
+
}
|
|
33
|
+
handlebars.registerPartial(name, source);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Deep-merge `overrides` into `target`. Only 'description', 'type',
|
|
38
|
+
* plus nested array/object entries get overridden; other keys remain intact.
|
|
39
|
+
*/
|
|
40
|
+
function mergeOverrides(target, overrides) {
|
|
41
|
+
if (!overrides || typeof overrides !== 'object') return target;
|
|
42
|
+
if (!target || typeof target !== 'object') {
|
|
43
|
+
throw new Error('Target must be a valid object');
|
|
44
|
+
}
|
|
45
|
+
for (const key in overrides) {
|
|
46
|
+
if (Array.isArray(target[key]) && Array.isArray(overrides[key])) {
|
|
47
|
+
// Merge two parallel arrays by matching items on `.name`
|
|
48
|
+
target[key] = target[key].map(item => {
|
|
49
|
+
const overrideItem = overrides[key].find(o => o.name === item.name);
|
|
50
|
+
if (overrideItem) {
|
|
51
|
+
// Overwrite description/type if present
|
|
52
|
+
['description', 'type'].forEach(field => {
|
|
53
|
+
if (Object.hasOwn(overrideItem, field)) {
|
|
54
|
+
item[field] = overrideItem[field];
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
// Recurse for nested children
|
|
58
|
+
item = mergeOverrides(item, overrideItem);
|
|
59
|
+
}
|
|
60
|
+
return item;
|
|
61
|
+
});
|
|
62
|
+
} else if (
|
|
63
|
+
typeof target[key] === 'object' &&
|
|
64
|
+
typeof overrides[key] === 'object' &&
|
|
65
|
+
!Array.isArray(target[key]) &&
|
|
66
|
+
!Array.isArray(overrides[key])
|
|
67
|
+
) {
|
|
68
|
+
// Deep-merge plain objects
|
|
69
|
+
target[key] = mergeOverrides(target[key], overrides[key]);
|
|
70
|
+
} else if (['description', 'type'].includes(key) && Object.hasOwn(overrides, key)) {
|
|
71
|
+
// Overwrite the primitive
|
|
72
|
+
target[key] = overrides[key];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return target;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function generateRpcnConnectorDocs(options) {
|
|
79
|
+
const {
|
|
80
|
+
data,
|
|
81
|
+
overrides,
|
|
82
|
+
template, // main Handlebars template (for full-draft mode)
|
|
83
|
+
templateIntro,
|
|
84
|
+
templateFields,
|
|
85
|
+
templateExamples,
|
|
86
|
+
writeFullDrafts
|
|
87
|
+
} = options;
|
|
88
|
+
|
|
89
|
+
// Read connector index (JSON or YAML)
|
|
90
|
+
const raw = fs.readFileSync(data, 'utf8');
|
|
91
|
+
const ext = path.extname(data).toLowerCase();
|
|
92
|
+
const dataObj = ext === '.json' ? JSON.parse(raw) : yaml.parse(raw);
|
|
93
|
+
|
|
94
|
+
// Apply overrides if provided
|
|
95
|
+
if (overrides) {
|
|
96
|
+
const ovRaw = fs.readFileSync(overrides, 'utf8');
|
|
97
|
+
const ovObj = JSON.parse(ovRaw);
|
|
98
|
+
mergeOverrides(dataObj, ovObj);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Compile the “main” template (used when writeFullDrafts = true)
|
|
102
|
+
const compiledTemplate = handlebars.compile(fs.readFileSync(template, 'utf8'));
|
|
103
|
+
|
|
104
|
+
// Determine which templates to use for “fields” and “examples”
|
|
105
|
+
// If templateFields is not provided, fall back to the single `template`.
|
|
106
|
+
// If templateExamples is not provided, skip examples entirely.
|
|
107
|
+
const fieldsTemplatePath = templateFields || template;
|
|
108
|
+
const examplesTemplatePath = templateExamples || null;
|
|
109
|
+
|
|
110
|
+
// Register partials
|
|
111
|
+
if (!writeFullDrafts) {
|
|
112
|
+
if (fieldsTemplatePath) {
|
|
113
|
+
registerPartial('fields', fieldsTemplatePath);
|
|
114
|
+
}
|
|
115
|
+
if (examplesTemplatePath) {
|
|
116
|
+
registerPartial('examples', examplesTemplatePath);
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
registerPartial('intro', templateIntro);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const outputRoot = path.resolve(process.cwd(), 'modules/components/partials');
|
|
123
|
+
const fieldsOutRoot = path.join(outputRoot, 'fields');
|
|
124
|
+
const examplesOutRoot = path.join(outputRoot, 'examples');
|
|
125
|
+
const draftsRoot = path.join(outputRoot, 'drafts');
|
|
126
|
+
|
|
127
|
+
if (!writeFullDrafts) {
|
|
128
|
+
fs.mkdirSync(fieldsOutRoot, { recursive: true });
|
|
129
|
+
fs.mkdirSync(examplesOutRoot, { recursive: true });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let partialsWritten = 0;
|
|
133
|
+
let draftsWritten = 0;
|
|
134
|
+
const partialFiles = [];
|
|
135
|
+
const draftFiles = [];
|
|
136
|
+
|
|
137
|
+
for (const [type, items] of Object.entries(dataObj)) {
|
|
138
|
+
if (!Array.isArray(items)) continue;
|
|
139
|
+
|
|
140
|
+
for (const item of items) {
|
|
141
|
+
if (!item.name) continue;
|
|
142
|
+
const name = item.name;
|
|
143
|
+
|
|
144
|
+
if (!writeFullDrafts) {
|
|
145
|
+
// Render fields using the registered “fields” partial
|
|
146
|
+
const fieldsOut = handlebars
|
|
147
|
+
.compile('{{> fields children=config.children}}')(item);
|
|
148
|
+
|
|
149
|
+
// Render examples only if an examples template was provided
|
|
150
|
+
let examplesOut = '';
|
|
151
|
+
if (examplesTemplatePath) {
|
|
152
|
+
examplesOut = handlebars
|
|
153
|
+
.compile('{{> examples examples=examples}}')(item);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (fieldsOut.trim()) {
|
|
157
|
+
const fPath = path.join(fieldsOutRoot, type, `${name}.adoc`);
|
|
158
|
+
fs.mkdirSync(path.dirname(fPath), { recursive: true });
|
|
159
|
+
fs.writeFileSync(fPath, fieldsOut);
|
|
160
|
+
partialsWritten++;
|
|
161
|
+
partialFiles.push(path.relative(process.cwd(), fPath));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (examplesOut.trim()) {
|
|
165
|
+
const ePath = path.join(examplesOutRoot, type, `${name}.adoc`);
|
|
166
|
+
fs.mkdirSync(path.dirname(ePath), { recursive: true });
|
|
167
|
+
fs.writeFileSync(ePath, examplesOut);
|
|
168
|
+
partialsWritten++;
|
|
169
|
+
partialFiles.push(path.relative(process.cwd(), ePath));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (writeFullDrafts) {
|
|
174
|
+
let content;
|
|
175
|
+
try {
|
|
176
|
+
content = compiledTemplate(item);
|
|
177
|
+
} catch (err) {
|
|
178
|
+
throw new Error(`Template render failed for component "${name}": ${err.message}`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const draftSubdir = name === 'gateway'
|
|
182
|
+
? path.join(draftsRoot, 'cloud-only')
|
|
183
|
+
: draftsRoot;
|
|
184
|
+
|
|
185
|
+
const destFile = path.join(draftSubdir, `${name}.adoc`);
|
|
186
|
+
fs.mkdirSync(path.dirname(destFile), { recursive: true });
|
|
187
|
+
fs.writeFileSync(destFile, content, 'utf8');
|
|
188
|
+
draftsWritten++;
|
|
189
|
+
draftFiles.push(path.relative(process.cwd(), destFile));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
partialsWritten,
|
|
196
|
+
draftsWritten,
|
|
197
|
+
partialFiles,
|
|
198
|
+
draftFiles
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
module.exports = {
|
|
203
|
+
generateRpcnConnectorDocs,
|
|
204
|
+
mergeOverrides
|
|
205
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const buildConfigYaml = require('./buildConfigYaml.js');
|
|
2
|
+
const handlebars = require('handlebars');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Handlebars helper “advancedConfig”. Omits only deprecated.
|
|
6
|
+
*
|
|
7
|
+
* Usage in template:
|
|
8
|
+
* {{advancedConfig this.type this.name this.config.children}}
|
|
9
|
+
*/
|
|
10
|
+
module.exports = function advancedConfig(type, connectorName, children) {
|
|
11
|
+
if (typeof type !== 'string' || typeof connectorName !== 'string' || !Array.isArray(children)) {
|
|
12
|
+
return '';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const yamlText = buildConfigYaml(type, connectorName, children, /*includeAdvanced=*/ true);
|
|
16
|
+
return new handlebars.SafeString(yamlText);
|
|
17
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const renderLeafField = require('./renderLeafField');
|
|
2
|
+
const renderObjectField = require('./renderObjectField');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Builds either “Common” or “Advanced” YAML for one connector.
|
|
6
|
+
*
|
|
7
|
+
* - type = “input” or “output” (or whatever type)
|
|
8
|
+
* - connectorName = such as “amqp_1”
|
|
9
|
+
* - children = the array of field‐definitions (entry.config.children)
|
|
10
|
+
* - includeAdvanced = if false → only fields where is_advanced !== true
|
|
11
|
+
* if true → all fields (except deprecated)
|
|
12
|
+
*
|
|
13
|
+
* Structure produced:
|
|
14
|
+
*
|
|
15
|
+
* type:
|
|
16
|
+
* label: ""
|
|
17
|
+
* connectorName:
|
|
18
|
+
* ...child fields (with comments for “no default”)
|
|
19
|
+
*/
|
|
20
|
+
module.exports = function buildConfigYaml(type, connectorName, children, includeAdvanced) {
|
|
21
|
+
const lines = [];
|
|
22
|
+
|
|
23
|
+
// “type:” top‐level
|
|
24
|
+
lines.push(`${type}:`);
|
|
25
|
+
|
|
26
|
+
// Two‐space indent for “label”
|
|
27
|
+
lines.push(` label: ""`);
|
|
28
|
+
|
|
29
|
+
// Two‐space indent for connectorName heading
|
|
30
|
+
lines.push(` ${connectorName}:`);
|
|
31
|
+
|
|
32
|
+
// Four‐space indent for children
|
|
33
|
+
const baseIndent = 4;
|
|
34
|
+
children.forEach(field => {
|
|
35
|
+
if (field.is_deprecated) {
|
|
36
|
+
return; // skip deprecated fields
|
|
37
|
+
}
|
|
38
|
+
if (!includeAdvanced && field.is_advanced) {
|
|
39
|
+
return; // skip advanced fields in “common” mode
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (field.type === 'object' && Array.isArray(field.children)) {
|
|
43
|
+
// Render nested object
|
|
44
|
+
const nestedLines = renderObjectField(field, baseIndent);
|
|
45
|
+
lines.push(...nestedLines);
|
|
46
|
+
} else {
|
|
47
|
+
// Render a scalar or array leaf
|
|
48
|
+
lines.push(renderLeafField(field, baseIndent));
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return lines.join('\n');
|
|
53
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const buildConfigYaml = require('./buildConfigYaml.js');
|
|
2
|
+
const handlebars = require('handlebars');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Handlebars helper “commonConfig”. Omits deprecated + advanced.
|
|
6
|
+
*
|
|
7
|
+
* Usage in template:
|
|
8
|
+
* {{commonConfig this.type this.name this.config.children}}
|
|
9
|
+
*/
|
|
10
|
+
module.exports = function commonConfig(type, connectorName, children) {
|
|
11
|
+
if (typeof type !== 'string' || !type.trim()) {
|
|
12
|
+
console.warn('commonConfig: type must be a non-empty string');
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
if (typeof connectorName !== 'string' || !connectorName.trim()) {
|
|
16
|
+
console.warn('commonConfig: connectorName must be a non-empty string');
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
if (!Array.isArray(children)) {
|
|
20
|
+
console.warn('commonConfig: children must be an array');
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const yamlText = buildConfigYaml(type, connectorName, children, /*includeAdvanced=*/ false);
|
|
26
|
+
return new handlebars.SafeString(yamlText);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error('Error in commonConfig helper:', error);
|
|
29
|
+
return new handlebars.SafeString('<!-- Error generating configuration -->');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
uppercase: require('./uppercase.js'),
|
|
5
|
+
eq: require('./eq.js'),
|
|
6
|
+
ne: require('./ne.js'),
|
|
7
|
+
join: require('./join.js'),
|
|
8
|
+
or: require('./or.js'),
|
|
9
|
+
toYaml: require('./toYaml.js'),
|
|
10
|
+
isObject: require('./isObject.js'),
|
|
11
|
+
renderYamlList: require('./renderYamlList.js'),
|
|
12
|
+
renderConnectFields: require('./renderConnectFields.js'),
|
|
13
|
+
renderConnectExamples: require('./renderConnectExamples.js'),
|
|
14
|
+
renderLeafField: require('./renderLeafField.js'),
|
|
15
|
+
renderObjectField: require('./renderObjectField.js'),
|
|
16
|
+
buildConfigYaml: require('./buildConfigYaml.js'),
|
|
17
|
+
commonConfig: require('./commonConfig.js'),
|
|
18
|
+
advancedConfig: require('./advancedConfig.js'),
|
|
19
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = v => v !== null && typeof v === 'object' && !Array.isArray(v)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const handlebars = require('handlebars');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Renders a list of examples.
|
|
5
|
+
*
|
|
6
|
+
* @param {Array<Object>} examples - An array of example objects.
|
|
7
|
+
* @returns {handlebars.SafeString} The rendered SafeString containing the examples.
|
|
8
|
+
*/
|
|
9
|
+
module.exports = function renderConnectExamples(examples) {
|
|
10
|
+
if (!examples || !Array.isArray(examples) || examples.length === 0) {
|
|
11
|
+
return '';
|
|
12
|
+
}
|
|
13
|
+
let output = '';
|
|
14
|
+
examples.forEach(example => {
|
|
15
|
+
if (example.title) {
|
|
16
|
+
const sanitizedTitle = example.title.replace(/[=]/g, '\\=');
|
|
17
|
+
output += `=== ${sanitizedTitle}\n\n`;
|
|
18
|
+
}
|
|
19
|
+
if (example.summary) {
|
|
20
|
+
output += `${example.summary}\n\n`;
|
|
21
|
+
}
|
|
22
|
+
if (example.config) {
|
|
23
|
+
if (typeof example.config !== 'string') {
|
|
24
|
+
console.warn('Example config must be a string, skipping');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const configContent = example.config.trim();
|
|
28
|
+
if (configContent.includes('----')) {
|
|
29
|
+
console.warn('Example config contains AsciiDoc delimiters, this may break rendering');
|
|
30
|
+
}
|
|
31
|
+
output += '[source,yaml]\n----\n';
|
|
32
|
+
output += configContent + '\n';
|
|
33
|
+
output += '----\n\n';
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return new handlebars.SafeString(output);
|
|
37
|
+
}
|