@@ -22,7 +22,7 @@ const helpers = require('./helpers');
2222 * CLI Usage: node generate-handlebars-docs.js <input-file> <output-dir>
2323 */
2424
25- // Register all helpers
25+ // Register helpers
2626Object . entries ( helpers ) . forEach ( ( [ name , fn ] ) => {
2727 if ( typeof fn !== 'function' ) {
2828 console . error ( `❌ Helper "${ name } " is not a function` ) ;
@@ -62,11 +62,6 @@ function getTemplatePath(defaultPath, envVar) {
6262
6363/**
6464 * Register Handlebars partials used to render property documentation.
65- *
66- * Registers:
67- * - "property"
68- * - "topic-property"
69- * - "deprecated-property"
7065 */
7166function registerPartials ( ) {
7267 const templatesDir = path . join ( __dirname , 'templates' ) ;
@@ -129,11 +124,24 @@ function generatePropertyPartials(properties, partialsDir) {
129124
130125 Object . values ( properties ) . forEach ( prop => {
131126 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 ) ;
127+
128+ switch ( prop . config_scope ) {
129+ case 'topic' :
130+ propertyGroups . topic . push ( prop ) ;
131+ break ;
132+ case 'broker' :
133+ propertyGroups . broker . push ( prop ) ;
134+ break ;
135+ case 'cluster' :
136+ if ( isObjectStorageProperty ( prop ) ) propertyGroups [ 'object-storage' ] . push ( prop ) ;
137+ else propertyGroups . cluster . push ( prop ) ;
138+ break ;
139+ case 'object-storage' :
140+ propertyGroups [ 'object-storage' ] . push ( prop ) ;
141+ break ;
142+ default :
143+ console . warn ( `⚠️ Unknown config_scope: ${ prop . config_scope } for ${ prop . name } ` ) ;
144+ break ;
137145 }
138146 } ) ;
139147
@@ -145,7 +153,7 @@ function generatePropertyPartials(properties, partialsDir) {
145153 const selectedTemplate = type === 'topic' ? topicTemplate : propertyTemplate ;
146154 const content = props . map ( p => selectedTemplate ( p ) ) . join ( '\n' ) ;
147155 const filename = `${ type } -properties.adoc` ;
148- fs . writeFileSync ( path . join ( propertiesPartialsDir , filename ) , AUTOGEN_NOTICE + content , 'utf8' ) ;
156+ fs . writeFileSync ( path . join ( propertiesPartialsDir , filename ) , AUTOGEN_NOTICE + content , 'utf8' ) ;
149157 console . log ( `✅ Generated ${ filename } (${ props . length } properties)` ) ;
150158 totalCount += props . length ;
151159 } ) ;
@@ -178,19 +186,18 @@ function generateDeprecatedDocs(properties, outputDir) {
178186 clusterProperties : clusterProperties . length ? clusterProperties : null
179187 } ;
180188
181- const output = template ( data ) ;
182189 const outputPath = process . env . OUTPUT_PARTIALS_DIR
183190 ? path . join ( process . env . OUTPUT_PARTIALS_DIR , 'deprecated' , 'deprecated-properties.adoc' )
184191 : path . join ( outputDir , 'partials' , 'deprecated' , 'deprecated-properties.adoc' ) ;
185192
186193 fs . mkdirSync ( path . dirname ( outputPath ) , { recursive : true } ) ;
187- fs . writeFileSync ( outputPath , AUTOGEN_NOTICE + output , 'utf8' ) ;
194+ fs . writeFileSync ( outputPath , AUTOGEN_NOTICE + template ( data ) , 'utf8' ) ;
188195 console . log ( `✅ Generated ${ outputPath } ` ) ;
189196 return deprecatedProperties . length ;
190197}
191198
192199/**
193- * Generate topic-property-mappings.adoc using the mappings template and topic properties.
200+ * Generate topic-property-mappings.adoc
194201 */
195202function generateTopicPropertyMappings ( properties , partialsDir ) {
196203 const templatesDir = path . join ( __dirname , 'templates' ) ;
@@ -218,31 +225,42 @@ function generateTopicPropertyMappings(properties, partialsDir) {
218225}
219226
220227/**
221- * Generate error reports for missing descriptions and deprecated properties.
228+ * Generate error reports for missing descriptions, deprecated, and undocumented properties.
222229 */
223- function generateErrorReports ( properties ) {
230+ function generateErrorReports ( properties , documentedProperties = [ ] ) {
224231 const emptyDescriptions = [ ] ;
225232 const deprecatedProperties = [ ] ;
233+ const allKeys = Object . keys ( properties ) ;
234+
235+ // Use documentedProperties array (property names that were rendered into partials)
236+ const documentedSet = new Set ( documentedProperties ) ;
237+ const undocumented = [ ] ;
226238
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 ) ;
239+ Object . entries ( properties ) . forEach ( ( [ key , p ] ) => {
240+ const name = p . name || key ;
241+ if ( ! p . description || ! p . description . trim ( ) ) emptyDescriptions . push ( name ) ;
242+ if ( p . is_deprecated ) deprecatedProperties . push ( name ) ;
243+ if ( ! documentedSet . has ( name ) ) undocumented . push ( name ) ;
230244 } ) ;
231245
232- const total = Object . keys ( properties ) . length ;
246+ const total = allKeys . length ;
233247 const pctEmpty = total ? ( ( emptyDescriptions . length / total ) * 100 ) . toFixed ( 2 ) : '0.00' ;
234248 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 } %)` ) ;
249+ const pctUndocumented = total ? ( ( undocumented . length / total ) * 100 ) . toFixed ( 2 ) : '0.00' ;
250+
251+ console . log ( `📉 Empty descriptions: ${ emptyDescriptions . length } (${ pctEmpty } %)` ) ;
252+ console . log ( `🕸️ Deprecated: ${ deprecatedProperties . length } (${ pctDeprecated } %)` ) ;
253+ console . log ( `🚫 Not documented: ${ undocumented . length } (${ pctUndocumented } %)` ) ;
237254
238255 return {
239256 empty_descriptions : emptyDescriptions . sort ( ) ,
240- deprecated_properties : deprecatedProperties . sort ( )
257+ deprecated_properties : deprecatedProperties . sort ( ) ,
258+ undocumented_properties : undocumented . sort ( ) ,
241259 } ;
242260}
243261
244262/**
245- * Main generator — only supports partials and deprecated docs.
263+ * Main generator
246264 */
247265function generateAllDocs ( inputFile , outputDir ) {
248266 const data = JSON . parse ( fs . readFileSync ( inputFile , 'utf8' ) ) ;
@@ -252,13 +270,49 @@ function generateAllDocs(inputFile, outputDir) {
252270
253271 let partialsCount = 0 ;
254272 let deprecatedCount = 0 ;
273+ const documentedProps = [ ] ; // Track which property names were rendered
255274
256275 if ( process . env . GENERATE_PARTIALS === '1' && process . env . OUTPUT_PARTIALS_DIR ) {
257276 console . log ( '📄 Generating property partials and deprecated docs...' ) ;
258277 deprecatedCount = generateDeprecatedDocs ( properties , outputDir ) ;
259- partialsCount = generatePropertyPartials ( properties , process . env . OUTPUT_PARTIALS_DIR ) ;
260278
261- // Generate topic-property-mappings.adoc
279+ // Wrap generatePropertyPartials to also collect property names
280+ const originalWrite = fs . writeFileSync ;
281+ const propertyTemplate = handlebars . compile (
282+ fs . readFileSync ( getTemplatePath ( path . join ( __dirname , 'templates' , 'property.hbs' ) , 'TEMPLATE_PROPERTY' ) , 'utf8' )
283+ ) ;
284+ const topicTemplate = handlebars . compile (
285+ fs . readFileSync ( getTemplatePath ( path . join ( __dirname , 'templates' , 'topic-property.hbs' ) , 'TEMPLATE_TOPIC_PROPERTY' ) , 'utf8' )
286+ ) ;
287+
288+ const propertiesPartialsDir = path . join ( process . env . OUTPUT_PARTIALS_DIR , 'properties' ) ;
289+ fs . mkdirSync ( propertiesPartialsDir , { recursive : true } ) ;
290+
291+ const propertyGroups = { cluster : [ ] , topic : [ ] , broker : [ ] , 'object-storage' : [ ] } ;
292+ Object . values ( properties ) . forEach ( p => {
293+ if ( ! p . name || ! p . config_scope ) return ;
294+ if ( p . config_scope === 'topic' ) propertyGroups . topic . push ( p ) ;
295+ else if ( p . config_scope === 'broker' ) propertyGroups . broker . push ( p ) ;
296+ else if ( p . config_scope === 'cluster' ) {
297+ if ( isObjectStorageProperty ( p ) ) propertyGroups [ 'object-storage' ] . push ( p ) ;
298+ else propertyGroups . cluster . push ( p ) ;
299+ }
300+ } ) ;
301+
302+ Object . entries ( propertyGroups ) . forEach ( ( [ type , props ] ) => {
303+ if ( props . length === 0 ) return ;
304+ props . sort ( ( a , b ) => String ( a . name || '' ) . localeCompare ( String ( b . name || '' ) ) ) ;
305+ const selectedTemplate = type === 'topic' ? topicTemplate : propertyTemplate ;
306+ const content = props . map ( p => {
307+ documentedProps . push ( p . name ) ;
308+ return selectedTemplate ( p ) ;
309+ } ) . join ( '\n' ) ;
310+ const filename = `${ type } -properties.adoc` ;
311+ originalWrite ( path . join ( propertiesPartialsDir , filename ) , AUTOGEN_NOTICE + content , 'utf8' ) ;
312+ console . log ( `✅ Generated ${ filename } (${ props . length } properties)` ) ;
313+ partialsCount += props . length ;
314+ } ) ;
315+
262316 try {
263317 generateTopicPropertyMappings ( properties , process . env . OUTPUT_PARTIALS_DIR ) ;
264318 } catch ( err ) {
@@ -268,24 +322,34 @@ function generateAllDocs(inputFile, outputDir) {
268322 console . log ( '📄 Skipping partial generation (set GENERATE_PARTIALS=1 and OUTPUT_PARTIALS_DIR to enable)' ) ;
269323 }
270324
271- const errors = generateErrorReports ( properties ) ;
272- const inputData = JSON . parse ( fs . readFileSync ( inputFile , 'utf8' ) ) ;
273- inputData . empty_descriptions = errors . empty_descriptions ;
274- inputData . deprecated_properties = errors . deprecated_properties ;
275- fs . writeFileSync ( inputFile , JSON . stringify ( inputData , null , 2 ) , 'utf8' ) ;
325+ const errors = generateErrorReports ( properties , documentedProps ) ;
326+
327+ const totalProperties = Object . keys ( properties ) . length ;
328+ const notRendered = errors . undocumented_properties . length ;
329+ const pctRendered = totalProperties
330+ ? ( ( partialsCount / totalProperties ) * 100 ) . toFixed ( 2 )
331+ : '0.00' ;
276332
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 } ` ) ;
333+ console . log ( '\n📊 Summary:' ) ;
334+ console . log ( ` 🧱 Total properties found: ${ totalProperties } ` ) ;
335+ console . log ( ` 🧩 Property partials generated: ${ partialsCount } (${ pctRendered } % of total)` ) ;
336+ console . log ( ` 🚫 Not documented: ${ notRendered } ` ) ;
337+ console . log ( ` 🕸️ Deprecated properties: ${ deprecatedCount } ` ) ;
338+
339+ if ( notRendered > 0 ) {
340+ console . log ( '⚠️ Undocumented properties:\n ' + errors . undocumented_properties . join ( '\n ' ) ) ;
341+ }
281342
282343 return {
283- totalProperties : Object . keys ( properties ) . length ,
284- propertyPartials : partialsCount ,
285- deprecatedProperties : deprecatedCount
344+ totalProperties,
345+ generatedPartials : partialsCount ,
346+ undocumentedProperties : errors . undocumented_properties ,
347+ deprecatedProperties : deprecatedCount ,
348+ percentageRendered : pctRendered
286349 } ;
287350}
288351
352+
289353module . exports = {
290354 generateAllDocs,
291355 generateDeprecatedDocs,
0 commit comments