Skip to content

Conversation

@JohannesHoppe
Copy link
Member

Summary

Markdown-Dateien können jetzt portable relative Links verwenden. Der Build transformiert diese zu absoluten Pfaden für die Angular-Website.

Relative Links → Absolute Pfade

[Anderer Post](../other-post#section)

wird zu:

<a href="/blog/other-post#section">Anderer Post</a>

Automatische TOC-Generierung

[[toc]] im Markdown wird durch ein generiertes Inhaltsverzeichnis ersetzt. Inline-Formatierung bleibt erhalten:

## Using `npm install`

erscheint im TOC als: Using <code>npm install</code>

Link-Validierung beim Build

Nach dem Build werden alle Anchor-Links geprüft. Bei kaputten Links gibt es Warnungen mit Korrekturvorschlägen (Levenshtein-Distanz):

⚠️  /blog/my-post
    → /blog/other#intrduction
    ✗ Anchor "#intrduction" not found
    ? Did you mean: #introduction

Sonstiges

  • marked-gfm-heading-id durch eigenen Fork ersetzt (mehr Kontrolle über Heading-Daten)
  • Shared utilities extrahiert: html.utils.ts, string.utils.ts

Fix <base href="/"> issue where #anchor links navigate to /#anchor.
The build now transforms relative links to absolute paths:
- #section → /blog/slug#section
- ../other-slug → /blog/other-slug
- ../other-slug#section → /blog/other-slug#section

Uses path.posix.resolve() for cross-platform path resolution.
linkBasePath is derived from folder structure (folder = URL path).

Also fixes: mailto:, tel:, ftp:// links are now correctly preserved
(isAbsoluteUrl uses /^\w+:/ regex to match all protocols).

Includes comprehensive documentation of URL transformation system.
- Add TOC_MARKER constant for [[toc]] placeholder
- Add generateToc() using getHeadingList() from marked-gfm-heading-id
- Add decodeHtmlEntities() for proper heading text display
- Include h2 and h3 headings, skip headings before marker
- Generate nested markdown list that gets processed normally

Also includes:
- Tests for mailto:, tel:, ftp:// links (not transformed)
- Tests for TOC generation (6 new tests)
- Add HeadingData, getHeadingList, and resetHeadings exports to types.d.ts
- Add missing linkBasePath parameter to markdownToEntry test calls
- Document image URL transformation (placeholder system)
- Document link transformation (relative to absolute)
- Document TOC generation with [[toc]] marker
- Add YAML frontmatter reference
- Add architecture overview
- Add submodule warning
- Fork marked-gfm-heading-id to shared/gfm-heading-id/
- Convert to TypeScript with improved types
- Simplified API (removed unused globalSlugs option)
- Better entity handling (decode before slugging)
- Replace marked-gfm-heading-id dependency with github-slugger
- Remove now-empty types.d.ts
- Update README with github-slugger reference

The fork is only 100 lines and gives us full control over heading ID
generation. All 131 tests pass.
- Remove decodeHtmlEntities() from JekyllMarkdownParser (now in fork)
- Use h.raw directly instead of decoding h.text
- Update comment to reference our gfm-heading-id fork

The fork provides heading.raw which is already decoded and stripped
of HTML tags, eliminating duplicate code.
Code Quality Improvements:
- Extract shared utilities to html.utils.ts (stripHtmlTags, decodeHtmlEntities, escapeHtml)
- Precompile regex patterns for better performance
- Simplify parse() return type (remove unused yaml/markdown fields)
- Update documentation to reference our gfm-heading-id fork

TOC Generation Simplification:
- Replace complex position-tracking algorithm with simple split approach
- Split markdown at [[toc]] marker, parse only content after it
- Reduces generateToc from 60+ lines to 30 lines
- More robust: no regex matching of heading text needed

Net result: -40 lines of code, cleaner architecture, same functionality.
All 131 tests pass.
- TOC links now preserve inline formatting (<code>, <strong>, <em>)
- Add warning for duplicate headings (known limitation)
- Move gfm-heading-id.ts out of subdirectory
- Add html.utils.spec.ts (45 tests)
- Add gfm-heading-id.spec.ts (24 tests)
- Add 5 TOC formatting tests
- Document HeadingData text vs raw separation
- Clarify slug terminology in base.types.ts

Total: 205 tests passing
Build-time validation for broken anchor links:
- Collects all heading IDs during parsing
- Extracts all anchor links from HTML
- Validates links point to existing anchors
- Suggests similar anchors using Levenshtein distance (typo detection)
- Warnings only, does not fail the build

New files:
- string.utils.ts: Levenshtein distance + findSimilar (27 tests)
- link-validator.ts: Anchor validation logic (14 tests)

Changes:
- jekyll-markdown-parser.ts: Now returns headingIds
- base.utils.ts: Registers anchors and links during parsing
- build.ts: Runs validation at end of build

Total: 246 tests passing
Copy link
Member

@fmalcher fmalcher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! Levenshtein-Distanz ist ein BISSCHEN über's Ziel hinausgeschossen, aber ist geil 😂

- link-validator: use matchAll() instead of exec() loop with global regex
- html.utils: add single quote escaping for complete HTML safety
- base.utils: replace string concat sort key with proper multi-field comparison
@JohannesHoppe
Copy link
Member Author

Levenshtein

Wenn's das Feature for free kommt! 🤷

@JohannesHoppe JohannesHoppe merged commit fc95bf5 into main Feb 10, 2026
1 check passed
@JohannesHoppe JohannesHoppe deleted the feature/relative-link-transformation branch February 10, 2026 07:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants