Build system for blog and material articles. Transforms Markdown to JSON for Angular websites.
Used as a Git submodule in:
npm install
npm run build # Single build
npm run watch # Watch mode for development
npm test # Run tests
npm run typecheck # TypeScript checkwebsite-articles-build/
├── build.ts # Main build script
├── blog/
│ ├── blog.types.ts # Blog-specific types
│ └── blog.utils.ts # Blog-specific utilities
├── material/
│ └── material.types.ts # Material-specific types
└── shared/
├── jekyll-markdown-parser.ts # Markdown parser
├── base.utils.ts # Shared utilities
└── list.utils.ts # List utilities
The build generates for each article:
| Output | Description |
|---|---|
dist/blog/{slug}/entry.json |
Full article with HTML |
dist/blog/list.json |
List of all articles (light version) |
dist/material/{slug}/entry.json |
Full material entry |
dist/material/list.json |
List of all material entries |
Relative image paths are automatically transformed:

Build output:
<img src="%%MARKDOWN_BASE_URL%%/blog/my-article/screenshot.png">The placeholder %%MARKDOWN_BASE_URL%% is replaced at runtime by the Angular app (CDN on prod, proxy in dev).
Not transformed:
- Absolute URLs:
https://example.com/image.png - Protocol-relative URLs:
//cdn.example.com/image.png - Asset paths:
assets/img/icon.svg - Absolute paths:
/images/logo.png - Data URIs:
data:image/png;base64,...
Relative links are transformed to absolute paths. This is necessary because our Angular website uses <base href="/">.
[Introduction](#introduction)Build output:
<a href="/blog/my-article#introduction">Introduction</a>[Other Article](../other-article)
[Other Article with Anchor](../other-article#setup)Build output:
<a href="/blog/other-article">Other Article</a>
<a href="/blog/other-article#setup">Other Article with Anchor</a>Not transformed:
- Absolute URLs:
https://angular.io/docs - Already absolute paths:
/blog/other-article - mailto:
mailto:team@example.com - tel:
tel:+49123456 - ftp:
ftp://files.example.com/file.zip
Place [[toc]] in your Markdown to generate an automatic table of contents.
---
title: My Article
published: 2024-01-15
---
## Contents
[[toc]]
## Introduction
Lorem ipsum...
### Subchapter
More text...
## Conclusion
End.<h2 id="contents">Contents</h2>
<ul>
<li><a href="/blog/my-article#introduction">Introduction</a></li>
<li>
<ul>
<li><a href="/blog/my-article#subchapter">Subchapter</a></li>
</ul>
</li>
<li><a href="/blog/my-article#conclusion">Conclusion</a></li>
</ul>| Rule | Description |
|---|---|
| Only h2 and h3 | h1 and h4+ are ignored |
| After the marker | Headings before [[toc]] are skipped |
| Automatic IDs | Heading IDs follow GitHub's algorithm |
| Special characters | Umlauts preserved (Über uns → #über-uns), & removed |
Code blocks are automatically formatted with highlight.js:
```typescript
const greeting = 'Hello World';
console.log(greeting);
```HTML in Markdown is passed through unchanged:
<div class="custom-box">
<p>Custom styled content</p>
</div>
<iframe src="https://stackblitz.com/edit/angular" width="100%"></iframe>Security note: This is intentional. We trust our own repository. There is no user-generated content.
Emoji shortcodes are converted to Unicode:
Hello :smile: World :rocket:Output: Hello 😄 World 🚀
Every article requires YAML frontmatter:
---
title: "Article Title"
author: John Doe
mail: john@example.com
published: 2024-01-15
language: en
header: header.jpg
keywords:
- Angular
- TypeScript
# Optional:
lastModified: 2024-02-01
hidden: false # Don't show article in list
sticky: false # Pin article to top
darkenHeader: false
author2: Co-Author
mail2: co@example.com
bio: Short author bio
---Both formats are supported:
published: 2024-01-15 # Converted to ISO string
published: "2024-01-15T10:00:00Z" # Stays as stringnpm test # Single run
npm run test:watch # Watch mode131 tests cover:
- Markdown parsing and HTML generation
- Image and link transformation
- TOC generation
- Edge cases (mailto, tel, CRLF, etc.)
npm run typecheck # Type checkMarkdown (README.md)
↓
JekyllMarkdownParser
├── YAML Frontmatter → parsedYaml
├── Markdown → marked → HTML
├── Image URLs → transformed with placeholder
├── Links → transformed to absolute paths
└── TOC → generated from headings
↓
entry.json
This repository is included as a Git submodule in website-articles.
Always make changes here, not in the build/ folder of the parent repo!
# CORRECT: Work here
cd website-articles-build
git checkout -b feature/xyz
# WRONG: Don't work in the submodule
cd website-articles/build # ❌