Post-processing
Folder: src/parsers/postprocessors/
Postprocessors run after HTML rendering to enhance the output.
Role in the Pipeline
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ PREPROCESSORS │ │ RENDERERS │ │ POSTPROCESSORS │
│ │ ──▶ │ │ ──▶ │ ◀── YOU ARE HERE│
│ │ │ │ │ • heading-ids │
│ │ │ │ │ • external-links│
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
▼
Transformers
Postprocessor Files
| File | Purpose |
|---|---|
heading-ids.ts |
Adds IDs to headings for anchor links |
external-links.ts |
Adds security attributes to external links |
index.ts |
Module exports |
Available Postprocessors
Heading IDs
Automatically adds IDs to headings for anchor links:
<!-- Input -->
<h2>Getting Started</h2>
<!-- Output -->
<h2 id="getting-started">Getting Started</h2>
This enables:
- Direct linking to sections via
#getting-started - Table of contents generation
- In-page navigation
Usage:
import { headingIdsPostprocessor, createHeadingIdsPostprocessor } from '@parsers/postprocessors';
// Use default
pipeline.addPostprocessor(headingIdsPostprocessor);
// Or create with options
const customHeadingIds = createHeadingIdsPostprocessor({
prefix: 'section-', // Add prefix to all IDs
});
External Links
Adds security attributes to external links:
<!-- Input -->
<a href="https://example.com">Link</a>
<!-- Output -->
<a href="https://example.com" target="_blank" rel="noopener noreferrer">Link</a>
Security Attributes:
| Attribute | Purpose |
|---|---|
target="_blank" |
Opens in new tab |
rel="noopener" |
Prevents window.opener access |
rel="noreferrer" |
Prevents referrer header |
Usage:
import { externalLinksPostprocessor, createExternalLinksPostprocessor } from '@parsers/postprocessors';
// Use default
pipeline.addPostprocessor(externalLinksPostprocessor);
// Or create with options
const customExternalLinks = createExternalLinksPostprocessor({
excludeDomains: ['example.com'], // Don't mark as external
});
Custom Postprocessors
Create custom postprocessors by implementing the Processor interface:
import type { Processor, ProcessContext } from '@parsers/types';
const myPostprocessor: Processor = {
name: 'my-postprocessor',
async process(html: string, context: ProcessContext): Promise<string> {
// Transform HTML here
return transformedHtml;
}
};
// Add to pipeline
pipeline.addPostprocessor(myPostprocessor);
Example: Add Copy Buttons to Code Blocks
const copyButtonPostprocessor: Processor = {
name: 'copy-buttons',
async process(html) {
return html.replace(
/<pre><code/g,
'<pre class="has-copy-button"><button class="copy-btn">Copy</button><code'
);
}
};
Example: Lazy Load Images
const lazyImagePostprocessor: Processor = {
name: 'lazy-images',
async process(html) {
return html.replace(
/<img /g,
'<img loading="lazy" '
);
}
};
Processing Order
Postprocessors run in the order they are added:
pipeline
.addPostprocessor(headingIdsPostprocessor) // 1st
.addPostprocessor(externalLinksPostprocessor) // 2nd
.addPostprocessor(transformerProcessor); // 3rd - transformers
Order matters when processors depend on each other's output.
Error Handling
Missing Position Prefix
[DOCS ERROR] Files missing required XX_ position prefix:
- overview.md
- installation.md
Docs files must be named with a position prefix (01-99).
Examples:
01_getting-started.md
02_installation.md
File Not Found
[PARSER ERROR] Content file not found: /path/to/missing.md
Code: FILE_NOT_FOUND
Directory Not Found
[PARSER ERROR] Content directory not found: /path/to/missing/
Code: DIR_NOT_FOUND