UNPKG

@redpanda-data/docs-extensions-and-macros

Version:

Antora extensions and macros developed for Redpanda documentation.

194 lines (163 loc) 6.44 kB
'use strict'; const _ = require('lodash'); module.exports.register = function ({ config }) { const logger = this.getLogger('generate-index-data-extension'); this.on('documentsConverted', async ({ contentCatalog, siteCatalog }) => { // Ensure data.sets exists and is an object const setsConfig = _.get(config, 'data.sets', {}); if (!setsConfig || Object.keys(setsConfig).length === 0) { logger.info('No index sets defined in the configuration. Skipping index data generation.'); return; } try { // Process each defined index set for (const [setName, setParams] of Object.entries(setsConfig)) { logger.info(`Processing index set: ${setName}`); const { component, filter, env_type, output_file, attribute_name } = setParams; // Validate required parameters const requiredParams = ['component', 'attribute_name']; const missingParams = requiredParams.filter(param => !setParams[param]); if (missingParams.length > 0) { logger.warn(`One or more required parameters are missing: ${missingParams.join(', ')} for set "${setName}". Skipping.`); continue; } // Gather items based on the target version const items = gatherItems(contentCatalog, { component, filter, env_type, }, logger); if (!items.length) { continue; } const uniqueItems = deduplicateAndSortItems(items); await addDataAttributeToComponents(uniqueItems, contentCatalog, attribute_name, logger); if (output_file) createIndexFile(uniqueItems, siteCatalog, output_file, logger); } } catch (error) { logger.error(`Failed to generate indexes: ${error.stack}`); } }); }; /** * Gathers items (pages) from the contentCatalog based on the provided setParams. * setParams should contain: * - component (string, required): Component name to search * - filter (string, optional): A substring to match in the URL or other criteria * - env_type (string, optional): Deployment type to match ('Docker', 'Kubernetes', 'Linux', 'Redpanda Cloud') */ function gatherItems(contentCatalog, setParams, logger) { const { component, filter, env_type } = setParams; // Find the component in the catalog const componentObj = contentCatalog.getComponents().find(comp => comp.name === component); if (!componentObj) { logger.warn(`Component "${component}" not found in the content catalog.`); return []; } const result = []; // Iterate through all versions of the component componentObj.versions.forEach(versionObj => { const versionPages = contentCatalog.findBy({ component, family: 'page', version: versionObj.version }); logger.debug(`Gathering pages for component "${component}" version "${versionObj.version}". Found ${versionPages.length} pages.`); // Filter pages based on criteria const matchedPages = versionPages.filter(page => { const deploymentType = getDeploymentType(page.asciidoc.attributes); const urlMatches = filter ? page.pub.url.includes(filter) : true; const envMatches = env_type ? (deploymentType === env_type) : true; return urlMatches && envMatches; }); // Map matched pages to the desired structure const pages = matchedPages.map(page => ({ title: page.asciidoc.doctitle, url: page.pub.url, description: page.asciidoc.attributes.description || '' })); // Add the component and version structure, even if pages is empty result.push({ component, version: versionObj.version, pages }); logger.debug(`Processed ${pages.length} pages for version "${versionObj.version}".`); }); logger.debug(`Completed gathering items for component "${component}".`); return result; } /** * Deduplicates items by their URL and returns them in alphabetical order by title. */ function deduplicateAndSortItems(items) { return items.map(item => { const seenUrls = new Set(); // Deduplicate pages by URL const deduplicatedPages = item.pages.filter(page => { if (seenUrls.has(page.url)) { return false; } seenUrls.add(page.url); return true; }); // Sort pages alphabetically by title const sortedPages = deduplicatedPages.sort((a, b) => { const titleA = a.title.toLowerCase(); const titleB = b.title.toLowerCase(); if (titleA < titleB) return -1; if (titleA > titleB) return 1; return 0; }); return { ...item, pages: sortedPages }; }); } /** * Add the gathered data as an attribute to all components. * * @param {Array} data - The deduplicated items to set as an attribute. * @param {Object} contentCatalog - The content catalog from Antora. * @param {string} attributeName - The name of the attribute to set. * @param {Object} logger - The Antora logger. */ async function addDataAttributeToComponents(data, contentCatalog, attributeName, logger) { try { const jsonData = JSON.stringify(data, null, 2); const components = await contentCatalog.getComponents(); components.forEach(component => { component.versions.forEach(({ name, asciidoc }) => { asciidoc.attributes[attributeName] = jsonData; logger.debug(`Set attribute "${attributeName}" on component "${name}".`); }); }); logger.info(`Added "${attributeName}" attribute to relevant component versions.`); } catch (error) { logger.error(`Failed to add data attribute "${attributeName}": ${error.message}`); } } /** * Create a JSON file in the output with the given data. */ function createIndexFile(data, siteCatalog, outputFile, logger) { if (!outputFile) { logger.info('No output_file specified, skipping JSON file creation.'); return; } const jsonData = JSON.stringify(data, null, 2); siteCatalog.addFile({ contents: Buffer.from(jsonData, 'utf8'), out: { path: outputFile }, }); logger.info(`Created "${outputFile}" with indexed data.`); } /** * Determine the deployment type from AsciiDoc attributes. */ function getDeploymentType(attributes) { return attributes['env-kubernetes'] ? 'Kubernetes' : attributes['env-linux'] ? 'Linux' : attributes['env-docker'] ? 'Docker' : attributes['env-cloud'] ? 'Redpanda Cloud' : attributes['page-cloud'] ? 'Redpanda Cloud' : ''; }