Skip to content

Instantly share code, notes, and snippets.

@moreal
Last active November 25, 2025 02:57
Show Gist options
  • Select an option

  • Save moreal/117e595fe153802eef7772bd5e6634df to your computer and use it in GitHub Desktop.

Select an option

Save moreal/117e595fe153802eef7772bd5e6634df to your computer and use it in GitHub Desktop.
CLAUDE CODE Plan

Link Rebase Transformer Implementation Plan

Overview

Add a new transformer to the document processing pipeline that rebases all relative markdown links to the /pages/{path}/ format. This ensures consistent internal linking across the wiki.

Requirements Summary

  • Transform ALL relative links (not absolute paths starting with /, not external links)
  • Convert to /pages/{link}/ format
  • No URL encoding - keep links as-is
  • Handle edge cases: anchors, query params, relative path markers

Implementation Approach

1. Modify Wikilink Transformer (Remove Encoding)

File: src/document-pipeline/transformers/wikilink.ts

Current behavior:

return `[${displayText}](${encodeURIComponent(link)})`;

New behavior:

return `[${displayText}](${link})`;

Rationale: Remove encodeURIComponent() so all links remain unencoded throughout the pipeline, consistent with the requirement for no URL encoding.

2. Create Rebase Links Transformer

File: src/document-pipeline/transformers/rebase-links.ts

Function signature:

export function rebaseLinks<
  TMetadata,
  TContent extends { markdown: string },
>(
  doc: Document<TMetadata, TContent>,
): Document<TMetadata, TContent>

Core logic:

  1. Match all markdown links: /\[([^\]]*)\]\(([^)]+)\)/g
  2. For each link, check if it's a relative link (not absolute, not external)
  3. If relative, transform to /pages/{path}/ format while preserving anchors and query params
  4. Replace in markdown content

Link detection (relative links):

  • NOT external: http://, https://, mailto:, tel:, ftp://
  • NOT absolute: starting with /
  • IS relative: everything else

Transformation algorithm:

function transformLink(href: string): string {
  // Skip external links
  if (/^(https?|mailto|tel|ftp):/.test(href)) return href;

  // Skip absolute paths
  if (href.startsWith('/')) return href;

  // Parse href components
  const [pathPart, ...rest] = href.split(/([?#])/);
  let path = pathPart;

  // Normalize relative paths
  path = path.replace(/^\.\//, '').replace(/^\.\.\//, '');

  // Construct new path
  const newPath = `/pages/${path}/`;

  // Reattach query params and anchors
  return newPath + rest.join('');
}

Examples:

  • [Text](PageName)[Text](/pages/PageName/)
  • [Text](Some Page)[Text](/pages/Some Page/) (no encoding!)
  • [Text](page?q=test)[Text](/pages/page/?q=test)
  • [Text](page#section)[Text](/pages/page/#section)
  • [Text](./relative)[Text](/pages/relative/)
  • [Text](../parent)[Text](/pages/parent/)
  • [Text](/absolute/path) → unchanged
  • [Text](https://example.com) → unchanged

3. Pipeline Integration

File: src/document.ts

Current pipeline (lines 132-144):

const doc = await pipe(
  parseMarkdown(file),
  extractFrontmatter,
  sanitizeFrontmatter(validateFrontmatter),
  removeFrontmatter,
  extractAliases,
  extractHeadingTitle,
  extractTitle,
  removeFirstH1,
  transformWikilink,
  extractLinks,
  render(options),
);

New pipeline:

const doc = await pipe(
  parseMarkdown(file),
  extractFrontmatter,
  sanitizeFrontmatter(validateFrontmatter),
  removeFrontmatter,
  extractAliases,
  extractHeadingTitle,
  extractTitle,
  removeFirstH1,
  transformWikilink,
  rebaseLinks,        // NEW: Add here
  extractLinks,
  render(options),
);

Rationale:

  • After transformWikilink: Wikilinks are converted to markdown links first
  • Before extractLinks: Links are rebased before extraction, so metadata.links contains rebased hrefs
  • Before render(): Rebased links are used in HTML rendering

4. Export Configuration

File: src/document-pipeline/index.ts

Add export:

export { rebaseLinks } from "./transformers/rebase-links.ts";

File: src/document.ts

Add to imports (line 9-25):

import {
  // ... existing imports
  rebaseLinks,  // NEW
  // ... rest of imports
} from "./document-pipeline/index.ts";

Add to exports (line 161-179):

export {
  // ... existing exports
  rebaseLinks,  // NEW
  // ... rest of exports
} from "./document-pipeline/index.ts";

Files to Modify/Create

New Files

  1. src/document-pipeline/transformers/rebase-links.ts - Main transformer implementation

Modified Files

  1. src/document-pipeline/transformers/wikilink.ts - Remove encodeURIComponent()
  2. src/document-pipeline/index.ts - Export rebaseLinks
  3. src/document.ts - Import, add to pipeline, re-export
  4. src/transformers/wikilink.ts - Remove encodeURIComponent() (utility version)

Implementation Steps

  1. Modify wikilink transformer (both versions)

    • Remove encodeURIComponent() from line 22 in src/document-pipeline/transformers/wikilink.ts
    • Remove encodeURIComponent() from line 22 in src/transformers/wikilink.ts
  2. Create rebaseLinks transformer

    • Implement in src/document-pipeline/transformers/rebase-links.ts
    • Follow the structure of transformWikilink as a pattern
    • Use the transformation algorithm specified above
  3. Update exports

    • Add export in src/document-pipeline/index.ts
  4. Integrate into pipeline

    • Import in src/document.ts
    • Add to pipe after transformWikilink
    • Re-export from src/document.ts
  5. Test thoroughly

    • Manual testing with various link formats
    • Verify wikilinks are converted and rebased correctly
    • Verify external/absolute links remain unchanged

Edge Cases to Handle

  1. Anchors: page#section/pages/page/#section
  2. Query params: page?key=val/pages/page/?key=val
  3. Combined: page?key=val#section/pages/page/?key=val#section
  4. Relative markers: ./page, ../page/pages/page/
  5. Spaces: Some Page/pages/Some Page/ (no encoding) ✓
  6. Already absolute: /pages/Home/ → unchanged ✓
  7. External: https://example.com → unchanged ✓
  8. Empty href: [text]() → unchanged (edge case, should be rare)

Testing Strategy

Manual Testing

  1. Create test document with various link types
  2. Run through pipeline
  3. Verify content.markdown has rebased links
  4. Verify content.html renders correctly
  5. Verify metadata.links contains rebased hrefs

Test Cases

# Test Document

- [[Wikilink]] - should become /pages/Wikilink/
- [[Wikilink|Alias]] - should become /pages/Wikilink/
- [Internal](PageName) - should become /pages/PageName/
- [With Space](Page Name) - should become /pages/Page Name/
- [Relative](./page) - should become /pages/page/
- [Parent](../page) - should become /pages/page/
- [Anchor](page#section) - should become /pages/page/#section
- [Query](page?q=test) - should become /pages/page/?q=test
- [Absolute](/absolute/path) - should stay /absolute/path
- [External](https://example.com) - should stay https://example.com

Summary

This plan implements link rebasing through:

  1. Removing encoding from wikilink transformer (consistency)
  2. Adding rebaseLinks transformer (core functionality)
  3. Integrating after wikilink conversion, before link extraction
  4. Preserving anchors, query params, and special link types
  5. No URL encoding throughout the pipeline

The approach is minimal, focused, and follows existing patterns in the codebase.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment