@redpanda-data/docs-extensions-and-macros 4.10.3 → 4.10.5

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.
@@ -1,3 +1,4 @@
1
+ const AUTOGEN_NOTICE = '// 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/1396244485/Review+Redpanda+configuration+properties\n';
1
2
  'use strict';
2
3
 
3
4
  const fs = require('fs');
@@ -7,14 +8,17 @@ const helpers = require('./helpers');
7
8
 
8
9
  /**
9
10
  * Handlebars documentation generator for Redpanda configuration properties.
10
- *
11
+ *
11
12
  * Supports custom template overrides using environment variables:
12
- * - TEMPLATE_PROPERTY_PAGE: Main property page template
13
- * - TEMPLATE_PROPERTY: Individual property section template
13
+ * - TEMPLATE_PROPERTY: Individual property section template
14
14
  * - TEMPLATE_TOPIC_PROPERTY: Individual topic property section template
15
15
  * - TEMPLATE_DEPRECATED_PROPERTY: Individual deprecated property section template
16
16
  * - TEMPLATE_DEPRECATED: Deprecated properties page template
17
- *
17
+ *
18
+ * Behavior flags (environment variables):
19
+ * - GENERATE_PARTIALS=1 Generate consolidated property partials and deprecated partials
20
+ * - OUTPUT_PARTIALS_DIR=<p> Destination for consolidated partials (required if GENERATE_PARTIALS=1)
21
+ *
18
22
  * CLI Usage: node generate-handlebars-docs.js <input-file> <output-dir>
19
23
  */
20
24
 
@@ -44,81 +48,6 @@ function isObjectStorageProperty(prop) {
44
48
  );
45
49
  }
46
50
 
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
51
  /**
123
52
  * Gets template path, checking environment variables for custom paths first
124
53
  */
