@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.
@@ -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
- * - TEMPLATE_PROPERTY_PAGE: Main property page template
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
- * 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)
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(hasCloudSupport = false) {
70
+ function registerPartials() {
147
71
  const templatesDir = path.join(__dirname, 'templates');
148
-
72
+
149
73
  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';
74
+ console.log('📝 Registering Handlebars templates');
75
+
154
76
  const propertyTemplatePath = getTemplatePath(
155
- path.join(templatesDir, propertyTemplateFile),
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
- 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';
83
+ handlebars.registerPartial('property', fs.readFileSync(propertyTemplatePath, 'utf8'));
84
+
169
85
  const topicPropertyTemplatePath = getTemplatePath(
170
- path.join(templatesDir, topicPropertyTemplateFile),
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
- 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
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
- const deprecatedPropertyTemplate = fs.readFileSync(deprecatedPropertyTemplatePath, 'utf8');
193
- handlebars.registerPartial('deprecated-property', deprecatedPropertyTemplate);
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(` 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/');
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, hasCloudSupport = false) {
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
- // 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'
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
- // Categorize properties
127
+ const propertyGroups = { cluster: [], topic: [], broker: [], 'object-storage': [] };
128
+
325
129
  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
- }
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
- // 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
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
- const outputPath = path.join(propertiesPartialsDir, filename);
360
- fs.writeFileSync(outputPath, content, 'utf8');
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(`✅ Generated consolidated property partials in ${partialsDir} (${totalCount} total properties)`);
151
+
152
+ console.log(`✅ Done. ${totalCount} total properties.`);
367
153
  return totalCount;
368
154
  }
369
155
 
370
156
  /**
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.
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(prop => prop.is_deprecated);
391
-
166
+ const deprecatedProperties = Object.values(properties).filter(p => p.is_deprecated);
392
167
  const brokerProperties = deprecatedProperties
393
- .filter(prop => prop.config_scope === 'broker')
394
- .sort((a, b) => String(a.name || '').localeCompare(String(b.name || '')));
395
-
168
+ .filter(p => p.config_scope === 'broker')
169
+ .sort((a, b) => a.name.localeCompare(b.name));
396
170
  const clusterProperties = deprecatedProperties
397
- .filter(prop => prop.config_scope === 'cluster')
398
- .sort((a, b) => String(a.name || '').localeCompare(String(b.name || '')));
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 > 0 ? brokerProperties : null,
403
- clusterProperties: clusterProperties.length > 0 ? clusterProperties : null
176
+ brokerProperties: brokerProperties.length ? brokerProperties : null,
177
+ clusterProperties: clusterProperties.length ? clusterProperties : null
404
178
  };
405
179
 
406
180
  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
-
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
- * 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.
192
+ * Generate topic-property-mappings.adoc using the mappings template and topic properties.
436
193
  */
437
- function hasCloudSupportMetadata(properties) {
438
- return Object.values(properties).some(prop =>
439
- Object.prototype.hasOwnProperty.call(prop, 'cloud_supported')
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 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.
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
- // 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
- }
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
- // Generate deprecated properties documentation
256
+ console.log('📄 Generating property partials and deprecated docs...');
525
257
  deprecatedCount = generateDeprecatedDocs(properties, outputDir);
526
-
527
- partialsCount = generatePropertyPartials(properties, process.env.OUTPUT_PARTIALS_DIR, hasCloudSupport);
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(`📄 Skipping property partials (use --generate-partials to enable)`);
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
- // 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
270
+ const errors = generateErrorReports(properties);
537
271
  const inputData = JSON.parse(fs.readFileSync(inputFile, 'utf8'));
538
- inputData.empty_descriptions = errorReport.empty_descriptions;
539
- inputData.deprecated_properties = errorReport.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
- 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
- }
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
- empty_descriptions: emptyDescriptions.sort(),
591
- deprecated_properties: deprecatedProperties.sort()
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 interface
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 (error) {
622
- console.error(`❌ Error generating documentation: ${error.message}`);
311
+ } catch (err) {
312
+ console.error(`❌ Error: ${err.message}`);
623
313
  process.exit(1);
624
314
  }
625
315
  }