@centrolabs/ngx-blogdown 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -0
- package/fesm2022/centrolabs-ngx-blogdown.mjs +104 -0
- package/fesm2022/centrolabs-ngx-blogdown.mjs.map +1 -0
- package/index.d.ts +104 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# @centrolabs/ngx-blogdown
|
|
2
|
+
|
|
3
|
+
A lightweight Angular library for building markdown-powered blogs. You handle the layout, we handle the pipeline.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @centrolabs/ngx-blogdown marked
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
### 1. Provide the library
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { provideHttpClient } from '@angular/common/http';
|
|
17
|
+
import { provideNgBlogdown } from '@centrolabs/ngx-blogdown';
|
|
18
|
+
|
|
19
|
+
bootstrapApplication(AppComponent, {
|
|
20
|
+
providers: [
|
|
21
|
+
provideHttpClient(),
|
|
22
|
+
provideNgBlogdown({
|
|
23
|
+
indexPath: '/blog/index.json',
|
|
24
|
+
postsDir: '/blog/posts',
|
|
25
|
+
}),
|
|
26
|
+
],
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 2. Write your posts
|
|
31
|
+
|
|
32
|
+
Create markdown files with YAML frontmatter in your posts directory:
|
|
33
|
+
|
|
34
|
+
```markdown
|
|
35
|
+
title: My First Post
|
|
36
|
+
date: 2026-01-15
|
|
37
|
+
cover: /images/first-post.png
|
|
38
|
+
tagline: A short description of this post
|
|
39
|
+
author: Jane Doe
|
|
40
|
+
---
|
|
41
|
+
# My First Post
|
|
42
|
+
|
|
43
|
+
Write your content here using **markdown**.
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 3. Generate the index
|
|
47
|
+
|
|
48
|
+
Use the bundled CLI to generate a JSON index from your markdown files:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx ngx-blogdown-index --postsDir src/content/posts --out src/content/index.json
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This scans all `.md` files, extracts frontmatter, and outputs a sorted JSON index.
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
Inject `BlogService` anywhere you need blog data:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { BlogService, BlogPostMeta } from '@centrolabs/ngx-blogdown';
|
|
62
|
+
|
|
63
|
+
@Component({
|
|
64
|
+
template: `
|
|
65
|
+
@for (post of posts; track post.slug) {
|
|
66
|
+
<article>
|
|
67
|
+
<h2>{{ post.title }}</h2>
|
|
68
|
+
<p>{{ post.tagline }}</p>
|
|
69
|
+
</article>
|
|
70
|
+
}
|
|
71
|
+
`,
|
|
72
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
73
|
+
})
|
|
74
|
+
export class BlogListComponent {
|
|
75
|
+
private blogService = inject(BlogService);
|
|
76
|
+
posts: BlogPostMeta[] = [];
|
|
77
|
+
|
|
78
|
+
async ngOnInit() {
|
|
79
|
+
this.posts = await this.blogService.getPosts();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Rendering a single post
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const post = await this.blogService.getPost('my-first-post');
|
|
88
|
+
if (post) {
|
|
89
|
+
// post.htmlContent contains the rendered HTML
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### SEO tags
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
const meta = posts.find(p => p.slug === 'my-first-post')!;
|
|
97
|
+
const seo = this.blogService.getSeoTags(meta);
|
|
98
|
+
// { title, description, image, date, author }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## API
|
|
102
|
+
|
|
103
|
+
### `provideNgBlogdown(config)`
|
|
104
|
+
|
|
105
|
+
Registers the library providers. Call this in your application bootstrap.
|
|
106
|
+
|
|
107
|
+
| Parameter | Type | Description |
|
|
108
|
+
| ------------------ | -------- | ---------------------------------------- |
|
|
109
|
+
| `config.indexPath` | `string` | Path to the JSON index file |
|
|
110
|
+
| `config.postsDir` | `string` | Directory where markdown files are served |
|
|
111
|
+
|
|
112
|
+
### `BlogService`
|
|
113
|
+
|
|
114
|
+
| Method | Returns | Description |
|
|
115
|
+
| ------------------------ | -------------------------- | ------------------------------------------------ |
|
|
116
|
+
| `getPosts()` | `Promise<BlogPostMeta[]>` | Fetches all post metadata. Cached after first call. |
|
|
117
|
+
| `getPost(slug)` | `Promise<BlogPost \| null>` | Fetches and renders a single post by slug. |
|
|
118
|
+
| `getSeoTags(postMeta)` | `SeoTags` | Derives SEO meta tags from post metadata. |
|
|
119
|
+
|
|
120
|
+
### CLI: `ngx-blogdown-index`
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
ngx-blogdown-index --postsDir <path> --out <file>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
| Flag | Description |
|
|
127
|
+
| ------------ | ------------------------------------------ |
|
|
128
|
+
| `--postsDir` | Directory containing `.md` files |
|
|
129
|
+
| `--out` | Output path for the generated JSON index |
|
|
130
|
+
|
|
131
|
+
The generated index is sorted by date (newest first). Slugs are derived from filenames (lowercased, spaces replaced with hyphens).
|
|
132
|
+
|
|
133
|
+
## Peer Dependencies
|
|
134
|
+
|
|
135
|
+
| Package | Version |
|
|
136
|
+
| ---------------- | --------- |
|
|
137
|
+
| `@angular/core` | `^20.3.0` |
|
|
138
|
+
| `@angular/common`| `^20.3.0` |
|
|
139
|
+
| `marked` | `^17.0.0` |
|
|
140
|
+
|
|
141
|
+
## License
|
|
142
|
+
|
|
143
|
+
MIT
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, signal, Injectable, InjectionToken } from '@angular/core';
|
|
3
|
+
import { HttpClient } from '@angular/common/http';
|
|
4
|
+
import { firstValueFrom } from 'rxjs';
|
|
5
|
+
import { marked } from 'marked';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Core service for fetching and rendering markdown blog posts.
|
|
9
|
+
*
|
|
10
|
+
* Provided via {@link provideNgBlogdown}. Requires `HttpClient` to be available.
|
|
11
|
+
*/
|
|
12
|
+
class BlogService {
|
|
13
|
+
http = inject(HttpClient);
|
|
14
|
+
config = inject(NG_BLOG_CONFIG);
|
|
15
|
+
indexCache = signal(null, ...(ngDevMode ? [{ debugName: "indexCache" }] : []));
|
|
16
|
+
/**
|
|
17
|
+
* Fetches the blog post index. Results are cached in memory after the first call.
|
|
18
|
+
*
|
|
19
|
+
* @returns All blog post metadata from the configured index path.
|
|
20
|
+
*/
|
|
21
|
+
async getPosts() {
|
|
22
|
+
if (this.indexCache())
|
|
23
|
+
return this.indexCache();
|
|
24
|
+
const posts = await firstValueFrom(this.http.get(this.config.indexPath));
|
|
25
|
+
this.indexCache.set(posts);
|
|
26
|
+
return posts;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Fetches a single blog post by its slug, parses its markdown body into HTML,
|
|
30
|
+
* and strips the YAML frontmatter.
|
|
31
|
+
*
|
|
32
|
+
* @param slug - The URL-friendly post identifier to look up.
|
|
33
|
+
* @returns The full blog post with rendered HTML, or `null` if not found.
|
|
34
|
+
*/
|
|
35
|
+
async getPost(slug) {
|
|
36
|
+
const posts = await this.getPosts();
|
|
37
|
+
const meta = posts.find((p) => p.slug === slug);
|
|
38
|
+
if (!meta)
|
|
39
|
+
return null;
|
|
40
|
+
const raw = await firstValueFrom(this.http.get(`${this.config.postsDir}/${encodeURIComponent(meta.filename)}`, {
|
|
41
|
+
responseType: 'text',
|
|
42
|
+
}));
|
|
43
|
+
const separatorMatch = raw.match(/^-{3,}$/m);
|
|
44
|
+
const headerEnd = separatorMatch ? separatorMatch.index : -1;
|
|
45
|
+
const body = headerEnd !== -1 ? raw.slice(headerEnd + separatorMatch[0].length).trim() : raw;
|
|
46
|
+
const htmlContent = await marked(body);
|
|
47
|
+
return { ...meta, htmlContent };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Derives SEO meta tags from a post's metadata.
|
|
51
|
+
*
|
|
52
|
+
* @param postMeta - The post metadata to extract tags from.
|
|
53
|
+
* @returns SEO-friendly tag values for use in `<meta>` elements.
|
|
54
|
+
*/
|
|
55
|
+
getSeoTags(postMeta) {
|
|
56
|
+
return {
|
|
57
|
+
title: postMeta.title,
|
|
58
|
+
description: postMeta.tagline,
|
|
59
|
+
image: postMeta.cover,
|
|
60
|
+
date: postMeta.date,
|
|
61
|
+
author: postMeta.author ?? null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: BlogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
65
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: BlogService });
|
|
66
|
+
}
|
|
67
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: BlogService, decorators: [{
|
|
68
|
+
type: Injectable
|
|
69
|
+
}] });
|
|
70
|
+
|
|
71
|
+
/** Injection token for supplying {@link NgBlogConfig} to the library. */
|
|
72
|
+
const NG_BLOG_CONFIG = new InjectionToken('NG_BLOG_CONFIG');
|
|
73
|
+
/**
|
|
74
|
+
* Provides the ngx-blogdown library with the given configuration.
|
|
75
|
+
*
|
|
76
|
+
* Register the returned providers in your application's bootstrap or route config.
|
|
77
|
+
*
|
|
78
|
+
* @param config - Paths to the blog index file and posts directory.
|
|
79
|
+
* @returns An array of providers including {@link BlogService} and the config token.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* bootstrapApplication(AppComponent, {
|
|
84
|
+
* providers: [
|
|
85
|
+
* provideHttpClient(),
|
|
86
|
+
* provideNgBlogdown({ indexPath: '/blog/index.json', postsDir: '/blog/posts' }),
|
|
87
|
+
* ],
|
|
88
|
+
* });
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
function provideNgBlogdown(config) {
|
|
92
|
+
return [{ provide: NG_BLOG_CONFIG, useValue: config }, BlogService];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/*
|
|
96
|
+
* Public API Surface of ng-blog
|
|
97
|
+
*/
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Generated bundle index. Do not edit.
|
|
101
|
+
*/
|
|
102
|
+
|
|
103
|
+
export { BlogService, NG_BLOG_CONFIG, provideNgBlogdown };
|
|
104
|
+
//# sourceMappingURL=centrolabs-ngx-blogdown.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"centrolabs-ngx-blogdown.mjs","sources":["../../../projects/ngx-blogdown/src/lib/blog.service.ts","../../../projects/ngx-blogdown/src/lib/blog.config.ts","../../../projects/ngx-blogdown/src/public-api.ts","../../../projects/ngx-blogdown/src/centrolabs-ngx-blogdown.ts"],"sourcesContent":["import { inject, Injectable, signal } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { firstValueFrom } from 'rxjs';\nimport { marked } from 'marked';\nimport { BlogPost, BlogPostMeta, SeoTags } from './blog.models';\nimport { NG_BLOG_CONFIG } from './blog.config';\n\n/**\n * Core service for fetching and rendering markdown blog posts.\n *\n * Provided via {@link provideNgBlogdown}. Requires `HttpClient` to be available.\n */\n@Injectable()\nexport class BlogService {\n private http = inject(HttpClient);\n private config = inject(NG_BLOG_CONFIG);\n\n private indexCache = signal<BlogPostMeta[] | null>(null);\n\n /**\n * Fetches the blog post index. Results are cached in memory after the first call.\n *\n * @returns All blog post metadata from the configured index path.\n */\n async getPosts(): Promise<BlogPostMeta[]> {\n if (this.indexCache()) return this.indexCache()!;\n\n const posts = await firstValueFrom(this.http.get<BlogPostMeta[]>(this.config.indexPath));\n this.indexCache.set(posts);\n return posts;\n }\n\n /**\n * Fetches a single blog post by its slug, parses its markdown body into HTML,\n * and strips the YAML frontmatter.\n *\n * @param slug - The URL-friendly post identifier to look up.\n * @returns The full blog post with rendered HTML, or `null` if not found.\n */\n async getPost(slug: string): Promise<BlogPost | null> {\n const posts = await this.getPosts();\n const meta = posts.find((p) => p.slug === slug);\n if (!meta) return null;\n\n const raw = await firstValueFrom(\n this.http.get(`${this.config.postsDir}/${encodeURIComponent(meta.filename)}`, {\n responseType: 'text',\n }),\n );\n\n const separatorMatch = raw.match(/^-{3,}$/m);\n const headerEnd = separatorMatch ? separatorMatch.index! : -1;\n const body = headerEnd !== -1 ? raw.slice(headerEnd + separatorMatch![0].length).trim() : raw;\n\n const htmlContent = await marked(body);\n\n return { ...meta, htmlContent };\n }\n\n /**\n * Derives SEO meta tags from a post's metadata.\n *\n * @param postMeta - The post metadata to extract tags from.\n * @returns SEO-friendly tag values for use in `<meta>` elements.\n */\n getSeoTags(postMeta: BlogPostMeta): SeoTags {\n return {\n title: postMeta.title,\n description: postMeta.tagline,\n image: postMeta.cover,\n date: postMeta.date,\n author: postMeta.author ?? null,\n };\n }\n}\n","import { InjectionToken, Provider } from '@angular/core';\nimport { NgBlogConfig } from './blog.models';\nimport { BlogService } from './blog.service';\n\n/** Injection token for supplying {@link NgBlogConfig} to the library. */\nexport const NG_BLOG_CONFIG = new InjectionToken<NgBlogConfig>('NG_BLOG_CONFIG');\n\n/**\n * Provides the ngx-blogdown library with the given configuration.\n *\n * Register the returned providers in your application's bootstrap or route config.\n *\n * @param config - Paths to the blog index file and posts directory.\n * @returns An array of providers including {@link BlogService} and the config token.\n *\n * @example\n * ```ts\n * bootstrapApplication(AppComponent, {\n * providers: [\n * provideHttpClient(),\n * provideNgBlogdown({ indexPath: '/blog/index.json', postsDir: '/blog/posts' }),\n * ],\n * });\n * ```\n */\nexport function provideNgBlogdown(config: NgBlogConfig): Provider[] {\n return [{ provide: NG_BLOG_CONFIG, useValue: config }, BlogService];\n}\n","/*\n * Public API Surface of ng-blog\n */\n\nexport * from './lib/blog.models';\nexport * from './lib/blog.config';\nexport * from './lib/blog.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;AAOA;;;;AAIG;MAEU,WAAW,CAAA;AACd,IAAA,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC;AACzB,IAAA,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC;AAE/B,IAAA,UAAU,GAAG,MAAM,CAAwB,IAAI,sDAAC;AAExD;;;;AAIG;AACH,IAAA,MAAM,QAAQ,GAAA;QACZ,IAAI,IAAI,CAAC,UAAU,EAAE;AAAE,YAAA,OAAO,IAAI,CAAC,UAAU,EAAG;AAEhD,QAAA,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAiB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AACxF,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;AAC1B,QAAA,OAAO,KAAK;IACd;AAEA;;;;;;AAMG;IACH,MAAM,OAAO,CAAC,IAAY,EAAA;AACxB,QAAA,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE;AACnC,QAAA,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC;AAC/C,QAAA,IAAI,CAAC,IAAI;AAAE,YAAA,OAAO,IAAI;QAEtB,MAAM,GAAG,GAAG,MAAM,cAAc,CAC9B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA,EAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAA,CAAA,EAAI,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA,CAAE,EAAE;AAC5E,YAAA,YAAY,EAAE,MAAM;AACrB,SAAA,CAAC,CACH;QAED,MAAM,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC;AAC5C,QAAA,MAAM,SAAS,GAAG,cAAc,GAAG,cAAc,CAAC,KAAM,GAAG,CAAC,CAAC;AAC7D,QAAA,MAAM,IAAI,GAAG,SAAS,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,GAAG,cAAe,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,GAAG,GAAG;AAE7F,QAAA,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC;AAEtC,QAAA,OAAO,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE;IACjC;AAEA;;;;;AAKG;AACH,IAAA,UAAU,CAAC,QAAsB,EAAA;QAC/B,OAAO;YACL,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,WAAW,EAAE,QAAQ,CAAC,OAAO;YAC7B,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,IAAI,EAAE,QAAQ,CAAC,IAAI;AACnB,YAAA,MAAM,EAAE,QAAQ,CAAC,MAAM,IAAI,IAAI;SAChC;IACH;wGA5DW,WAAW,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;4GAAX,WAAW,EAAA,CAAA;;4FAAX,WAAW,EAAA,UAAA,EAAA,CAAA;kBADvB;;;ACRD;MACa,cAAc,GAAG,IAAI,cAAc,CAAe,gBAAgB;AAE/E;;;;;;;;;;;;;;;;;AAiBG;AACG,SAAU,iBAAiB,CAAC,MAAoB,EAAA;AACpD,IAAA,OAAO,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,WAAW,CAAC;AACrE;;AC3BA;;AAEG;;ACFH;;AAEG;;;;"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, Provider } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
/** Metadata for a blog post, as stored in the index JSON file. */
|
|
5
|
+
interface BlogPostMeta {
|
|
6
|
+
/** URL-friendly identifier derived from the filename. */
|
|
7
|
+
slug: string;
|
|
8
|
+
/** Original markdown filename including extension. */
|
|
9
|
+
filename: string;
|
|
10
|
+
/** Display title of the post. */
|
|
11
|
+
title: string;
|
|
12
|
+
/** Publication date as an ISO date string (e.g. `"2026-01-15"`). */
|
|
13
|
+
date: string;
|
|
14
|
+
/** URL or path to the post's cover image. */
|
|
15
|
+
cover: string;
|
|
16
|
+
/** Short summary or subtitle for the post. */
|
|
17
|
+
tagline: string;
|
|
18
|
+
/** Author name, or `null` if not specified. */
|
|
19
|
+
author: string | null;
|
|
20
|
+
}
|
|
21
|
+
/** A full blog post including its rendered HTML content. */
|
|
22
|
+
interface BlogPost extends BlogPostMeta {
|
|
23
|
+
/** The post body rendered from markdown to HTML. */
|
|
24
|
+
htmlContent: string;
|
|
25
|
+
}
|
|
26
|
+
/** Configuration for the ngx-blogdown library. */
|
|
27
|
+
interface NgBlogConfig {
|
|
28
|
+
/** Path to the JSON index file containing all post metadata. */
|
|
29
|
+
indexPath: string;
|
|
30
|
+
/** Directory path where markdown post files are served from. */
|
|
31
|
+
postsDir: string;
|
|
32
|
+
}
|
|
33
|
+
/** SEO meta tag values extracted from a blog post. */
|
|
34
|
+
interface SeoTags {
|
|
35
|
+
/** Page title. */
|
|
36
|
+
title: string | null;
|
|
37
|
+
/** Meta description, derived from the post's tagline. */
|
|
38
|
+
description: string | null;
|
|
39
|
+
/** Open Graph image URL, derived from the post's cover. */
|
|
40
|
+
image: string | null;
|
|
41
|
+
/** Publication date. */
|
|
42
|
+
date: string | null;
|
|
43
|
+
/** Author name. */
|
|
44
|
+
author: string | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Injection token for supplying {@link NgBlogConfig} to the library. */
|
|
48
|
+
declare const NG_BLOG_CONFIG: InjectionToken<NgBlogConfig>;
|
|
49
|
+
/**
|
|
50
|
+
* Provides the ngx-blogdown library with the given configuration.
|
|
51
|
+
*
|
|
52
|
+
* Register the returned providers in your application's bootstrap or route config.
|
|
53
|
+
*
|
|
54
|
+
* @param config - Paths to the blog index file and posts directory.
|
|
55
|
+
* @returns An array of providers including {@link BlogService} and the config token.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* bootstrapApplication(AppComponent, {
|
|
60
|
+
* providers: [
|
|
61
|
+
* provideHttpClient(),
|
|
62
|
+
* provideNgBlogdown({ indexPath: '/blog/index.json', postsDir: '/blog/posts' }),
|
|
63
|
+
* ],
|
|
64
|
+
* });
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
declare function provideNgBlogdown(config: NgBlogConfig): Provider[];
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Core service for fetching and rendering markdown blog posts.
|
|
71
|
+
*
|
|
72
|
+
* Provided via {@link provideNgBlogdown}. Requires `HttpClient` to be available.
|
|
73
|
+
*/
|
|
74
|
+
declare class BlogService {
|
|
75
|
+
private http;
|
|
76
|
+
private config;
|
|
77
|
+
private indexCache;
|
|
78
|
+
/**
|
|
79
|
+
* Fetches the blog post index. Results are cached in memory after the first call.
|
|
80
|
+
*
|
|
81
|
+
* @returns All blog post metadata from the configured index path.
|
|
82
|
+
*/
|
|
83
|
+
getPosts(): Promise<BlogPostMeta[]>;
|
|
84
|
+
/**
|
|
85
|
+
* Fetches a single blog post by its slug, parses its markdown body into HTML,
|
|
86
|
+
* and strips the YAML frontmatter.
|
|
87
|
+
*
|
|
88
|
+
* @param slug - The URL-friendly post identifier to look up.
|
|
89
|
+
* @returns The full blog post with rendered HTML, or `null` if not found.
|
|
90
|
+
*/
|
|
91
|
+
getPost(slug: string): Promise<BlogPost | null>;
|
|
92
|
+
/**
|
|
93
|
+
* Derives SEO meta tags from a post's metadata.
|
|
94
|
+
*
|
|
95
|
+
* @param postMeta - The post metadata to extract tags from.
|
|
96
|
+
* @returns SEO-friendly tag values for use in `<meta>` elements.
|
|
97
|
+
*/
|
|
98
|
+
getSeoTags(postMeta: BlogPostMeta): SeoTags;
|
|
99
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<BlogService, never>;
|
|
100
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<BlogService>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export { BlogService, NG_BLOG_CONFIG, provideNgBlogdown };
|
|
104
|
+
export type { BlogPost, BlogPostMeta, NgBlogConfig, SeoTags };
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@centrolabs/ngx-blogdown",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight Angular library for building markdown-powered blogs. You handle the layout, we handle the pipeline.",
|
|
5
|
+
"peerDependencies": {
|
|
6
|
+
"@angular/common": "^20.3.0",
|
|
7
|
+
"@angular/core": "^20.3.0",
|
|
8
|
+
"marked": "^17.0.0"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"tslib": "^2.3.0"
|
|
12
|
+
},
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"bin": {
|
|
15
|
+
"ngx-blogdown-index": "./bin/ngx-blogdown-index.mjs"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/centrolabs/ngx-blogdown"
|
|
20
|
+
},
|
|
21
|
+
"module": "fesm2022/centrolabs-ngx-blogdown.mjs",
|
|
22
|
+
"typings": "index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
"./package.json": {
|
|
25
|
+
"default": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./index.d.ts",
|
|
29
|
+
"default": "./fesm2022/centrolabs-ngx-blogdown.mjs"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|