@redpanda-data/docs-extensions-and-macros 4.10.3 → 4.10.4
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/bin/doc-tools.js +92 -99
- package/package.json +1 -1
- package/tools/property-extractor/compare-properties.js +4 -4
- package/tools/property-extractor/generate-handlebars-docs.js +131 -441
- package/tools/property-extractor/helpers/anchorName.js +7 -0
- package/tools/property-extractor/helpers/index.js +1 -0
- package/tools/property-extractor/property_extractor.py +62 -2
- package/tools/property-extractor/templates/deprecated-property.hbs +1 -7
- package/tools/property-extractor/templates/property.hbs +55 -17
- package/tools/property-extractor/templates/topic-property-mappings.hbs +13 -0
- package/tools/property-extractor/templates/topic-property.hbs +41 -2
- package/tools/property-extractor/templates/property-cloud.hbs +0 -100
- package/tools/property-extractor/templates/property-page-with-includes.hbs +0 -17
- package/tools/property-extractor/templates/property-page.hbs +0 -22
- package/tools/property-extractor/templates/topic-property-cloud.hbs +0 -94
|
@@ -7,14 +7,17 @@ const helpers = require('./helpers');
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Handlebars documentation generator for Redpanda configuration properties.
|
|
10
|
-
*
|
|
10
|
+
*
|
|
11
11
|
* Supports custom template overrides using environment variables:
|
|
12
|
-
* -
|
|
13
|
-
* - TEMPLATE_PROPERTY: Individual property section template
|
|
12
|
+
* - TEMPLATE_PROPERTY: Individual property section template
|
|
14
13
|
* - TEMPLATE_TOPIC_PROPERTY: Individual topic property section template
|
|
15
14
|
* - TEMPLATE_DEPRECATED_PROPERTY: Individual deprecated property section template
|
|
16
15
|
* - TEMPLATE_DEPRECATED: Deprecated properties page template
|
|
17
|
-
*
|
|
16
|
+
*
|
|
17
|
+
* Behavior flags (environment variables):
|
|
18
|
+
* - GENERATE_PARTIALS=1 Generate consolidated property partials and deprecated partials
|
|
19
|
+
* - OUTPUT_PARTIALS_DIR=<p> Destination for consolidated partials (required if GENERATE_PARTIALS=1)
|
|
20
|
+
*
|
|
18
21
|
* CLI Usage: node generate-handlebars-docs.js <input-file> <output-dir>
|
|
19
22
|
*/
|
|
20
23
|
|
|
@@ -44,81 +47,6 @@ function isObjectStorageProperty(prop) {
|
|
|
44
47
|
);
|
|
45
48
|
}
|
|
46
49
|
|
|
47
|
-
/**
|
|
48
|
-
* Configuration mapping for different property types
|
|
49
|
-
*/
|
|
50
|
-
const PROPERTY_CONFIG = {
|
|
51
|
-
broker: {
|
|
52
|
-
pageTitle: 'Broker Configuration Properties',
|
|
53
|
-
pageAliases: ['reference:node-properties.adoc', 'reference:node-configuration-sample.adoc'],
|
|
54
|
-
description: 'Reference of broker configuration properties.',
|
|
55
|
-
intro: `Broker configuration properties are applied individually to each broker in a cluster. You can find and modify these properties in the \`redpanda.yaml\` configuration file.
|
|
56
|
-
|
|
57
|
-
For information on how to edit broker properties, see xref:manage:cluster-maintenance/node-property-configuration.adoc[].
|
|
58
|
-
|
|
59
|
-
NOTE: All broker properties require that you restart Redpanda for any update to take effect.`,
|
|
60
|
-
sectionTitle: 'Broker configuration',
|
|
61
|
-
groups: [
|
|
62
|
-
{
|
|
63
|
-
filter: (prop) => prop.config_scope === 'broker' && !prop.is_deprecated
|
|
64
|
-
}
|
|
65
|
-
],
|
|
66
|
-
filename: 'broker-properties.adoc'
|
|
67
|
-
},
|
|
68
|
-
cluster: {
|
|
69
|
-
pageTitle: 'Cluster Configuration Properties',
|
|
70
|
-
pageAliases: ['reference:tunable-properties.adoc', 'reference:cluster-properties.adoc'],
|
|
71
|
-
description: 'Cluster configuration properties list.',
|
|
72
|
-
intro: `Cluster configuration properties are the same for all brokers in a cluster, and are set at the cluster level.
|
|
73
|
-
|
|
74
|
-
For information on how to edit cluster properties, see xref:manage:cluster-maintenance/cluster-property-configuration.adoc[] or xref:manage:kubernetes/k-cluster-property-configuration.adoc[].
|
|
75
|
-
|
|
76
|
-
NOTE: Some cluster properties require that you restart the cluster for any updates to take effect. See the specific property details to identify whether or not a restart is required.`,
|
|
77
|
-
sectionTitle: 'Cluster configuration',
|
|
78
|
-
groups: [
|
|
79
|
-
{
|
|
80
|
-
filter: (prop) => prop.config_scope === 'cluster' && !prop.is_deprecated && !isObjectStorageProperty(prop)
|
|
81
|
-
}
|
|
82
|
-
],
|
|
83
|
-
filename: 'cluster-properties.adoc'
|
|
84
|
-
},
|
|
85
|
-
'object-storage': {
|
|
86
|
-
pageTitle: 'Object Storage Properties',
|
|
87
|
-
description: 'Reference of object storage properties.',
|
|
88
|
-
intro: `Object storage properties are a type of cluster property. For information on how to edit cluster properties, see xref:manage:cluster-maintenance/cluster-property-configuration.adoc[].
|
|
89
|
-
|
|
90
|
-
NOTE: Some object storage properties require that you restart the cluster for any updates to take effect. See the specific property details to identify whether or not a restart is required.`,
|
|
91
|
-
sectionTitle: 'Object storage configuration',
|
|
92
|
-
sectionIntro: 'Object storage properties should only be set if you enable xref:manage:tiered-storage.adoc[Tiered Storage].',
|
|
93
|
-
groups: [
|
|
94
|
-
{
|
|
95
|
-
filter: (prop) => isObjectStorageProperty(prop) && !prop.is_deprecated
|
|
96
|
-
}
|
|
97
|
-
],
|
|
98
|
-
filename: 'object-storage-properties.adoc'
|
|
99
|
-
},
|
|
100
|
-
topic: {
|
|
101
|
-
pageTitle: 'Topic Configuration Properties',
|
|
102
|
-
pageAliases: ['reference:topic-properties.adoc'],
|
|
103
|
-
description: 'Reference of topic configuration properties.',
|
|
104
|
-
intro: `A topic-level property sets a Redpanda or Kafka configuration for a particular topic.
|
|
105
|
-
|
|
106
|
-
Many topic-level properties have corresponding xref:manage:cluster-maintenance/cluster-property-configuration.adoc[cluster properties] that set a default value for all topics of a cluster. To customize the value for a topic, you can set a topic-level property that overrides the value of the corresponding cluster property.
|
|
107
|
-
|
|
108
|
-
NOTE: All topic properties take effect immediately after being set.`,
|
|
109
|
-
sectionTitle: 'Topic configuration',
|
|
110
|
-
groups: [
|
|
111
|
-
{
|
|
112
|
-
filter: (prop) => prop.config_scope === 'topic' && !prop.is_deprecated,
|
|
113
|
-
template: 'topic-property'
|
|
114
|
-
}
|
|
115
|
-
],
|
|
116
|
-
filename: 'topic-properties.adoc'
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
// "src/v/kafka/server/handlers/topics/types.cc": "topic"
|
|
121
|
-
|
|
122
50
|
/**
|
|
123
51
|
* Gets template path, checking environment variables for custom paths first
|
|
124
52
|
*/
|
|
@@ -134,251 +62,99 @@ function getTemplatePath(defaultPath, envVar) {
|
|
|
134
62
|
/**
|
|
135
63
|
* Register Handlebars partials used to render property documentation.
|
|
136
64
|
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* - "property"
|
|
140
|
-
* - "topic-property" (uses cloud-aware `topic-property-cloud.hbs` when enabled)
|
|
65
|
+
* Registers:
|
|
66
|
+
* - "property"
|
|
67
|
+
* - "topic-property"
|
|
141
68
|
* - "deprecated-property"
|
|
142
|
-
*
|
|
143
|
-
* @param {boolean} [hasCloudSupport=false] - If true, select cloud-aware templates for the `property` and `topic-property` partials.
|
|
144
|
-
* @throws {Error} If any required template file is missing or cannot be read; errors are rethrown after logging.
|
|
145
69
|
*/
|
|
146
|
-
function registerPartials(
|
|
70
|
+
function registerPartials() {
|
|
147
71
|
const templatesDir = path.join(__dirname, 'templates');
|
|
148
|
-
|
|
72
|
+
|
|
149
73
|
try {
|
|
150
|
-
console.log(
|
|
151
|
-
|
|
152
|
-
// Register property partial (choose cloud or regular version)
|
|
153
|
-
const propertyTemplateFile = hasCloudSupport ? 'property-cloud.hbs' : 'property.hbs';
|
|
74
|
+
console.log('📝 Registering Handlebars templates');
|
|
75
|
+
|
|
154
76
|
const propertyTemplatePath = getTemplatePath(
|
|
155
|
-
path.join(templatesDir,
|
|
77
|
+
path.join(templatesDir, 'property.hbs'),
|
|
156
78
|
'TEMPLATE_PROPERTY'
|
|
157
79
|
);
|
|
158
|
-
|
|
159
80
|
if (!fs.existsSync(propertyTemplatePath)) {
|
|
160
81
|
throw new Error(`Property template not found: ${propertyTemplatePath}`);
|
|
161
82
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
handlebars.registerPartial('property', propertyTemplate);
|
|
165
|
-
console.log(`✅ Registered property template: ${propertyTemplateFile}`);
|
|
166
|
-
|
|
167
|
-
// Register topic property partial (choose cloud or regular version)
|
|
168
|
-
const topicPropertyTemplateFile = hasCloudSupport ? 'topic-property-cloud.hbs' : 'topic-property.hbs';
|
|
83
|
+
handlebars.registerPartial('property', fs.readFileSync(propertyTemplatePath, 'utf8'));
|
|
84
|
+
|
|
169
85
|
const topicPropertyTemplatePath = getTemplatePath(
|
|
170
|
-
path.join(templatesDir,
|
|
86
|
+
path.join(templatesDir, 'topic-property.hbs'),
|
|
171
87
|
'TEMPLATE_TOPIC_PROPERTY'
|
|
172
88
|
);
|
|
173
|
-
|
|
174
89
|
if (!fs.existsSync(topicPropertyTemplatePath)) {
|
|
175
90
|
throw new Error(`Topic property template not found: ${topicPropertyTemplatePath}`);
|
|
176
91
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
handlebars.registerPartial('topic-property', topicPropertyTemplate);
|
|
180
|
-
console.log(`✅ Registered topic property template: ${topicPropertyTemplateFile}`);
|
|
181
|
-
|
|
182
|
-
// Register deprecated property partial
|
|
92
|
+
handlebars.registerPartial('topic-property', fs.readFileSync(topicPropertyTemplatePath, 'utf8'));
|
|
93
|
+
|
|
183
94
|
const deprecatedPropertyTemplatePath = getTemplatePath(
|
|
184
95
|
path.join(templatesDir, 'deprecated-property.hbs'),
|
|
185
96
|
'TEMPLATE_DEPRECATED_PROPERTY'
|
|
186
97
|
);
|
|
187
|
-
|
|
188
98
|
if (!fs.existsSync(deprecatedPropertyTemplatePath)) {
|
|
189
99
|
throw new Error(`Deprecated property template not found: ${deprecatedPropertyTemplatePath}`);
|
|
190
100
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
console.log(`✅ Registered deprecated property template`);
|
|
195
|
-
|
|
101
|
+
handlebars.registerPartial('deprecated-property', fs.readFileSync(deprecatedPropertyTemplatePath, 'utf8'));
|
|
102
|
+
|
|
103
|
+
console.log('✅ Registered all partials');
|
|
196
104
|
} catch (error) {
|
|
197
105
|
console.error('❌ Failed to register Handlebars templates:');
|
|
198
|
-
console.error(`
|
|
199
|
-
console.error(' This indicates missing or corrupted template files.');
|
|
200
|
-
console.error(' Check that all .hbs files exist in tools/property-extractor/templates/');
|
|
106
|
+
console.error(` ${error.message}`);
|
|
201
107
|
throw error;
|
|
202
108
|
}
|
|
203
109
|
}
|
|
204
110
|
|
|
205
|
-
/**
|
|
206
|
-
* Generates documentation for a specific property type
|
|
207
|
-
*/
|
|
208
|
-
function generatePropertyDocs(properties, config, outputDir) {
|
|
209
|
-
// Check if partials are being generated to determine which template to use
|
|
210
|
-
const useIncludes = process.env.GENERATE_PARTIALS === '1';
|
|
211
|
-
|
|
212
|
-
let templatePath;
|
|
213
|
-
if (useIncludes) {
|
|
214
|
-
// Use the include-based template when partials are also being generated
|
|
215
|
-
templatePath = getTemplatePath(
|
|
216
|
-
path.join(__dirname, 'templates', 'property-page-with-includes.hbs'),
|
|
217
|
-
'TEMPLATE_PROPERTY_PAGE_WITH_INCLUDES'
|
|
218
|
-
);
|
|
219
|
-
} else {
|
|
220
|
-
// Use the standard template for full content
|
|
221
|
-
templatePath = getTemplatePath(
|
|
222
|
-
path.join(__dirname, 'templates', 'property-page.hbs'),
|
|
223
|
-
'TEMPLATE_PROPERTY_PAGE'
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const template = handlebars.compile(fs.readFileSync(templatePath, 'utf8'));
|
|
228
|
-
|
|
229
|
-
if (useIncludes) {
|
|
230
|
-
// For include-based pages, we need minimal data - just page metadata and filename for include
|
|
231
|
-
const data = {
|
|
232
|
-
...config,
|
|
233
|
-
filename: config.filename.replace('.adoc', '') // Remove .adoc extension for include
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
const output = template(data);
|
|
237
|
-
const outputPath = path.join(outputDir, config.filename);
|
|
238
|
-
|
|
239
|
-
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
240
|
-
fs.writeFileSync(outputPath, output, 'utf8');
|
|
241
|
-
|
|
242
|
-
console.log(`✅ Generated include-based page ${outputPath}`);
|
|
243
|
-
|
|
244
|
-
// Count properties for this type
|
|
245
|
-
const typeCount = Object.values(properties).filter(prop => {
|
|
246
|
-
return config.groups.some(group => group.filter(prop));
|
|
247
|
-
}).length;
|
|
248
|
-
|
|
249
|
-
return typeCount;
|
|
250
|
-
} else {
|
|
251
|
-
// Filter and group properties according to configuration
|
|
252
|
-
const groups = config.groups.map(group => {
|
|
253
|
-
const filteredProperties = Object.values(properties)
|
|
254
|
-
.filter(prop => group.filter(prop))
|
|
255
|
-
.sort((a, b) => String(a.name || '').localeCompare(String(b.name || '')));
|
|
256
|
-
|
|
257
|
-
return {
|
|
258
|
-
title: group.title,
|
|
259
|
-
intro: group.intro,
|
|
260
|
-
properties: filteredProperties,
|
|
261
|
-
template: group.template || 'property' // Default to 'property' template
|
|
262
|
-
};
|
|
263
|
-
}).filter(group => group.properties.length > 0);
|
|
264
|
-
|
|
265
|
-
const data = {
|
|
266
|
-
...config,
|
|
267
|
-
groups
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
const output = template(data);
|
|
271
|
-
const outputPath = path.join(outputDir, config.filename);
|
|
272
|
-
|
|
273
|
-
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
274
|
-
fs.writeFileSync(outputPath, output, 'utf8');
|
|
275
|
-
|
|
276
|
-
console.log(`✅ Generated full content page ${outputPath}`);
|
|
277
|
-
return groups.reduce((total, group) => total + group.properties.length, 0);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
111
|
/**
|
|
282
112
|
* Generate consolidated AsciiDoc partials for properties grouped by type.
|
|
283
|
-
*
|
|
284
|
-
* Creates separate .adoc files for each property type (cluster-properties.adoc,
|
|
285
|
-
* topic-properties.adoc, object-storage-properties.adoc, broker-properties.adoc)
|
|
286
|
-
* containing all properties of that type using the appropriate templates.
|
|
287
|
-
*
|
|
288
|
-
* @param {Object} properties - Map of properties (property name → property object).
|
|
289
|
-
* @param {string} partialsDir - Directory where consolidated property files will be written.
|
|
290
|
-
* @param {boolean} [hasCloudSupport=false] - If true, use cloud-aware templates.
|
|
291
|
-
* @returns {number} The total number of properties included in the consolidated partials.
|
|
292
113
|
*/
|
|
293
|
-
function generatePropertyPartials(properties, partialsDir
|
|
114
|
+
function generatePropertyPartials(properties, partialsDir) {
|
|
294
115
|
console.log(`📝 Generating consolidated property partials in ${partialsDir}…`);
|
|
295
|
-
|
|
296
|
-
// Use the appropriate template based on cloud support
|
|
297
|
-
const templateName = hasCloudSupport ? 'property-cloud' : 'property';
|
|
298
|
-
const templatePath = getTemplatePath(
|
|
299
|
-
path.join(__dirname, 'templates', `${templateName}.hbs`),
|
|
300
|
-
'TEMPLATE_PROPERTY'
|
|
301
|
-
);
|
|
302
|
-
const template = handlebars.compile(fs.readFileSync(templatePath, 'utf8'));
|
|
303
116
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
'TEMPLATE_TOPIC_PROPERTY'
|
|
117
|
+
const propertyTemplate = handlebars.compile(
|
|
118
|
+
fs.readFileSync(getTemplatePath(path.join(__dirname, 'templates', 'property.hbs'), 'TEMPLATE_PROPERTY'), 'utf8')
|
|
119
|
+
);
|
|
120
|
+
const topicTemplate = handlebars.compile(
|
|
121
|
+
fs.readFileSync(getTemplatePath(path.join(__dirname, 'templates', 'topic-property.hbs'), 'TEMPLATE_TOPIC_PROPERTY'), 'utf8')
|
|
309
122
|
);
|
|
310
|
-
const topicTemplate = handlebars.compile(fs.readFileSync(topicTemplatePath, 'utf8'));
|
|
311
123
|
|
|
312
|
-
// Create the main partials directory
|
|
313
124
|
const propertiesPartialsDir = path.join(partialsDir, 'properties');
|
|
314
125
|
fs.mkdirSync(propertiesPartialsDir, { recursive: true });
|
|
315
|
-
|
|
316
|
-
// Group properties by type
|
|
317
|
-
const propertyGroups = {
|
|
318
|
-
cluster: [],
|
|
319
|
-
topic: [],
|
|
320
|
-
broker: [],
|
|
321
|
-
'object-storage': []
|
|
322
|
-
};
|
|
323
126
|
|
|
324
|
-
|
|
127
|
+
const propertyGroups = { cluster: [], topic: [], broker: [], 'object-storage': [] };
|
|
128
|
+
|
|
325
129
|
Object.values(properties).forEach(prop => {
|
|
326
|
-
if (!prop.name || !prop.config_scope) return;
|
|
327
|
-
|
|
328
|
-
if (prop.config_scope === '
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
propertyGroups.
|
|
332
|
-
} else if (prop.config_scope === 'cluster') {
|
|
333
|
-
// Check if it's an object storage property
|
|
334
|
-
if (isObjectStorageProperty(prop)) {
|
|
335
|
-
propertyGroups['object-storage'].push(prop);
|
|
336
|
-
} else {
|
|
337
|
-
propertyGroups.cluster.push(prop);
|
|
338
|
-
}
|
|
130
|
+
if (!prop.name || !prop.config_scope) return;
|
|
131
|
+
if (prop.config_scope === 'topic') propertyGroups.topic.push(prop);
|
|
132
|
+
else if (prop.config_scope === 'broker') propertyGroups.broker.push(prop);
|
|
133
|
+
else if (prop.config_scope === 'cluster') {
|
|
134
|
+
if (isObjectStorageProperty(prop)) propertyGroups['object-storage'].push(prop);
|
|
135
|
+
else propertyGroups.cluster.push(prop);
|
|
339
136
|
}
|
|
340
137
|
});
|
|
341
138
|
|
|
342
139
|
let totalCount = 0;
|
|
343
|
-
|
|
344
|
-
// Generate consolidated partials for each property type
|
|
140
|
+
|
|
345
141
|
Object.entries(propertyGroups).forEach(([type, props]) => {
|
|
346
142
|
if (props.length === 0) return;
|
|
347
|
-
|
|
348
|
-
// Sort properties by name
|
|
349
143
|
props.sort((a, b) => String(a.name || '').localeCompare(String(b.name || '')));
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
const selectedTemplate = type === 'topic' ? topicTemplate : template;
|
|
353
|
-
|
|
354
|
-
// Generate content for all properties of this type
|
|
355
|
-
const content = props.map(prop => selectedTemplate(prop)).join('\n');
|
|
356
|
-
|
|
357
|
-
// Write the consolidated file
|
|
144
|
+
const selectedTemplate = type === 'topic' ? topicTemplate : propertyTemplate;
|
|
145
|
+
const content = props.map(p => selectedTemplate(p)).join('\n');
|
|
358
146
|
const filename = `${type}-properties.adoc`;
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
console.log(`✅ Generated ${outputPath} with ${props.length} properties`);
|
|
147
|
+
fs.writeFileSync(path.join(propertiesPartialsDir, filename), content, 'utf8');
|
|
148
|
+
console.log(`✅ Generated ${filename} (${props.length} properties)`);
|
|
363
149
|
totalCount += props.length;
|
|
364
150
|
});
|
|
365
|
-
|
|
366
|
-
console.log(`✅
|
|
151
|
+
|
|
152
|
+
console.log(`✅ Done. ${totalCount} total properties.`);
|
|
367
153
|
return totalCount;
|
|
368
154
|
}
|
|
369
155
|
|
|
370
156
|
/**
|
|
371
|
-
* Generate
|
|
372
|
-
*
|
|
373
|
-
* Scans the provided properties map for entries with `is_deprecated === true`, groups
|
|
374
|
-
* them by `config_scope` ("broker" and "cluster"), sorts each group by property name,
|
|
375
|
-
* renders the `deprecated-properties` Handlebars template, and writes the output to
|
|
376
|
-
* `<outputDir>/deprecated/partials/deprecated-properties.adoc`.
|
|
377
|
-
*
|
|
378
|
-
* @param {Object.<string, Object>} properties - Map of property objects keyed by property name.
|
|
379
|
-
* Each property object may contain `is_deprecated`, `config_scope`, and `name` fields.
|
|
380
|
-
* @param {string} outputDir - Destination directory where the deprecated fragment will be written.
|
|
381
|
-
* @returns {number} The total number of deprecated properties found and written.
|
|
157
|
+
* Generate deprecated properties documentation.
|
|
382
158
|
*/
|
|
383
159
|
function generateDeprecatedDocs(properties, outputDir) {
|
|
384
160
|
const templatePath = getTemplatePath(
|
|
@@ -387,220 +163,135 @@ function generateDeprecatedDocs(properties, outputDir) {
|
|
|
387
163
|
);
|
|
388
164
|
const template = handlebars.compile(fs.readFileSync(templatePath, 'utf8'));
|
|
389
165
|
|
|
390
|
-
const deprecatedProperties = Object.values(properties).filter(
|
|
391
|
-
|
|
166
|
+
const deprecatedProperties = Object.values(properties).filter(p => p.is_deprecated);
|
|
392
167
|
const brokerProperties = deprecatedProperties
|
|
393
|
-
.filter(
|
|
394
|
-
.sort((a, b) =>
|
|
395
|
-
|
|
168
|
+
.filter(p => p.config_scope === 'broker')
|
|
169
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
396
170
|
const clusterProperties = deprecatedProperties
|
|
397
|
-
.filter(
|
|
398
|
-
.sort((a, b) =>
|
|
171
|
+
.filter(p => p.config_scope === 'cluster')
|
|
172
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
399
173
|
|
|
400
174
|
const data = {
|
|
401
175
|
deprecated: deprecatedProperties.length > 0,
|
|
402
|
-
brokerProperties: brokerProperties.length
|
|
403
|
-
clusterProperties: clusterProperties.length
|
|
176
|
+
brokerProperties: brokerProperties.length ? brokerProperties : null,
|
|
177
|
+
clusterProperties: clusterProperties.length ? clusterProperties : null
|
|
404
178
|
};
|
|
405
179
|
|
|
406
180
|
const output = template(data);
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
// Use the explicitly set partials directory
|
|
412
|
-
outputPath = path.join(process.env.OUTPUT_PARTIALS_DIR, 'deprecated', 'deprecated-properties.adoc');
|
|
413
|
-
} else if (outputDir.includes('pages/properties')) {
|
|
414
|
-
// Fallback: Navigate back from pages/properties to reference, then into partials/deprecated
|
|
415
|
-
outputPath = path.join(path.dirname(path.dirname(outputDir)), 'partials', 'deprecated', 'deprecated-properties.adoc');
|
|
416
|
-
} else {
|
|
417
|
-
// Fallback: Direct path when outputDir is the base directory
|
|
418
|
-
outputPath = path.join(outputDir, 'partials', 'deprecated', 'deprecated-properties.adoc');
|
|
419
|
-
}
|
|
420
|
-
|
|
181
|
+
const outputPath = process.env.OUTPUT_PARTIALS_DIR
|
|
182
|
+
? path.join(process.env.OUTPUT_PARTIALS_DIR, 'deprecated', 'deprecated-properties.adoc')
|
|
183
|
+
: path.join(outputDir, 'partials', 'deprecated', 'deprecated-properties.adoc');
|
|
184
|
+
|
|
421
185
|
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
422
186
|
fs.writeFileSync(outputPath, output, 'utf8');
|
|
423
|
-
|
|
424
187
|
console.log(`✅ Generated ${outputPath}`);
|
|
425
188
|
return deprecatedProperties.length;
|
|
426
189
|
}
|
|
427
190
|
|
|
428
191
|
/**
|
|
429
|
-
*
|
|
430
|
-
*
|
|
431
|
-
* Checks the provided map of properties and returns true if at least one
|
|
432
|
-
* property object has a `cloud_supported` own property (regardless of its value).
|
|
433
|
-
*
|
|
434
|
-
* @param {Object<string, Object>} properties - Map from property name to its metadata object.
|
|
435
|
-
* @return {boolean} True if any property has a `cloud_supported` attribute; otherwise false.
|
|
192
|
+
* Generate topic-property-mappings.adoc using the mappings template and topic properties.
|
|
436
193
|
*/
|
|
437
|
-
function
|
|
438
|
-
|
|
439
|
-
|
|
194
|
+
function generateTopicPropertyMappings(properties, partialsDir) {
|
|
195
|
+
const templatesDir = path.join(__dirname, 'templates');
|
|
196
|
+
const mappingsTemplatePath = getTemplatePath(
|
|
197
|
+
path.join(templatesDir, 'topic-property-mappings.hbs'),
|
|
198
|
+
'TEMPLATE_TOPIC_PROPERTY_MAPPINGS'
|
|
199
|
+
);
|
|
200
|
+
if (!fs.existsSync(mappingsTemplatePath)) {
|
|
201
|
+
throw new Error(`topic-property-mappings.hbs template not found: ${mappingsTemplatePath}`);
|
|
202
|
+
}
|
|
203
|
+
const topicProperties = Object.values(properties).filter(
|
|
204
|
+
p => p.is_topic_property && p.corresponding_cluster_property
|
|
440
205
|
);
|
|
206
|
+
if (topicProperties.length === 0) {
|
|
207
|
+
console.log('ℹ️ No topic properties with corresponding_cluster_property found. Skipping topic-property-mappings.adoc.');
|
|
208
|
+
return 0;
|
|
209
|
+
}
|
|
210
|
+
const hbsSource = fs.readFileSync(mappingsTemplatePath, 'utf8');
|
|
211
|
+
const hbs = handlebars.compile(hbsSource);
|
|
212
|
+
const rendered = hbs({ topicProperties });
|
|
213
|
+
const mappingsOut = path.join(partialsDir, 'topic-property-mappings.adoc');
|
|
214
|
+
fs.writeFileSync(mappingsOut, rendered, 'utf8');
|
|
215
|
+
console.log(`✅ Generated ${mappingsOut}`);
|
|
216
|
+
return topicProperties.length;
|
|
441
217
|
}
|
|
442
218
|
|
|
443
219
|
/**
|
|
444
|
-
* Generate
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
220
|
+
* Generate error reports for missing descriptions and deprecated properties.
|
|
221
|
+
*/
|
|
222
|
+
function generateErrorReports(properties) {
|
|
223
|
+
const emptyDescriptions = [];
|
|
224
|
+
const deprecatedProperties = [];
|
|
225
|
+
|
|
226
|
+
Object.values(properties).forEach(p => {
|
|
227
|
+
if (!p.description || !p.description.trim()) emptyDescriptions.push(p.name);
|
|
228
|
+
if (p.is_deprecated) deprecatedProperties.push(p.name);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const total = Object.keys(properties).length;
|
|
232
|
+
const pctEmpty = total ? ((emptyDescriptions.length / total) * 100).toFixed(2) : '0.00';
|
|
233
|
+
const pctDeprecated = total ? ((deprecatedProperties.length / total) * 100).toFixed(2) : '0.00';
|
|
234
|
+
console.log(`Empty descriptions: ${emptyDescriptions.length} (${pctEmpty}%)`);
|
|
235
|
+
console.log(`Deprecated: ${deprecatedProperties.length} (${pctDeprecated}%)`);
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
empty_descriptions: emptyDescriptions.sort(),
|
|
239
|
+
deprecated_properties: deprecatedProperties.sort()
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Main generator — only supports partials and deprecated docs.
|
|
459
245
|
*/
|
|
460
246
|
function generateAllDocs(inputFile, outputDir) {
|
|
461
|
-
// Read input JSON
|
|
462
247
|
const data = JSON.parse(fs.readFileSync(inputFile, 'utf8'));
|
|
463
248
|
const properties = data.properties || {};
|
|
464
249
|
|
|
465
|
-
|
|
466
|
-
const hasCloudSupport = hasCloudSupportMetadata(properties);
|
|
467
|
-
if (hasCloudSupport) {
|
|
468
|
-
console.log('🌤️ Cloud support metadata detected, using cloud-aware templates');
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// Register partials with cloud support detection
|
|
472
|
-
registerPartials(hasCloudSupport);
|
|
473
|
-
|
|
474
|
-
let totalProperties = 0;
|
|
475
|
-
let totalBrokerProperties = 0;
|
|
476
|
-
let totalClusterProperties = 0;
|
|
477
|
-
let totalObjectStorageProperties = 0;
|
|
478
|
-
let totalTopicProperties = 0;
|
|
479
|
-
|
|
480
|
-
// Generate complete property pages only if requested
|
|
481
|
-
if (process.env.GENERATE_PAGES === '1') {
|
|
482
|
-
console.log(`📄 Generating complete property pages...`);
|
|
483
|
-
|
|
484
|
-
// Generate each type of documentation
|
|
485
|
-
for (const [type, config] of Object.entries(PROPERTY_CONFIG)) {
|
|
486
|
-
const count = generatePropertyDocs(properties, config, outputDir);
|
|
487
|
-
totalProperties += count;
|
|
488
|
-
|
|
489
|
-
if (type === 'broker') totalBrokerProperties = count;
|
|
490
|
-
else if (type === 'cluster') totalClusterProperties = count;
|
|
491
|
-
else if (type === 'object-storage') totalObjectStorageProperties = count;
|
|
492
|
-
else if (type === 'topic') totalTopicProperties = count;
|
|
493
|
-
}
|
|
494
|
-
} else {
|
|
495
|
-
console.log(`📄 Skipping complete property pages (use --generate-pages to enable)`);
|
|
496
|
-
|
|
497
|
-
// Still count properties for summary
|
|
498
|
-
Object.values(properties).forEach(prop => {
|
|
499
|
-
if (prop.config_scope === 'broker' && !prop.is_deprecated) totalBrokerProperties++;
|
|
500
|
-
else if (prop.config_scope === 'cluster' && !prop.is_deprecated) {
|
|
501
|
-
if (prop.name && (
|
|
502
|
-
prop.name.includes('cloud_storage') ||
|
|
503
|
-
prop.name.includes('s3_') ||
|
|
504
|
-
prop.name.includes('azure_') ||
|
|
505
|
-
prop.name.includes('gcs_') ||
|
|
506
|
-
prop.name.includes('archival_') ||
|
|
507
|
-
prop.name.includes('remote_') ||
|
|
508
|
-
prop.name.includes('tiered_')
|
|
509
|
-
)) {
|
|
510
|
-
totalObjectStorageProperties++;
|
|
511
|
-
} else {
|
|
512
|
-
totalClusterProperties++;
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
else if (prop.config_scope === 'topic' && !prop.is_deprecated) totalTopicProperties++;
|
|
516
|
-
});
|
|
517
|
-
totalProperties = totalBrokerProperties + totalClusterProperties + totalObjectStorageProperties + totalTopicProperties;
|
|
518
|
-
}
|
|
250
|
+
registerPartials();
|
|
519
251
|
|
|
520
|
-
// Generate individual property partials if requested
|
|
521
252
|
let partialsCount = 0;
|
|
522
253
|
let deprecatedCount = 0;
|
|
254
|
+
|
|
523
255
|
if (process.env.GENERATE_PARTIALS === '1' && process.env.OUTPUT_PARTIALS_DIR) {
|
|
524
|
-
|
|
256
|
+
console.log('📄 Generating property partials and deprecated docs...');
|
|
525
257
|
deprecatedCount = generateDeprecatedDocs(properties, outputDir);
|
|
526
|
-
|
|
527
|
-
|
|
258
|
+
partialsCount = generatePropertyPartials(properties, process.env.OUTPUT_PARTIALS_DIR);
|
|
259
|
+
|
|
260
|
+
// Generate topic-property-mappings.adoc
|
|
261
|
+
try {
|
|
262
|
+
generateTopicPropertyMappings(properties, process.env.OUTPUT_PARTIALS_DIR);
|
|
263
|
+
} catch (err) {
|
|
264
|
+
console.error(`❌ Failed to generate topic-property-mappings.adoc: ${err.message}`);
|
|
265
|
+
}
|
|
528
266
|
} else {
|
|
529
|
-
console.log(
|
|
530
|
-
console.log(`📄 Skipping deprecated properties documentation (use --generate-partials to enable)`);
|
|
267
|
+
console.log('📄 Skipping partial generation (set GENERATE_PARTIALS=1 and OUTPUT_PARTIALS_DIR to enable)');
|
|
531
268
|
}
|
|
532
269
|
|
|
533
|
-
|
|
534
|
-
const errorReport = generateErrorReports(properties, outputDir);
|
|
535
|
-
|
|
536
|
-
// Add error arrays directly to the input file so they're included when copied
|
|
270
|
+
const errors = generateErrorReports(properties);
|
|
537
271
|
const inputData = JSON.parse(fs.readFileSync(inputFile, 'utf8'));
|
|
538
|
-
inputData.empty_descriptions =
|
|
539
|
-
inputData.deprecated_properties =
|
|
272
|
+
inputData.empty_descriptions = errors.empty_descriptions;
|
|
273
|
+
inputData.deprecated_properties = errors.deprecated_properties;
|
|
540
274
|
fs.writeFileSync(inputFile, JSON.stringify(inputData, null, 2), 'utf8');
|
|
541
|
-
console.log(`📝 Added error arrays to ${inputFile}`);
|
|
542
|
-
|
|
543
|
-
console.log(`📊 Generation Summary:`);
|
|
544
|
-
console.log(` Total properties read: ${Object.keys(properties).length}`);
|
|
545
|
-
console.log(` Total Broker properties: ${totalBrokerProperties}`);
|
|
546
|
-
console.log(` Total Cluster properties: ${totalClusterProperties}`);
|
|
547
|
-
console.log(` Total Object Storage properties: ${totalObjectStorageProperties}`);
|
|
548
|
-
console.log(` Total Topic properties: ${totalTopicProperties}`);
|
|
549
|
-
console.log(` Total Deprecated properties: ${deprecatedCount}`);
|
|
550
|
-
if (partialsCount > 0) {
|
|
551
|
-
console.log(` Total Property partials: ${partialsCount}`);
|
|
552
|
-
}
|
|
553
275
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
objectStorageProperties: totalObjectStorageProperties,
|
|
559
|
-
topicProperties: totalTopicProperties,
|
|
560
|
-
deprecatedProperties: deprecatedCount,
|
|
561
|
-
propertyPartials: partialsCount
|
|
562
|
-
};
|
|
563
|
-
}
|
|
276
|
+
console.log('📊 Summary:');
|
|
277
|
+
console.log(` Total properties: ${Object.keys(properties).length}`);
|
|
278
|
+
console.log(` Total partials generated: ${partialsCount}`);
|
|
279
|
+
console.log(` Deprecated properties: ${deprecatedCount}`);
|
|
564
280
|
|
|
565
|
-
/**
|
|
566
|
-
* Generate error reports for properties with missing or invalid data
|
|
567
|
-
*/
|
|
568
|
-
function generateErrorReports(properties, outputDir) {
|
|
569
|
-
const emptyDescriptions = [];
|
|
570
|
-
const deprecatedProperties = [];
|
|
571
|
-
|
|
572
|
-
Object.values(properties).forEach(prop => {
|
|
573
|
-
if (!prop.description || prop.description.trim() === '') {
|
|
574
|
-
emptyDescriptions.push(prop.name);
|
|
575
|
-
}
|
|
576
|
-
if (prop.is_deprecated) {
|
|
577
|
-
deprecatedProperties.push(prop.name);
|
|
578
|
-
}
|
|
579
|
-
});
|
|
580
|
-
|
|
581
|
-
// Add these arrays to the properties JSON file
|
|
582
|
-
const totalProperties = Object.keys(properties).length;
|
|
583
|
-
const percentageEmpty = totalProperties > 0 ? ((emptyDescriptions.length / totalProperties) * 100).toFixed(2) : '0.00';
|
|
584
|
-
const percentageDeprecated = totalProperties > 0 ? ((deprecatedProperties.length / totalProperties) * 100).toFixed(2) : '0.00';
|
|
585
|
-
console.log(`You have ${emptyDescriptions.length} properties with empty description. Percentage of errors: ${percentageEmpty}%.`);
|
|
586
|
-
console.log(`You have ${deprecatedProperties.length} deprecated properties. Percentage of errors: ${percentageDeprecated}%.`);
|
|
587
|
-
|
|
588
|
-
// Return the arrays sorted for deterministic output
|
|
589
281
|
return {
|
|
590
|
-
|
|
591
|
-
|
|
282
|
+
totalProperties: Object.keys(properties).length,
|
|
283
|
+
propertyPartials: partialsCount,
|
|
284
|
+
deprecatedProperties: deprecatedCount
|
|
592
285
|
};
|
|
593
286
|
}
|
|
594
287
|
|
|
595
288
|
module.exports = {
|
|
596
289
|
generateAllDocs,
|
|
597
|
-
generatePropertyDocs,
|
|
598
290
|
generateDeprecatedDocs,
|
|
599
|
-
generatePropertyPartials
|
|
600
|
-
PROPERTY_CONFIG
|
|
291
|
+
generatePropertyPartials
|
|
601
292
|
};
|
|
602
293
|
|
|
603
|
-
// CLI
|
|
294
|
+
// CLI
|
|
604
295
|
if (require.main === module) {
|
|
605
296
|
const args = process.argv.slice(2);
|
|
606
297
|
if (args.length < 2) {
|
|
@@ -609,7 +300,6 @@ if (require.main === module) {
|
|
|
609
300
|
}
|
|
610
301
|
|
|
611
302
|
const [inputFile, outputDir] = args;
|
|
612
|
-
|
|
613
303
|
if (!fs.existsSync(inputFile)) {
|
|
614
304
|
console.error(`❌ Input file not found: ${inputFile}`);
|
|
615
305
|
process.exit(1);
|
|
@@ -618,8 +308,8 @@ if (require.main === module) {
|
|
|
618
308
|
try {
|
|
619
309
|
generateAllDocs(inputFile, outputDir);
|
|
620
310
|
console.log('✅ Documentation generation completed successfully');
|
|
621
|
-
} catch (
|
|
622
|
-
console.error(`❌ Error
|
|
311
|
+
} catch (err) {
|
|
312
|
+
console.error(`❌ Error: ${err.message}`);
|
|
623
313
|
process.exit(1);
|
|
624
314
|
}
|
|
625
315
|
}
|