@redpanda-data/docs-extensions-and-macros 4.15.7 → 4.15.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redpanda-data/docs-extensions-and-macros",
3
- "version": "4.15.7",
3
+ "version": "4.15.9",
4
4
  "description": "Antora extensions and macros developed for Redpanda documentation.",
5
5
  "keywords": [
6
6
  "antora",
@@ -991,6 +991,14 @@ def _apply_override_to_existing_property(property_dict, override, overrides_file
991
991
  if "category" in override:
992
992
  property_dict["category"] = override["category"]
993
993
 
994
+ # Apply accepted_values override - replaces enum field to filter displayed values
995
+ # Use case: Exclude internal-only enum values (e.g., greedy mode for leader_balancer_mode)
996
+ if "accepted_values" in override:
997
+ if isinstance(override["accepted_values"], list):
998
+ property_dict["enum"] = override["accepted_values"]
999
+ else:
1000
+ logger.warning(f"accepted_values for property must be an array")
1001
+
994
1002
 
995
1003
  def _create_property_from_override(prop_name, override, overrides_file_path):
996
1004
  """Create a new property from override specification."""
@@ -1025,9 +1033,10 @@ def _create_property_from_override(prop_name, override, overrides_file_path):
1025
1033
 
1026
1034
  # Add any other custom fields from override
1027
1035
  for key, value in override.items():
1028
- if key not in ["description", "type", "default", "config_scope", "version",
1029
- "example", "example_file", "example_yaml", "related_topics",
1030
- "is_deprecated", "visibility"]:
1036
+ if key not in ["description", "type", "default", "config_scope", "version",
1037
+ "example", "example_file", "example_yaml", "related_topics",
1038
+ "is_deprecated", "visibility", "exclude_from_docs", "category",
1039
+ "accepted_values", "_comment"]:
1031
1040
  new_property[key] = value
1032
1041
 
1033
1042
  # Add exclude_from_docs if specified
@@ -1037,7 +1046,14 @@ def _create_property_from_override(prop_name, override, overrides_file_path):
1037
1046
  # Add category if specified
1038
1047
  if "category" in override:
1039
1048
  new_property["category"] = override["category"]
1040
-
1049
+
1050
+ # Add accepted_values as enum field if specified
1051
+ if "accepted_values" in override:
1052
+ if isinstance(override["accepted_values"], list):
1053
+ new_property["enum"] = override["accepted_values"]
1054
+ else:
1055
+ logger.warning(f"accepted_values for property '{prop_name}' must be an array")
1056
+
1041
1057
  return new_property
1042
1058
 
1043
1059
 
@@ -247,6 +247,7 @@ function getConnectorList(binaryPath) {
247
247
 
248
248
  let result;
249
249
  if (needsDocker) {
250
+ console.log(` Using Docker to run Linux binary on ${os.platform()}`);
250
251
  // Use Docker to run Linux binaries on macOS/Windows
251
252
  const binaryDir = path.dirname(binaryPath);
252
253
  const binaryFile = path.basename(binaryPath);
@@ -268,6 +269,7 @@ function getConnectorList(binaryPath) {
268
269
  maxBuffer: 10 * 1024 * 1024 // 10MB buffer
269
270
  });
270
271
  } else {
272
+ console.log(` Running natively on ${os.platform()}`);
271
273
  // Run natively
272
274
  result = spawnSync(binaryPath, ['list', '--format', 'json-full'], {
273
275
  stdio: ['ignore', 'pipe', 'ignore'],
@@ -472,6 +472,28 @@ async function generateRpcnConnectorDocs(options) {
472
472
  if (!Array.isArray(items)) continue;
473
473
  const outRoot = path.join(outputRoot, folder);
474
474
  fs.mkdirSync(outRoot, { recursive: true });
475
+
476
+ // Get current item names
477
+ const currentNames = new Set(items.filter(fn => fn.name).map(fn => fn.name));
478
+
479
+ // Delete partials that no longer exist in the data
480
+ if (fs.existsSync(outRoot)) {
481
+ const existingFiles = fs.readdirSync(outRoot).filter(f => f.endsWith('.adoc'));
482
+ for (const file of existingFiles) {
483
+ const name = file.replace('.adoc', '');
484
+ // Skip hand-authored files (by convention, start with underscore)
485
+ if (name.startsWith('_')) {
486
+ console.log(`Skipping hand-authored file: ${file}`);
487
+ continue;
488
+ }
489
+ if (!currentNames.has(name)) {
490
+ const filePath = path.join(outRoot, file);
491
+ fs.unlinkSync(filePath);
492
+ console.log(`Deleted removed ${folder} partial: ${file}`);
493
+ }
494
+ }
495
+ }
496
+
475
497
  // Use custom or default template
476
498
  const bloblangTemplatePath = templateBloblang || path.resolve(__dirname, './templates/bloblang-function.hbs');
477
499
  const bloblangTemplate = handlebars.compile(fs.readFileSync(bloblangTemplatePath, 'utf8'));
@@ -484,6 +506,71 @@ async function generateRpcnConnectorDocs(options) {
484
506
  partialFiles.push(path.relative(process.cwd(), outPath));
485
507
  }
486
508
  }
509
+
510
+ // Generate overview pages for Bloblang methods and functions
511
+ const guidesRoot = path.join(process.cwd(), 'modules/guides/pages/bloblang');
512
+ fs.mkdirSync(guidesRoot, { recursive: true });
513
+
514
+ // Generate methods.adoc with categorized methods
515
+ if (dataObj['bloblang-methods']) {
516
+ const methodsData = dataObj['bloblang-methods'];
517
+ const categoriesMap = new Map();
518
+
519
+ // Extract categories and their methods
520
+ for (const method of methodsData) {
521
+ if (!method.name || !method.categories || !Array.isArray(method.categories)) continue;
522
+
523
+ for (const cat of method.categories) {
524
+ const categoryName = cat.Category;
525
+ if (!categoryName) continue;
526
+
527
+ if (!categoriesMap.has(categoryName)) {
528
+ categoriesMap.set(categoryName, []);
529
+ }
530
+ categoriesMap.get(categoryName).push(method.name);
531
+ }
532
+ }
533
+
534
+ // Sort categories and prepare data for template
535
+ const categories = Array.from(categoriesMap.entries())
536
+ .map(([name, methods]) => ({
537
+ name,
538
+ methods: methods.sort()
539
+ }))
540
+ .sort((a, b) => {
541
+ // Special ordering: General first, Deprecated last
542
+ if (a.name === 'General') return -1;
543
+ if (b.name === 'General') return 1;
544
+ if (a.name === 'Deprecated') return 1;
545
+ if (b.name === 'Deprecated') return -1;
546
+ return a.name.localeCompare(b.name);
547
+ });
548
+
549
+ const methodsTemplatePath = path.resolve(__dirname, './templates/bloblang-methods-overview.hbs');
550
+ const methodsTemplate = handlebars.compile(fs.readFileSync(methodsTemplatePath, 'utf8'));
551
+ const methodsAdoc = methodsTemplate({ categories });
552
+ const methodsOutPath = path.join(guidesRoot, 'methods.adoc');
553
+ fs.writeFileSync(methodsOutPath, methodsAdoc, 'utf8');
554
+ console.log('Generated methods.adoc overview page');
555
+ partialFiles.push(path.relative(process.cwd(), methodsOutPath));
556
+ }
557
+
558
+ // Generate functions.adoc with all functions
559
+ if (dataObj['bloblang-functions']) {
560
+ const functionsData = dataObj['bloblang-functions'];
561
+ const functionNames = functionsData
562
+ .filter(fn => fn.name)
563
+ .map(fn => fn.name)
564
+ .sort();
565
+
566
+ const functionsTemplatePath = path.resolve(__dirname, './templates/bloblang-functions-overview.hbs');
567
+ const functionsTemplate = handlebars.compile(fs.readFileSync(functionsTemplatePath, 'utf8'));
568
+ const functionsAdoc = functionsTemplate({ functions: functionNames });
569
+ const functionsOutPath = path.join(guidesRoot, 'functions.adoc');
570
+ fs.writeFileSync(functionsOutPath, functionsAdoc, 'utf8');
571
+ console.log('Generated functions.adoc overview page');
572
+ partialFiles.push(path.relative(process.cwd(), functionsOutPath));
573
+ }
487
574
  }
488
575
 
489
576
  // Common/Advanced config snippet YAMLs in modules/components/examples
@@ -1,10 +1,30 @@
1
1
  // Bloblang example formatting helper for Handlebars
2
2
  function bloblangExample(example) {
3
3
  if (typeof example === 'object' && example !== null && example.mapping) {
4
+ let leadIn = '';
4
5
  let codeBlock = '';
6
+
7
+ // Extract summary as lead-in prose (not a comment in code)
5
8
  if (example.summary && example.summary.trim()) {
6
- codeBlock += `# ${example.summary.trim().replace(/\n/g, '\n# ')}\n\n`;
9
+ let summary = example.summary.trim();
10
+
11
+ // Convert Markdown headings to AsciiDoc
12
+ // ##### Heading -> ==== Heading (H5 -> H4 in AsciiDoc)
13
+ summary = summary.replace(/^#####\s+(.+)$/gm, '==== $1');
14
+ // #### Heading -> === Heading (H4 -> H3 in AsciiDoc)
15
+ summary = summary.replace(/^####\s+(.+)$/gm, '=== $1');
16
+ // ### Heading -> == Heading (H3 -> H2 in AsciiDoc)
17
+ summary = summary.replace(/^###\s+(.+)$/gm, '== $1');
18
+
19
+ // Ensure lead-in ends with a colon (replace period/exclamation/question mark if present)
20
+ if (summary.endsWith('.') || summary.endsWith('!') || summary.endsWith('?')) {
21
+ summary = summary.slice(0, -1) + ':';
22
+ } else if (!summary.endsWith(':')) {
23
+ summary += ':';
24
+ }
25
+ leadIn = summary + '\n\n';
7
26
  }
27
+
8
28
  if (typeof example.mapping === 'string') {
9
29
  codeBlock += example.mapping.trim() + '\n';
10
30
  }
@@ -15,7 +35,7 @@ function bloblangExample(example) {
15
35
  }
16
36
  }
17
37
  }
18
- return `[,coffeescript]\n----\n${codeBlock.trim()}\n----\n`;
38
+ return `${leadIn}[,bloblang]\n----\n${codeBlock.trim()}\n----\n`;
19
39
  } else {
20
40
  let exStr = '';
21
41
  if (typeof example === 'string') {
@@ -35,7 +55,7 @@ function bloblangExample(example) {
35
55
  } else {
36
56
  exStr = String(example);
37
57
  }
38
- return `[source,coffeescript]\n----\n${exStr}\n----\n`;
58
+ return `[source,bloblang]\n----\n${exStr}\n----\n`;
39
59
  }
40
60
  }
41
61
 
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Ensures a text string ends with a period (or other terminal punctuation).
3
+ * Used for normalizing Bloblang descriptions.
4
+ *
5
+ * @param {string} text - The text to process
6
+ * @returns {string} Text ending with appropriate punctuation
7
+ */
8
+ function ensurePeriod(text) {
9
+ if (!text || typeof text !== 'string') {
10
+ return text;
11
+ }
12
+
13
+ const trimmed = text.trim();
14
+ if (!trimmed) {
15
+ return text;
16
+ }
17
+
18
+ // Check if already ends with terminal punctuation
19
+ if (trimmed.endsWith('.') || trimmed.endsWith('!') || trimmed.endsWith('?')) {
20
+ return text;
21
+ }
22
+
23
+ // Add period
24
+ return text.trim() + '.';
25
+ }
26
+
27
+ module.exports = ensurePeriod;
@@ -0,0 +1,7 @@
1
+ // Check if any parameters are optional
2
+ function hasOptionalParams(params) {
3
+ if (!Array.isArray(params)) return false;
4
+ return params.some(param => param.is_optional);
5
+ }
6
+
7
+ module.exports = hasOptionalParams;
@@ -17,4 +17,7 @@ module.exports = {
17
17
  commonConfig: require('./commonConfig.js'),
18
18
  advancedConfig: require('./advancedConfig.js'),
19
19
  bloblangExample: require('./bloblangExample.js'),
20
+ hasOptionalParams: require('./hasOptionalParams.js'),
21
+ ensurePeriod: require('./ensurePeriod.js'),
22
+ toSentenceCase: require('./toSentenceCase.js'),
20
23
  };
@@ -0,0 +1,69 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * Convert a string to sentence case, preserving acronyms and proper nouns
5
+ * @param {string} text - The text to convert
6
+ * @returns {string} The text in sentence case
7
+ */
8
+ function toSentenceCase(text) {
9
+ if (!text) return ''
10
+
11
+ // Map of exact word matches to preserve (case-sensitive)
12
+ const exactPreserve = new Map([
13
+ ['geoip', 'GeoIP'],
14
+ ['GEOIP', 'GeoIP'],
15
+ ['GeoIP', 'GeoIP']
16
+ ])
17
+
18
+ // List of acronyms to preserve (will be uppercased)
19
+ const preserveWords = new Set([
20
+ 'SQL',
21
+ 'JSON',
22
+ 'JWT',
23
+ 'XML',
24
+ 'HTML',
25
+ 'URL',
26
+ 'URI',
27
+ 'HTTP',
28
+ 'HTTPS',
29
+ 'TLS',
30
+ 'SSL',
31
+ 'AWS',
32
+ 'GCP',
33
+ 'API',
34
+ 'ID',
35
+ 'UUID',
36
+ 'CSV'
37
+ ])
38
+
39
+ // Split into words
40
+ const words = text.split(/\s+/)
41
+
42
+ return words.map((word, index) => {
43
+ // Check if word is in exact preserve map first
44
+ if (exactPreserve.has(word) || exactPreserve.has(word.toLowerCase()) || exactPreserve.has(word.toUpperCase())) {
45
+ const key = exactPreserve.has(word) ? word : (exactPreserve.has(word.toLowerCase()) ? word.toLowerCase() : word.toUpperCase())
46
+ return exactPreserve.get(key)
47
+ }
48
+
49
+ // Check if word is in preserve list (case-insensitive check)
50
+ if (preserveWords.has(word.toUpperCase())) {
51
+ return word.toUpperCase()
52
+ }
53
+
54
+ // Check if word contains special characters like &
55
+ if (word === '&') {
56
+ return word
57
+ }
58
+
59
+ // First word: capitalize first letter, lowercase rest
60
+ if (index === 0) {
61
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
62
+ }
63
+
64
+ // Subsequent words: lowercase
65
+ return word.toLowerCase()
66
+ }).join(' ')
67
+ }
68
+
69
+ module.exports = toSentenceCase