{
  "type": "module",
  "source": "doc/api/contributing-comparators.md",
  "modules": [
    {
      "textRaw": "Creating Comparators",
      "name": "creating_comparators",
      "type": "module",
      "desc": "<p>This guide explains how to create build comparison scripts for <code>@doc-kit/core</code>. Comparators help identify differences between documentation builds, useful for CI/CD and regression testing.</p>",
      "modules": [
        {
          "textRaw": "Comparator Concepts",
          "name": "comparator_concepts",
          "type": "module",
          "desc": "<p>Comparators are scripts that:</p>\n<ol>\n<li><strong>Compare</strong> generated documentation between two builds (base vs. head)</li>\n<li><strong>Identify differences</strong> in content, structure, file size, or performance</li>\n<li><strong>Report results</strong> in a format suitable for CI/CD systems</li>\n<li><strong>Help catch regressions</strong> before merging changes</li>\n</ol>",
          "modules": [
            {
              "textRaw": "When to Use Comparators",
              "name": "when_to_use_comparators",
              "type": "module",
              "desc": "<ul>\n<li><strong>Verify backward compatibility</strong> - Ensure new code produces same output</li>\n<li><strong>Track file size changes</strong> - Monitor bundle size growth</li>\n<li><strong>Catch performance regressions</strong> - Compare elapsed time, CPU time, and peak memory</li>\n<li><strong>Validate transformations</strong> - Check that refactors don't alter output</li>\n<li><strong>Debug generation issues</strong> - Understand what changed between versions</li>\n</ul>",
              "displayName": "When to Use Comparators"
            }
          ],
          "displayName": "Comparator Concepts"
        },
        {
          "textRaw": "Comparator Structure",
          "name": "comparator_structure",
          "type": "module",
          "desc": "<p>Comparators are standalone ESM scripts located in <code>scripts/comparators/</code>,\nsharing the <code>BASE</code>, <code>HEAD</code>, and <code>TITLE</code> constants from <code>scripts/constants.mjs</code>:</p>\n<pre><code class=\"language-text\">scripts/\n├── constants.mjs            # Shared constants (BASE, HEAD, TITLE)\n└── comparators/\n    ├── file-size.mjs        # Compare file sizes and performance between builds\n    ├── files.mjs            # Shared output-file listing helpers\n    ├── object-assertion.mjs # Deep equality assertion for JSON objects\n    ├── performance.mjs      # Compare benchmark measurements\n    └── your-comparator.mjs  # Your new comparator\n</code></pre>",
          "modules": [
            {
              "textRaw": "Naming Convention",
              "name": "naming_convention",
              "type": "module",
              "desc": "<p>Comparators can be reused across multiple generators. You specify which comparator to use in the workflow file using the <code>compare</code> field. For example:</p>\n<ul>\n<li><code>file-size.mjs</code> can compare output from <code>html</code>, <code>legacy-html</code>, or any generator</li>\n<li><code>object-assertion.mjs</code> can compare JSON output from <code>legacy-json</code>, <code>json-simple</code>, etc.</li>\n<li><code>my-comparator.mjs</code> would be a custom comparator for specific needs</li>\n</ul>\n<p>The generation workflow also stores timing, CPU, and peak resident memory in\n<code>benchmark.json</code>. The built-in comparators include these measurements in their\nMarkdown report and exclude the metadata file from output comparisons. If a\nbase artifact predates performance measurement, the output comparison still\nruns and the performance section is omitted.</p>",
              "displayName": "Naming Convention"
            }
          ],
          "displayName": "Comparator Structure"
        },
        {
          "textRaw": "Creating a Comparator",
          "name": "creating_a_comparator",
          "type": "module",
          "modules": [
            {
              "textRaw": "Step 1: Create the Comparator File",
              "name": "step_1:_create_the_comparator_file",
              "type": "module",
              "desc": "<p>Create a new file in <code>scripts/comparators/</code> with the same name as your generator:</p>\n<pre><code class=\"language-mjs\">import { readdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { BASE, HEAD, TITLE } from '../constants.mjs';\n\n// Fetch files from both directories\nconst [baseFiles, headFiles] = await Promise.all(\n  [BASE, HEAD].map(dir => readdir(dir))\n);\n\n// Find all unique files across both builds\nconst allFiles = [...new Set([...baseFiles, ...headFiles])];\n\n/**\n * Compare a single file between base and head\n * @param {string} file - Filename to compare\n * @returns {Promise&#x3C;Object|null>} Difference object or null if identical\n */\nconst compareFile = async file => {\n  const basePath = join(BASE, file);\n  const headPath = join(HEAD, file);\n\n  try {\n    const baseContent = await readFile(basePath, 'utf-8');\n    const headContent = await readFile(headPath, 'utf-8');\n\n    if (baseContent !== headContent) {\n      return {\n        file,\n        type: 'modified',\n        baseSize: baseContent.length,\n        headSize: headContent.length,\n      };\n    }\n\n    return null;\n  } catch (error) {\n    // File missing in one of the builds\n    const exists = await Promise.all([\n      readFile(basePath, 'utf-8')\n        .then(() => true)\n        .catch(() => false),\n      readFile(headPath, 'utf-8')\n        .then(() => true)\n        .catch(() => false),\n    ]);\n\n    if (exists[0] &#x26;&#x26; !exists[1]) {\n      return { file, type: 'removed' };\n    }\n    if (!exists[0] &#x26;&#x26; exists[1]) {\n      return { file, type: 'added' };\n    }\n\n    return { file, type: 'error', error: error.message };\n  }\n};\n\n// Compare all files in parallel\nconst results = await Promise.all(allFiles.map(compareFile));\n\n// Filter out null results (identical files)\nconst differences = results.filter(Boolean);\n\n// Output markdown results\nif (differences.length > 0) {\n  console.log(TITLE);\n  console.log('');\n  console.log(`Found ${differences.length} difference(s):`);\n  console.log('');\n\n  // Group by type\n  const added = differences.filter(d => d.type === 'added');\n  const removed = differences.filter(d => d.type === 'removed');\n  const modified = differences.filter(d => d.type === 'modified');\n\n  if (added.length) {\n    console.log('### Added Files');\n    console.log('');\n    added.forEach(d => console.log(`- \\`${d.file}\\``));\n    console.log('');\n  }\n\n  if (removed.length) {\n    console.log('### Removed Files');\n    console.log('');\n    removed.forEach(d => console.log(`- \\`${d.file}\\``));\n    console.log('');\n  }\n\n  if (modified.length) {\n    console.log('### Modified Files');\n    console.log('');\n    console.log('| File | Base Size | Head Size | Diff |');\n    console.log('|-|-|-|-|');\n    modified.forEach(({ file, baseSize, headSize }) => {\n      const diff = headSize - baseSize;\n      const sign = diff > 0 ? '+' : '';\n      console.log(\n        `| \\`${file}\\` | ${baseSize} | ${headSize} | ${sign}${diff} |`\n      );\n    });\n    console.log('');\n  }\n}\n</code></pre>",
              "displayName": "Step 1: Create the Comparator File"
            },
            {
              "textRaw": "Step 2: Test Locally",
              "name": "step_2:_test_locally",
              "type": "module",
              "desc": "<p>Run your comparator locally to verify it works:</p>\n<pre><code class=\"language-bash\"># Set up BASE and HEAD directories\nexport BASE=path/to/base/output\nexport HEAD=path/to/head/output\n\n# Run the comparator\nnode scripts/comparators/my-format.mjs\n</code></pre>",
              "displayName": "Step 2: Test Locally"
            },
            {
              "textRaw": "Step 3: Integrate with CI/CD",
              "name": "step_3:_integrate_with_ci/cd",
              "type": "module",
              "desc": "<p>The comparator will automatically run in GitHub Actions when:</p>\n<ol>\n<li>Your generator is configured with <code>compare: &#x3C;my-comparator></code> in the workflow, which tells the system which comparator script to run</li>\n</ol>",
              "displayName": "Step 3: Integrate with CI/CD"
            }
          ],
          "displayName": "Creating a Comparator"
        }
      ],
      "displayName": "Creating Comparators"
    }
  ]
}