@redpanda-data/docs-extensions-and-macros 4.12.2 → 4.12.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 CHANGED
@@ -480,6 +480,42 @@ function runClusterDocs(mode, tag, options) {
480
480
  if (r.status !== 0) process.exit(r.status);
481
481
  }
482
482
 
483
+ /**
484
+ * Cleanup old diff files, keeping only the 2 most recent.
485
+ *
486
+ * @param {string} diffDir - Directory containing diff files
487
+ */
488
+ function cleanupOldDiffs(diffDir) {
489
+ try {
490
+ console.log('Cleaning up old diff JSON files (keeping only 2 most recent)…');
491
+
492
+ const absoluteDiffDir = path.resolve(diffDir);
493
+ if (!fs.existsSync(absoluteDiffDir)) {
494
+ return;
495
+ }
496
+
497
+ // Get all diff files sorted by modification time (newest first)
498
+ const files = fs.readdirSync(absoluteDiffDir)
499
+ .filter(file => file.startsWith('redpanda-property-changes-') && file.endsWith('.json'))
500
+ .map(file => ({
501
+ name: file,
502
+ path: path.join(absoluteDiffDir, file),
503
+ time: fs.statSync(path.join(absoluteDiffDir, file)).mtime.getTime()
504
+ }))
505
+ .sort((a, b) => b.time - a.time);
506
+
507
+ // Delete all but the 2 most recent
508
+ if (files.length > 2) {
509
+ files.slice(2).forEach(file => {
510
+ console.log(` Removing old file: ${file.name}`);
511
+ fs.unlinkSync(file.path);
512
+ });
513
+ }
514
+ } catch (error) {
515
+ console.error(` Failed to cleanup old diff files: ${error.message}`);
516
+ }
517
+ }
518
+
483
519
  /**
484
520
  * Generate a detailed JSON report describing property changes between two releases.
485
521
  *
@@ -499,12 +535,13 @@ function runClusterDocs(mode, tag, options) {
499
535
  function generatePropertyComparisonReport(oldTag, newTag, outputDir) {
500
536
  try {
501
537
  console.log(`\nšŸ“Š Generating detailed property comparison report...`);
502
-
503
- // Look for the property JSON files in outputDir/examples
538
+
539
+ // Look for the property JSON files in the standard location (modules/reference/attachments)
540
+ // regardless of where we're saving the diff output
504
541
  const repoRoot = findRepoRoot();
505
- const examplesDir = path.join(repoRoot, outputDir, 'examples');
506
- const oldJsonPath = path.join(examplesDir, `${oldTag}-properties.json`);
507
- const newJsonPath = path.join(examplesDir, `${newTag}-properties.json`);
542
+ const attachmentsDir = path.join(repoRoot, 'modules/reference/attachments');
543
+ const oldJsonPath = path.join(attachmentsDir, `redpanda-properties-${oldTag}.json`);
544
+ const newJsonPath = path.join(attachmentsDir, `redpanda-properties-${newTag}.json`);
508
545
 
509
546
  // Check if JSON files exist
510
547
  if (!fs.existsSync(oldJsonPath)) {
@@ -526,7 +563,7 @@ function generatePropertyComparisonReport(oldTag, newTag, outputDir) {
526
563
  // Run the property comparison tool with descriptive filename
527
564
  const propertyExtractorDir = path.resolve(__dirname, '../tools/property-extractor');
528
565
  const compareScript = path.join(propertyExtractorDir, 'compare-properties.js');
529
- const reportFilename = `property-changes-${oldTag}-to-${newTag}.json`;
566
+ const reportFilename = `redpanda-property-changes-${oldTag}-to-${newTag}.json`;
530
567
  const reportPath = path.join(absoluteOutputDir, reportFilename);
531
568
  const args = [compareScript, oldJsonPath, newJsonPath, oldTag, newTag, absoluteOutputDir, reportFilename];
532
569
 
@@ -858,6 +895,26 @@ automation
858
895
 
859
896
  const newIndex = JSON.parse(fs.readFileSync(dataFile, 'utf8'));
860
897
 
898
+ // Check if versions match - skip diff and updates if so
899
+ if (oldVersion && newVersion && oldVersion === newVersion) {
900
+ console.log(`\nāœ“ Already at version ${newVersion}`);
901
+ console.log(' No diff or version updates needed.\n');
902
+
903
+ console.log('šŸ“Š Generation Report:');
904
+ console.log(` • Partial files: ${partialsWritten}`);
905
+ const fieldsPartials = partialFiles.filter(fp => fp.includes('/fields/'));
906
+ const examplesPartials = partialFiles.filter(fp => fp.includes('/examples/'));
907
+
908
+ console.log(` • Fields partials: ${fieldsPartials.length}`);
909
+ console.log(` • Examples partials: ${examplesPartials.length}`);
910
+
911
+ if (options.draftMissing && draftsWritten) {
912
+ console.log(` • Draft files: ${draftsWritten}`);
913
+ }
914
+
915
+ process.exit(0);
916
+ }
917
+
861
918
  // Publish merged version with overrides to modules/components/attachments
862
919
  if (options.overrides && fs.existsSync(options.overrides)) {
863
920
  try {
@@ -1073,65 +1130,144 @@ automation
1073
1130
  endIdx = nextMatch ? startIdx + 1 + nextMatch.index : whatsNew.length;
1074
1131
  }
1075
1132
  // Compose new section
1076
- let section = `\n== Version ${diff.comparison.newVersion}\n\n=== Component updates\n\n`;
1077
- // Add link to full release notes for this connector version after version heading and before component updates
1133
+ // Add link to full release notes for this connector version after version heading
1078
1134
  let releaseNotesLink = '';
1079
1135
  if (diff.comparison && diff.comparison.newVersion) {
1080
1136
  releaseNotesLink = `link:https://github.com/redpanda-data/connect/releases/tag/v${diff.comparison.newVersion}[See the full release notes^].\n\n`;
1081
1137
  }
1082
- section = `\n== Version ${diff.comparison.newVersion}\n\n${releaseNotesLink}=== Component updates\n\n`;
1083
- // New components
1138
+ let section = `\n== Version ${diff.comparison.newVersion}\n\n${releaseNotesLink}`;
1139
+
1140
+ // Separate Bloblang components from regular components
1141
+ const bloblangComponents = [];
1142
+ const regularComponents = [];
1143
+
1084
1144
  if (diff.details.newComponents && diff.details.newComponents.length) {
1085
- section += 'This release adds the following new components:\n\n';
1086
- // Group by type
1087
- const byType = {};
1088
1145
  for (const comp of diff.details.newComponents) {
1146
+ if (comp.type === 'bloblang-functions' || comp.type === 'bloblang-methods') {
1147
+ bloblangComponents.push(comp);
1148
+ } else {
1149
+ regularComponents.push(comp);
1150
+ }
1151
+ }
1152
+ }
1153
+
1154
+ // Bloblang updates section
1155
+ if (bloblangComponents.length > 0) {
1156
+ section += '=== Bloblang updates\n\n';
1157
+ section += 'This release adds the following new Bloblang capabilities:\n\n';
1158
+
1159
+ // Group by type (functions vs methods)
1160
+ const byType = {};
1161
+ for (const comp of bloblangComponents) {
1089
1162
  if (!byType[comp.type]) byType[comp.type] = [];
1090
1163
  byType[comp.type].push(comp);
1091
1164
  }
1165
+
1092
1166
  for (const [type, comps] of Object.entries(byType)) {
1093
- section += `* ${type.charAt(0).toUpperCase() + type.slice(1)}:\n`;
1094
- for (const comp of comps) {
1095
- section += `** xref:components:${type}/${comp.name}.adoc[\`${comp.name}\`]`;
1096
- if (comp.status) section += ` (${comp.status})`;
1097
- if (comp.description) section += `: ${capToTwoSentences(comp.description)}`;
1098
- section += '\n';
1167
+ if (type === 'bloblang-functions') {
1168
+ section += '* Functions:\n';
1169
+ for (const comp of comps) {
1170
+ section += `** xref:guides:bloblang/functions.adoc#${comp.name}[\`${comp.name}\`]`;
1171
+ if (comp.status && comp.status !== 'stable') section += ` (${comp.status})`;
1172
+ if (comp.description) section += `: ${capToTwoSentences(comp.description)}`;
1173
+ section += '\n';
1174
+ }
1175
+ } else if (type === 'bloblang-methods') {
1176
+ section += '* Methods:\n';
1177
+ for (const comp of comps) {
1178
+ section += `** xref:guides:bloblang/methods.adoc#${comp.name}[\`${comp.name}\`]`;
1179
+ if (comp.status && comp.status !== 'stable') section += ` (${comp.status})`;
1180
+ if (comp.description) section += `: ${capToTwoSentences(comp.description)}`;
1181
+ section += '\n';
1182
+ }
1099
1183
  }
1100
1184
  }
1185
+ section += '\n';
1101
1186
  }
1102
1187
 
1103
- // New fields
1104
- if (diff.details.newFields && diff.details.newFields.length) {
1105
- section += '\nThis release adds support for the following new fields:\n\n';
1106
- // Group new fields by component type
1107
- const fieldsByType = {};
1108
- for (const field of diff.details.newFields) {
1109
- // component: "inputs:kafka", field: "timely_nacks_maximum_wait"
1110
- const [type, compName] = field.component.split(':');
1111
- if (!fieldsByType[type]) fieldsByType[type] = [];
1112
- fieldsByType[type].push({
1113
- compName,
1114
- field: field.field,
1115
- description: field.description || '',
1116
- });
1188
+ // Regular component updates section
1189
+ if (regularComponents.length > 0) {
1190
+ section += '=== Component updates\n\n';
1191
+ section += 'This release adds the following new components:\n\n';
1192
+
1193
+ section += '[cols="1m,1,1,3"]\n';
1194
+ section += '|===\n';
1195
+ section += '|Component |Type |Status |Description\n\n';
1196
+
1197
+ for (const comp of regularComponents) {
1198
+ const typeLabel = comp.type.charAt(0).toUpperCase() + comp.type.slice(1);
1199
+ const statusLabel = comp.status || '-';
1200
+ const desc = comp.description ? capToTwoSentences(comp.description) : '-';
1201
+
1202
+ section += `|xref:components:${comp.type}/${comp.name}.adoc[${comp.name}]\n`;
1203
+ section += `|${typeLabel}\n`;
1204
+ section += `|${statusLabel}\n`;
1205
+ section += `|${desc}\n\n`;
1117
1206
  }
1118
- for (const [type, fields] of Object.entries(fieldsByType)) {
1119
- section += `* ${type.charAt(0).toUpperCase() + type.slice(1)}:\n`;
1120
- // Group by component name
1121
- const byComp = {};
1122
- for (const f of fields) {
1123
- if (!byComp[f.compName]) byComp[f.compName] = [];
1124
- byComp[f.compName].push(f);
1207
+
1208
+ section += '|===\n\n';
1209
+ }
1210
+
1211
+ // New fields (exclude Bloblang functions/methods)
1212
+ if (diff.details.newFields && diff.details.newFields.length) {
1213
+ // Filter out Bloblang components
1214
+ const regularFields = diff.details.newFields.filter(field => {
1215
+ const [type] = field.component.split(':');
1216
+ return type !== 'bloblang-functions' && type !== 'bloblang-methods';
1217
+ });
1218
+
1219
+ if (regularFields.length > 0) {
1220
+ section += '\n=== New field support\n\n';
1221
+ section += 'This release adds support for the following new fields:\n\n';
1222
+
1223
+ // Group by field name
1224
+ const byField = {};
1225
+ for (const field of regularFields) {
1226
+ const [type, compName] = field.component.split(':');
1227
+ if (!byField[field.field]) {
1228
+ byField[field.field] = {
1229
+ description: field.description,
1230
+ components: []
1231
+ };
1232
+ }
1233
+ byField[field.field].components.push({ type, name: compName });
1125
1234
  }
1126
- for (const [comp, compFields] of Object.entries(byComp)) {
1127
- section += `** xref:components:${type}/${comp}.adoc[\`${comp}\`]:`;
1128
- section += '\n';
1129
- for (const f of compFields) {
1130
- section += `*** xref:components:${type}/${comp}.adoc#${f.field}[\`${f.field}\`]`;
1131
- if (f.description) section += `: ${capToTwoSentences(f.description)}`;
1132
- section += '\n';
1235
+
1236
+ section += '[cols="1m,3,2a"]\n';
1237
+ section += '|===\n';
1238
+ section += '|Field |Description |Affected components\n\n';
1239
+
1240
+ for (const [fieldName, info] of Object.entries(byField)) {
1241
+ // Format component list - group by type
1242
+ const byType = {};
1243
+ for (const comp of info.components) {
1244
+ if (!byType[comp.type]) byType[comp.type] = [];
1245
+ byType[comp.type].push(comp.name);
1246
+ }
1247
+
1248
+ let componentList = '';
1249
+ for (const [type, names] of Object.entries(byType)) {
1250
+ if (componentList) componentList += '\n\n';
1251
+
1252
+ // Smart pluralization: don't add 's' if already plural
1253
+ const typeLabel = names.length === 1
1254
+ ? type.charAt(0).toUpperCase() + type.slice(1)
1255
+ : type.charAt(0).toUpperCase() + type.slice(1) + (type.endsWith('s') ? '' : 's');
1256
+
1257
+ componentList += `*${typeLabel}:*\n\n`;
1258
+ names.forEach(name => {
1259
+ componentList += `* xref:components:${type}/${name}.adoc#${fieldName}[${name}]\n`;
1260
+ });
1133
1261
  }
1262
+
1263
+ const desc = info.description ? capToTwoSentences(info.description) : '-';
1264
+
1265
+ section += `|${fieldName}\n`;
1266
+ section += `|${desc}\n`;
1267
+ section += `|${componentList}\n\n`;
1134
1268
  }
1269
+
1270
+ section += '|===\n\n';
1135
1271
  }
1136
1272
  }
1137
1273
 
@@ -1139,56 +1275,180 @@ automation
1139
1275
  if (diff.details.deprecatedComponents && diff.details.deprecatedComponents.length) {
1140
1276
  section += '\n=== Deprecations\n\n';
1141
1277
  section += 'The following components are now deprecated:\n\n';
1142
- // Group by type
1143
- const byType = {};
1278
+
1279
+ section += '[cols="1m,1,3"]\n';
1280
+ section += '|===\n';
1281
+ section += '|Component |Type |Description\n\n';
1282
+
1144
1283
  for (const comp of diff.details.deprecatedComponents) {
1145
- if (!byType[comp.type]) byType[comp.type] = [];
1146
- byType[comp.type].push(comp);
1147
- }
1148
- for (const [type, comps] of Object.entries(byType)) {
1149
- section += `* ${type.charAt(0).toUpperCase() + type.slice(1)}:\n`;
1150
- for (const comp of comps) {
1151
- section += `** xref:components:${type}/${comp.name}.adoc[\`${comp.name}\`]\n`;
1284
+ const typeLabel = comp.type.charAt(0).toUpperCase() + comp.type.slice(1);
1285
+ const desc = comp.description ? capToTwoSentences(comp.description) : '-';
1286
+
1287
+ if (comp.type === 'bloblang-functions') {
1288
+ section += `|xref:guides:bloblang/functions.adoc#${comp.name}[${comp.name}]\n`;
1289
+ } else if (comp.type === 'bloblang-methods') {
1290
+ section += `|xref:guides:bloblang/methods.adoc#${comp.name}[${comp.name}]\n`;
1291
+ } else {
1292
+ section += `|xref:components:${comp.type}/${comp.name}.adoc[${comp.name}]\n`;
1152
1293
  }
1294
+ section += `|${typeLabel}\n`;
1295
+ section += `|${desc}\n\n`;
1153
1296
  }
1297
+
1298
+ section += '|===\n\n';
1154
1299
  }
1155
1300
 
1156
- // Deprecated fields
1301
+ // Deprecated fields (exclude Bloblang functions/methods)
1157
1302
  if (diff.details.deprecatedFields && diff.details.deprecatedFields.length) {
1158
- if (!diff.details.deprecatedComponents || diff.details.deprecatedComponents.length === 0) {
1159
- section += '\n=== Deprecations\n\n';
1160
- }
1161
- section += '\nThe following fields are now deprecated:\n\n';
1162
- // Group deprecated fields by component type
1163
- const fieldsByType = {};
1164
- for (const field of diff.details.deprecatedFields) {
1165
- const [type, compName] = field.component.split(':');
1166
- if (!fieldsByType[type]) fieldsByType[type] = [];
1167
- fieldsByType[type].push({
1168
- compName,
1169
- field: field.field
1170
- });
1303
+ // Filter out Bloblang components
1304
+ const regularDeprecatedFields = diff.details.deprecatedFields.filter(field => {
1305
+ const [type] = field.component.split(':');
1306
+ return type !== 'bloblang-functions' && type !== 'bloblang-methods';
1307
+ });
1308
+
1309
+ if (regularDeprecatedFields.length > 0) {
1310
+ if (!diff.details.deprecatedComponents || diff.details.deprecatedComponents.length === 0) {
1311
+ section += '\n=== Deprecations\n\n';
1312
+ } else {
1313
+ section += '\n';
1314
+ }
1315
+ section += 'The following fields are now deprecated:\n\n';
1316
+
1317
+ // Group by field name
1318
+ const byField = {};
1319
+ for (const field of regularDeprecatedFields) {
1320
+ const [type, compName] = field.component.split(':');
1321
+ if (!byField[field.field]) {
1322
+ byField[field.field] = {
1323
+ description: field.description,
1324
+ components: []
1325
+ };
1326
+ }
1327
+ byField[field.field].components.push({ type, name: compName });
1328
+ }
1329
+
1330
+ section += '[cols="1m,3,2a"]\n';
1331
+ section += '|===\n';
1332
+ section += '|Field |Description |Affected components\n\n';
1333
+
1334
+ for (const [fieldName, info] of Object.entries(byField)) {
1335
+ // Format component list - group by type
1336
+ const byType = {};
1337
+ for (const comp of info.components) {
1338
+ if (!byType[comp.type]) byType[comp.type] = [];
1339
+ byType[comp.type].push(comp.name);
1340
+ }
1341
+
1342
+ let componentList = '';
1343
+ for (const [type, names] of Object.entries(byType)) {
1344
+ if (componentList) componentList += '\n\n';
1345
+
1346
+ // Smart pluralization: don't add 's' if already plural
1347
+ const typeLabel = names.length === 1
1348
+ ? type.charAt(0).toUpperCase() + type.slice(1)
1349
+ : type.charAt(0).toUpperCase() + type.slice(1) + (type.endsWith('s') ? '' : 's');
1350
+
1351
+ componentList += `*${typeLabel}:*\n\n`;
1352
+ names.forEach(name => {
1353
+ componentList += `* xref:components:${type}/${name}.adoc#${fieldName}[${name}]\n`;
1354
+ });
1355
+ }
1356
+
1357
+ const desc = info.description ? capToTwoSentences(info.description) : '-';
1358
+
1359
+ section += `|${fieldName}\n`;
1360
+ section += `|${desc}\n`;
1361
+ section += `|${componentList}\n\n`;
1362
+ }
1363
+
1364
+ section += '|===\n\n';
1171
1365
  }
1172
- for (const [type, fields] of Object.entries(fieldsByType)) {
1173
- section += `* ${type.charAt(0).toUpperCase() + type.slice(1)} components\n`;
1174
- // Group by component name
1175
- const byComp = {};
1176
- for (const f of fields) {
1177
- if (!byComp[f.compName]) byComp[f.compName] = [];
1178
- byComp[f.compName].push(f);
1366
+ }
1367
+
1368
+ // Changed defaults (exclude Bloblang functions/methods)
1369
+ if (diff.details.changedDefaults && diff.details.changedDefaults.length) {
1370
+ // Filter out Bloblang components
1371
+ const regularChangedDefaults = diff.details.changedDefaults.filter(change => {
1372
+ const [type] = change.component.split(':');
1373
+ return type !== 'bloblang-functions' && type !== 'bloblang-methods';
1374
+ });
1375
+
1376
+ if (regularChangedDefaults.length > 0) {
1377
+ section += '\n=== Default value changes\n\n';
1378
+ section += 'This release includes the following default value changes:\n\n';
1379
+
1380
+ // Group by field name and default values to avoid overwriting different default changes
1381
+ const byFieldAndDefaults = {};
1382
+ for (const change of regularChangedDefaults) {
1383
+ const [type, compName] = change.component.split(':');
1384
+ const compositeKey = `${change.field}|${String(change.oldDefault)}|${String(change.newDefault)}`;
1385
+ if (!byFieldAndDefaults[compositeKey]) {
1386
+ byFieldAndDefaults[compositeKey] = {
1387
+ field: change.field,
1388
+ oldDefault: change.oldDefault,
1389
+ newDefault: change.newDefault,
1390
+ description: change.description,
1391
+ components: []
1392
+ };
1393
+ }
1394
+ byFieldAndDefaults[compositeKey].components.push({
1395
+ type,
1396
+ name: compName
1397
+ });
1179
1398
  }
1180
- for (const [comp, compFields] of Object.entries(byComp)) {
1181
- section += `** xref:components:${type}/${comp}.adoc[\`${comp}\`]`;
1182
- if (compFields.length === 1) {
1183
- const f = compFields[0];
1184
- section += `: xref:components:${type}/${comp}.adoc#${f.field}[\`${f.field}\`]\n`;
1185
- } else {
1186
- section += '\n';
1187
- for (const f of compFields) {
1188
- section += `*** xref:components:${type}/${comp}.adoc#${f.field}[\`${f.field}\`]\n`;
1189
- }
1399
+
1400
+ // Create table
1401
+ section += '[cols="1m,1,1,3,2a"]\n';
1402
+ section += '|===\n';
1403
+ section += '|Field |Old default |New default |Description |Affected components\n\n';
1404
+
1405
+ for (const [compositeKey, info] of Object.entries(byFieldAndDefaults)) {
1406
+ // Format old and new defaults
1407
+ const formatDefault = (val) => {
1408
+ if (val === undefined || val === null) return 'none';
1409
+ if (typeof val === 'string') return val;
1410
+ if (typeof val === 'number' || typeof val === 'boolean') return String(val);
1411
+ return JSON.stringify(val);
1412
+ };
1413
+
1414
+ const oldVal = formatDefault(info.oldDefault);
1415
+ const newVal = formatDefault(info.newDefault);
1416
+
1417
+ // Get description
1418
+ const desc = info.description ? capToTwoSentences(info.description) : '-';
1419
+
1420
+ // Format component references - group by type
1421
+ const byType = {};
1422
+ for (const comp of info.components) {
1423
+ if (!byType[comp.type]) byType[comp.type] = [];
1424
+ byType[comp.type].push(comp.name);
1425
+ }
1426
+
1427
+ let componentList = '';
1428
+ for (const [type, names] of Object.entries(byType)) {
1429
+ if (componentList) componentList += '\n\n';
1430
+
1431
+ // Smart pluralization: don't add 's' if already plural
1432
+ const typeLabel = names.length === 1
1433
+ ? type.charAt(0).toUpperCase() + type.slice(1)
1434
+ : type.charAt(0).toUpperCase() + type.slice(1) + (type.endsWith('s') ? '' : 's');
1435
+
1436
+ componentList += `*${typeLabel}:*\n\n`;
1437
+
1438
+ // List components, with links to the field anchor
1439
+ names.forEach(name => {
1440
+ componentList += `* xref:components:${type}/${name}.adoc#${info.field}[${name}]\n`;
1441
+ });
1190
1442
  }
1443
+
1444
+ section += `|${info.field}\n`;
1445
+ section += `|${oldVal}\n`;
1446
+ section += `|${newVal}\n`;
1447
+ section += `|${desc}\n`;
1448
+ section += `|${componentList}\n\n`;
1191
1449
  }
1450
+
1451
+ section += '|===\n\n';
1192
1452
  }
1193
1453
  }
1194
1454
 
@@ -1302,7 +1562,7 @@ automation
1302
1562
  if (templates.deprecatedProperty) {
1303
1563
  env.TEMPLATE_DEPRECATED_PROPERTY = path.resolve(templates.deprecatedProperty);
1304
1564
  }
1305
- env.OUTPUT_JSON_DIR = path.resolve(outDir, 'examples');
1565
+ env.OUTPUT_JSON_DIR = path.resolve(outDir, 'attachments');
1306
1566
  env.OUTPUT_AUTOGENERATED_DIR = path.resolve(outDir);
1307
1567
  if (options.generatePartials) {
1308
1568
  env.GENERATE_PARTIALS = '1';
@@ -1330,7 +1590,12 @@ automation
1330
1590
  }
1331
1591
  make(newTag, overridesPath, templates, outputDir);
1332
1592
  if (oldTag && !tagsAreSame) {
1333
- generatePropertyComparisonReport(oldTag, newTag, outputDir);
1593
+ // Save diff to overrides directory if OVERRIDES is specified, otherwise to outputDir
1594
+ const diffOutputDir = overridesPath ? path.dirname(path.resolve(overridesPath)) : outputDir;
1595
+ generatePropertyComparisonReport(oldTag, newTag, diffOutputDir);
1596
+
1597
+ // Cleanup old diff files (keep only 2 most recent)
1598
+ cleanupOldDiffs(diffOutputDir);
1334
1599
  } else if (tagsAreSame) {
1335
1600
  console.log('--diff and --tag are the same. Skipping diff and Antora config update.');
1336
1601
  }
@@ -6,6 +6,9 @@ const path = require('path')
6
6
  const URL = require('url')
7
7
  const chalk = require('chalk')
8
8
 
9
+ // Create encoder once at module scope for efficiency
10
+ const textEncoder = new TextEncoder()
11
+
9
12
  /**
10
13
  * Generates an Algolia index:
11
14
  *
@@ -143,37 +146,50 @@ function generateIndex (playbook, contentCatalog, { indexLatestOnly = false, exc
143
146
  if (!(cname in algolia)) algolia[cname] = {}
144
147
  if (!(version in algolia[cname])) algolia[cname][version] = []
145
148
 
149
+ // Check if this is a properties reference page (or has many titles)
150
+ const isPropertiesPage = page.pub.url.includes('/properties/') || titles.length > 30;
151
+
146
152
  // Handle the article text
147
- const contentElements = article.querySelectorAll('p, table, li');
148
- let contentText = '';
149
- let currentSize = 0;
150
- // Maximum size in bytes
151
- const MAX_SIZE = 50000;
152
- const encoder = new TextEncoder();
153
- contentElements.forEach(element => {
154
- let elementText = '';
155
- if (element.tagName === 'TABLE') {
156
- element.querySelectorAll('tr').forEach(tr => {
157
- tr.querySelectorAll('td, th').forEach(cell => {
158
- elementText += cell.text + ' ';
159
- });
160
- });
161
- } else {
162
- elementText = decode(element.rawText);
163
- }
164
- const elementSize = encoder.encode(elementText).length;
165
- if (currentSize + elementSize > MAX_SIZE) {
166
- return;
167
- } else {
153
+ let text = '';
154
+
155
+ if (!isPropertiesPage) {
156
+ // For normal pages, index full text content
157
+ const contentElements = article.querySelectorAll('p, table, li');
158
+ let contentText = '';
159
+ let currentSize = 0;
160
+ // Maximum size in bytes (Algolia's limit is 100KB, using 50KB for safety)
161
+ const MAX_SIZE = 50000;
162
+
163
+ for (const element of contentElements) {
164
+ let elementText = '';
165
+ if (element.tagName === 'TABLE') {
166
+ for (const tr of element.querySelectorAll('tr')) {
167
+ for (const cell of tr.querySelectorAll('td, th')) {
168
+ elementText += cell.textContent + ' ';
169
+ }
170
+ }
171
+ } else {
172
+ elementText = element.textContent;
173
+ }
174
+
175
+ const elementSize = textEncoder.encode(elementText).length;
176
+ if (currentSize + elementSize > MAX_SIZE) {
177
+ break;
178
+ }
179
+
168
180
  contentText += elementText;
169
181
  currentSize += elementSize;
170
182
  }
171
- });
172
-
173
- var text = contentText.replace(/\n/g, ' ')
174
- .replace(/\r/g, ' ')
175
- .replace(/\s+/g, ' ')
176
- .trim();
183
+
184
+ text = contentText.replace(/\n/g, ' ')
185
+ .replace(/\r/g, ' ')
186
+ .replace(/\s+/g, ' ')
187
+ .trim();
188
+ } else {
189
+ // For long pages, only use intro as text (property names are already in titles array)
190
+ text = intro;
191
+ logger.info(`Skipping full text indexing for long page: ${page.pub.url} (${titles.length} properties)`);
192
+ }
177
193
 
178
194
  let tag;
179
195
  const title = (component.title || '').trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redpanda-data/docs-extensions-and-macros",
3
- "version": "4.12.2",
3
+ "version": "4.12.4",
4
4
  "description": "Antora extensions and macros developed for Redpanda documentation.",
5
5
  "keywords": [
6
6
  "antora",