@@ -134,251 +63,99 @@ function getTemplatePath(defaultPath, envVar) {
134
63
  /**
135
64
  * Register Handlebars partials used to render property documentation.
136
65
  *
137
- * Loads templates from the local templates directory (overridable via environment
138
- * variables handled by getTemplatePath) and registers three partials:
139
- * - "property" (uses cloud-aware `property-cloud.hbs` when enabled)
140
- * - "topic-property" (uses cloud-aware `topic-property-cloud.hbs` when enabled)
66
+ * Registers:
67
+ * - "property"
68
+ * - "topic-property"
141
69
  * - "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
70
  */
146
- function registerPartials(hasCloudSupport = false) {
71
+ function registerPartials() {
147
72
  const templatesDir = path.join(__dirname, 'templates');
148
-
73
+
149
74
  try {
150
- console.log(`📝 Registering Handlebars templates (cloud support: ${hasCloudSupport ? 'enabled' : 'disabled'})`);
151
-
152
- // Register property partial (choose cloud or regular version)
153
- const propertyTemplateFile = hasCloudSupport ? 'property-cloud.hbs' : 'property.hbs';
75
+ console.log('📝 Registering Handlebars templates');
76
+
154
77
  const propertyTemplatePath = getTemplatePath(
155
- path.join(templatesDir, propertyTemplateFile),
78
+ path.join(templatesDir, 'property.hbs'),
156
79
  'TEMPLATE_PROPERTY'
157
80
  );
158
-
159
81
  if (!fs.existsSync(propertyTemplatePath)) {
160
82
  throw new Error(`Property template not found: ${propertyTemplatePath}`);
161
83
  }
162
-
163
- const propertyTemplate = fs.readFileSync(propertyTemplatePath, 'utf8');
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';
84
+ handlebars.registerPartial('property', fs.readFileSync(propertyTemplatePath, 'utf8'));
85
+
169
86
  const topicPropertyTemplatePath = getTemplatePath(
170
- path.join(templatesDir, topicPropertyTemplateFile),
87
+ path.join(templatesDir, 'topic-property.hbs'),
171
88
  'TEMPLATE_TOPIC_PROPERTY'
172
89
  );
173
-
174
90
  if (!fs.existsSync(topicPropertyTemplatePath)) {
175
91
  throw new Error(`Topic property template not found: ${topicPropertyTemplatePath}`);
176
92
  }
177
-
178
- const topicPropertyTemplate = fs.readFileSync(topicPropertyTemplatePath, 'utf8');
179
- handlebars.registerPartial('topic-property', topicPropertyTemplate);
180
- console.log(`✅ Registered topic property template: ${topicPropertyTemplateFile}`);
181
-
182
- // Register deprecated property partial
93
+ handlebars.registerPartial('topic-property', fs.readFileSync(topicPropertyTemplatePath, 'utf8'));
94
+
183
95
  const deprecatedPropertyTemplatePath = getTemplatePath(
184
96
  path.join(templatesDir, 'deprecated-property.hbs'),
185
97
  'TEMPLATE_DEPRECATED_PROPERTY'
186
98
  );
187
-
188
99
  if (!fs.existsSync(deprecatedPropertyTemplatePath)) {
189
100
  throw new Error(`Deprecated property template not found: ${deprecatedPropertyTemplatePath}`);
190
101
  }
191
-
192
- const deprecatedPropertyTemplate = fs.readFileSync(deprecatedPropertyTemplatePath, 'utf8');
193
- handlebars.registerPartial('deprecated-property', deprecatedPropertyTemplate);
194
- console.log(`✅ Registered deprecated property template`);
195
-
102
+ handlebars.registerPartial('deprecated-property', fs.readFileSync(deprecatedPropertyTemplatePath, 'utf8'));
103
+
104
+ console.log('✅ Registered all partials');
196
105
  } catch (error) {
197
106
  console.error('❌ Failed to register Handlebars templates:');
198
- console.error(` Error: ${error.message}`);
199
- console.error(' This indicates missing or corrupted template files.');
200
- console.error(' Check that all .hbs files exist in tools/property-extractor/templates/');
107
+ console.error(` ${error.message}`);
201
108
  throw error;
202
109
  }
203
110
  }
204
111
 
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
112
  /**
282
113
  * 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
114
  */
293
- function generatePropertyPartials(properties, partialsDir, hasCloudSupport = false) {
115
+ function generatePropertyPartials(properties, partialsDir) {
294
116
  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
117
 
304
- // Use the topic property template for topic properties
305
- const topicTemplateName = hasCloudSupport ? 'topic-property-cloud' : 'topic-property';
306
- const topicTemplatePath = getTemplatePath(
307
- path.join(__dirname, 'templates', `${topicTemplateName}.hbs`),
308
- 'TEMPLATE_TOPIC_PROPERTY'
118
+ const propertyTemplate = handlebars.compile(
119
+ fs.readFileSync(getTemplatePath(path.join(__dirname, 'templates', 'property.hbs'), 'TEMPLATE_PROPERTY'), 'utf8')
120
+ );
121
+ const topicTemplate = handlebars.compile(
122
+ fs.readFileSync(getTemplatePath(path.join(__dirname, 'templates', 'topic-property.hbs'), 'TEMPLATE_TOPIC_PROPERTY'), 'utf8')
309
123
  );
310
- const topicTemplate = handlebars.compile(fs.readFileSync(topicTemplatePath, 'utf8'));
311
124
 
312
- // Create the main partials directory
313
125
  const propertiesPartialsDir = path.join(partialsDir, 'properties');
314
126
  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
127
 
324
- // Categorize properties
128
+ const propertyGroups = { cluster: [], topic: [], broker: [], 'object-storage': [] };
129
+
325
130
  Object.values(properties).forEach(prop => {
326
- if (!prop.name || !prop.config_scope) return; // Skip properties without names or scope
327
-
328
- if (prop.config_scope === 'topic') {
329
- propertyGroups.topic.push(prop);
330
- } else if (prop.config_scope === 'broker') {
331
- propertyGroups.broker.push(prop);
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
- }
131
+ if (!prop.name || !prop.config_scope) return;
132
+ if (prop.config_scope === 'topic') propertyGroups.topic.push(prop);
133
+ else if (prop.config_scope === 'broker') propertyGroups.broker.push(prop);
134
+ else if (prop.config_scope === 'cluster') {
135
+ if (isObjectStorageProperty(prop)) propertyGroups['object-storage'].push(prop);
136
+ else propertyGroups.cluster.push(prop);
339
137
  }
340
138
  });
341
139
 
342
140
  let totalCount = 0;
343
-
344
- // Generate consolidated partials for each property type
141
+
345
142
  Object.entries(propertyGroups).forEach(([type, props]) => {
346
143
  if (props.length === 0) return;
347
-
348
- // Sort properties by name
349
144
  props.sort((a, b) => String(a.name || '').localeCompare(String(b.name || '')));
350
-
351
- // Choose the appropriate template based on property type
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
145
+ const selectedTemplate = type === 'topic' ? topicTemplate : propertyTemplate;
146
+ const content = props.map(p => selectedTemplate(p)).join('\n');
358
147
  const filename = `${type}-properties.adoc`;
359
- const outputPath = path.join(propertiesPartialsDir, filename);
360
- fs.writeFileSync(outputPath, content, 'utf8');
361
-
362
- console.log(`✅ Generated ${outputPath} with ${props.length} properties`);
148
+ fs.writeFileSync(path.join(propertiesPartialsDir, filename), AUTOGEN_NOTICE + content, 'utf8');
149
+ console.log(`✅ Generated ${filename} (${props.length} properties)`);
363
150
  totalCount += props.length;
364
151
  });
365
-
366
- console.log(`✅ Generated consolidated property partials in ${partialsDir} (${totalCount} total properties)`);
152
+
153
+ console.log(`✅ Done. ${totalCount} total properties.`);
367
154
  return totalCount;
368
155
  }
369
156
 
370
157
  /**
371
- * Generate an AsciiDoc fragment listing deprecated properties and write it to disk.
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.
158
+ * Generate deprecated properties documentation.
382
159
  */
383
160
  function generateDeprecatedDocs(properties, outputDir) {
384
161
  const templatePath = getTemplatePath(
@@ -387,220 +164,135 @@ function generateDeprecatedDocs(properties, outputDir) {
387
164
  );
388
165
  const template = handlebars.compile(fs.readFileSync(templatePath, 'utf8'));
389
166
 
390
- const deprecatedProperties = Object.values(properties).filter(prop => prop.is_deprecated);
391
-
167
+ const deprecatedProperties = Object.values(properties).filter(p => p.is_deprecated);
392
168
  const brokerProperties = deprecatedProperties
393
- .filter(prop => prop.config_scope === 'broker')
394
- .sort((a, b) => String(a.name || '').localeCompare(String(b.name || '')));
395
-
169
+ .filter(p => p.config_scope === 'broker')
170
+ .sort((a, b) => a.name.localeCompare(b.name));
396
171
  const clusterProperties = deprecatedProperties
397
- .filter(prop => prop.config_scope === 'cluster')
398
- .sort((a, b) => String(a.name || '').localeCompare(String(b.name || '')));
172
+ .filter(p => p.config_scope === 'cluster')
173
+ .sort((a, b) => a.name.localeCompare(b.name));
399
174
 
400
175
  const data = {
401
176
  deprecated: deprecatedProperties.length > 0,
402
- brokerProperties: brokerProperties.length > 0 ? brokerProperties : null,
403
- clusterProperties: clusterProperties.length > 0 ? clusterProperties : null
177
+ brokerProperties: brokerProperties.length ? brokerProperties : null,
178
+ clusterProperties: clusterProperties.length ? clusterProperties : null
404
179
  };
405
180
 
406
181
  const output = template(data);
407
-
408
- // Determine the correct path for deprecated properties
409
- let outputPath;
410
- if (process.env.OUTPUT_PARTIALS_DIR) {
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
-
182
+ const outputPath = process.env.OUTPUT_PARTIALS_DIR
183
+ ? path.join(process.env.OUTPUT_PARTIALS_DIR, 'deprecated', 'deprecated-properties.adoc')
184
+ : path.join(outputDir, 'partials', 'deprecated', 'deprecated-properties.adoc');
185
+
421
186
  fs.mkdirSync(path.dirname(outputPath), { recursive: true });
422
- fs.writeFileSync(outputPath, output, 'utf8');
423
-
187
+ fs.writeFileSync(outputPath, AUTOGEN_NOTICE + output, 'utf8');
424
188
  console.log(`✅ Generated ${outputPath}`);
425
189
  return deprecatedProperties.length;
426
190
  }
427
191
 
428
192
  /**
429
- * Determine whether any property includes cloud support metadata.
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.
193
+ * Generate topic-property-mappings.adoc using the mappings template and topic properties.
436
194
  */
437
- function hasCloudSupportMetadata(properties) {
438
- return Object.values(properties).some(prop =>
439
- Object.prototype.hasOwnProperty.call(prop, 'cloud_supported')
195
+ function generateTopicPropertyMappings(properties, partialsDir) {
196
+ const templatesDir = path.join(__dirname, 'templates');
197
+ const mappingsTemplatePath = getTemplatePath(
198
+ path.join(templatesDir, 'topic-property-mappings.hbs'),
199
+ 'TEMPLATE_TOPIC_PROPERTY_MAPPINGS'
200
+ );
201
+ if (!fs.existsSync(mappingsTemplatePath)) {
202
+ throw new Error(`topic-property-mappings.hbs template not found: ${mappingsTemplatePath}`);
203
+ }
204
+ const topicProperties = Object.values(properties).filter(
205
+ p => p.is_topic_property && p.corresponding_cluster_property
440
206
  );
207
+ if (topicProperties.length === 0) {
208
+ console.log('ℹ️ No topic properties with corresponding_cluster_property found. Skipping topic-property-mappings.adoc.');
209
+ return 0;
210
+ }
211
+ const hbsSource = fs.readFileSync(mappingsTemplatePath, 'utf8');
212
+ const hbs = handlebars.compile(hbsSource);
213
+ const rendered = hbs({ topicProperties });
214
+ const mappingsOut = path.join(partialsDir, 'topic-property-mappings.adoc');
215
+ fs.writeFileSync(mappingsOut, AUTOGEN_NOTICE + rendered, 'utf8');
216
+ console.log(`✅ Generated ${mappingsOut}`);
217
+ return topicProperties.length;
441
218
  }
442
219
 
443
220
  /**
444
- * Generate all property documentation and write output files to disk.
445
- *
446
- * Reads properties from the provided JSON file, detects whether any property
447
- * includes cloud support metadata to select cloud-aware templates, registers
448
- * Handlebars partials accordingly, renders per-type property pages and a
449
- * deprecated-properties partial, writes a flat list of all property names, and
450
- * produces error reports.
451
- *
452
- * Generated artifacts are written under the given output directory (e.g.:
453
- * pages/<type>/*.adoc, pages/deprecated/partials/deprecated-properties.adoc,
454
- * all_properties.txt, and files under outputDir/error).
455
- *
456
- * @param {string} inputFile - Filesystem path to the input JSON containing a top-level `properties` object.
457
- * @param {string} outputDir - Destination directory where generated pages and reports will be written.
458
- * @returns {{totalProperties: number, brokerProperties: number, clusterProperties: number, objectStorageProperties: number, topicProperties: number, deprecatedProperties: number}} Summary counts for all properties and per-type totals.
221
+ * Generate error reports for missing descriptions and deprecated properties.
222
+ */
223
+ function generateErrorReports(properties) {
224
+ const emptyDescriptions = [];
225
+ const deprecatedProperties = [];
226
+
227
+ Object.values(properties).forEach(p => {
228
+ if (!p.description || !p.description.trim()) emptyDescriptions.push(p.name);
229
+ if (p.is_deprecated) deprecatedProperties.push(p.name);
230
+ });
231
+
232
+ const total = Object.keys(properties).length;
233
+ const pctEmpty = total ? ((emptyDescriptions.length / total) * 100).toFixed(2) : '0.00';
234
+ const pctDeprecated = total ? ((deprecatedProperties.length / total) * 100).toFixed(2) : '0.00';
235
+ console.log(`Empty descriptions: ${emptyDescriptions.length} (${pctEmpty}%)`);
236
+ console.log(`Deprecated: ${deprecatedProperties.length} (${pctDeprecated}%)`);
237
+
238
+ return {
239
+ empty_descriptions: emptyDescriptions.sort(),
240
+ deprecated_properties: deprecatedProperties.sort()
241
+ };
242
+ }
243
+
244
+ /**
245
+ * Main generator — only supports partials and deprecated docs.
459
246
  */
460
247
  function generateAllDocs(inputFile, outputDir) {
461
- // Read input JSON
462
248
  const data = JSON.parse(fs.readFileSync(inputFile, 'utf8'));
463
249
  const properties = data.properties || {};
464
250
 
465
- // Check if cloud support is enabled
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
- }
251
+ registerPartials();
519
252
 
520
- // Generate individual property partials if requested
521
253
  let partialsCount = 0;
522
254
  let deprecatedCount = 0;
255
+
523
256
  if (process.env.GENERATE_PARTIALS === '1' && process.env.OUTPUT_PARTIALS_DIR) {
524
- // Generate deprecated properties documentation
257
+ console.log('📄 Generating property partials and deprecated docs...');
525
258
  deprecatedCount = generateDeprecatedDocs(properties, outputDir);
526
-
527
- partialsCount = generatePropertyPartials(properties, process.env.OUTPUT_PARTIALS_DIR, hasCloudSupport);
259
+ partialsCount = generatePropertyPartials(properties, process.env.OUTPUT_PARTIALS_DIR);
260
+
261
+ // Generate topic-property-mappings.adoc
262
+ try {
263
+ generateTopicPropertyMappings(properties, process.env.OUTPUT_PARTIALS_DIR);
264
+ } catch (err) {
265
+ console.error(`❌ Failed to generate topic-property-mappings.adoc: ${err.message}`);
266
+ }
528
267
  } else {
529
- console.log(`📄 Skipping property partials (use --generate-partials to enable)`);
530
- console.log(`📄 Skipping deprecated properties documentation (use --generate-partials to enable)`);
268
+ console.log('📄 Skipping partial generation (set GENERATE_PARTIALS=1 and OUTPUT_PARTIALS_DIR to enable)');
531
269
  }
532
270
 
533
- // Generate error reports and add to input JSON output
534
- const errorReport = generateErrorReports(properties, outputDir);
535
-
536
- // Add error arrays directly to the input file so they're included when copied
271
+ const errors = generateErrorReports(properties);
537
272
  const inputData = JSON.parse(fs.readFileSync(inputFile, 'utf8'));
538
- inputData.empty_descriptions = errorReport.empty_descriptions;
539
- inputData.deprecated_properties = errorReport.deprecated_properties;
273
+ inputData.empty_descriptions = errors.empty_descriptions;
274
+ inputData.deprecated_properties = errors.deprecated_properties;
540
275
  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
276
 
554
- return {
555
- totalProperties: Object.keys(properties).length,
556
- brokerProperties: totalBrokerProperties,
557
- clusterProperties: totalClusterProperties,
558
- objectStorageProperties: totalObjectStorageProperties,
559
- topicProperties: totalTopicProperties,
560
- deprecatedProperties: deprecatedCount,
561
- propertyPartials: partialsCount
562
- };
563
- }
277
+ console.log('📊 Summary:');
278
+ console.log(` Total properties: ${Object.keys(properties).length}`);
279
+ console.log(` Total partials generated: ${partialsCount}`);
280
+ console.log(` Deprecated properties: ${deprecatedCount}`);
564
281
 
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
282
  return {
590
- empty_descriptions: emptyDescriptions.sort(),
591
- deprecated_properties: deprecatedProperties.sort()
283
+ totalProperties: Object.keys(properties).length,
284
+ propertyPartials: partialsCount,
285
+ deprecatedProperties: deprecatedCount
592
286
  };
593
287
  }
594
288
 
595
289
  module.exports = {
596
290
  generateAllDocs,
597
- generatePropertyDocs,
598
291
  generateDeprecatedDocs,
599
- generatePropertyPartials,
600
- PROPERTY_CONFIG
292
+ generatePropertyPartials
601
293
  };
602
294
 
603
- // CLI interface
295
+ // CLI
604
296
  if (require.main === module) {
605
297
  const args = process.argv.slice(2);
606
298
  if (args.length < 2) {
@@ -609,7 +301,6 @@ if (require.main === module) {
609
301
  }
610
302
 
611
303
  const [inputFile, outputDir] = args;
612
-
613
304
  if (!fs.existsSync(inputFile)) {
614
305
  console.error(`❌ Input file not found: ${inputFile}`);
615
306
  process.exit(1);
@@ -618,8 +309,8 @@ if (require.main === module) {
618
309
  try {
619
310
  generateAllDocs(inputFile, outputDir);
620
311
  console.log('✅ Documentation generation completed successfully');
621
- } catch (error) {
622
- console.error(`❌ Error generating documentation: ${error.message}`);
312
+ } catch (err) {
313
+ console.error(`❌ Error: ${err.message}`);
623
314
  process.exit(1);
624
315
  }
625
316
  }