From 00a6831fb1419d4b256db6f450701da075c44688 Mon Sep 17 00:00:00 2001
From: Marc Stammerjohann <8985933+marcjulian@users.noreply.github.com>
Date: Fri, 17 Nov 2023 12:43:38 +0100
Subject: [PATCH] add provideMarkdocOptions
---
README.md | 53 ++++++++++++++++--
projects/ngx-markdoc/src/lib/config.ts | 8 ---
.../ngx-markdoc/src/lib/extensions/index.ts | 1 +
.../ngx-markdoc/src/lib/markdoc.component.ts | 25 +++++++--
.../src/lib/provide-markdoc-options.ts | 29 ++++++++++
projects/ngx-markdoc/src/public-api.ts | 2 +-
src/app/app.config.ts | 14 +++++
src/app/markdoc-example.ts | 13 +++++
src/app/pages/landing.component.ts | 14 ++---
src/assets/md/docs/installation.md | 56 ++++++++++++++++++-
10 files changed, 187 insertions(+), 28 deletions(-)
delete mode 100644 projects/ngx-markdoc/src/lib/config.ts
create mode 100644 projects/ngx-markdoc/src/lib/provide-markdoc-options.ts
create mode 100644 src/app/markdoc-example.ts
diff --git a/README.md b/README.md
index 7dd7ab2..37491e0 100644
--- a/README.md
+++ b/README.md
@@ -28,16 +28,13 @@ Import `Markdoc` into your component and use `` in your templ
```ts
import { Component } from '@angular/core';
-import { ActivatedRoute } from '@angular/router';
import { Markdoc } from '@notiz/ngx-markdoc';
@Component({
selector: 'app-docs',
standalone: true,
imports: [Markdoc],
- template: `
-
- `,
+ template: ` `,
})
export class DocsComponent {}
```
@@ -59,3 +56,51 @@ export class DocsComponent {}
```html
```
+
+## Options
+
+Use `provideMarkdocOptions` to optionally pass a Markdoc configuration options.
+
+```ts
+import { ApplicationConfig } from '@angular/core';
+import { provideRouter } from '@angular/router';
+
+import { routes } from './app.routes';
+import { provideHttpClient } from '@angular/common/http';
+
+import { provideMarkdocOptions } from '@notiz/ngx-markdoc';
+import { Config, Node, Tag } from '@markdoc/markdoc';
+
+export const appConfig: ApplicationConfig = {
+ providers: [
+ provideRouter(routes),
+ provideHttpClient(),
+ provideMarkdocOptions({
+ config: {
+ tags: {
+ figure: {
+ selfClosing: true,
+ attributes: {
+ src: { type: String, required: true },
+ alt: { type: String, required: true },
+ caption: { type: String, required: true },
+ },
+ transform: (node: Node, config: Config) => {
+ const { src, alt, caption } = node.transformAttributes(config);
+ const imageTag = new Tag('img', { src, alt });
+ const captionTag = new Tag('figcaption', {}, [caption]);
+ return new Tag('figure', {}, [imageTag, captionTag]);
+ },
+ },
+ },
+ },
+ }),
+ ],
+};
+```
+
+Now you can use `{% figure %}` tag in your Markdown file
+
+```md
+{% figure src="https://images.unsplash.com/photo-1610296669228-602fa827fc1f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1675&q=80" alt="Pelican nebulae mosaic" caption="Pelican nebulae mosaic" /%}
+```
diff --git a/projects/ngx-markdoc/src/lib/config.ts b/projects/ngx-markdoc/src/lib/config.ts
deleted file mode 100644
index 3fa428b..0000000
--- a/projects/ngx-markdoc/src/lib/config.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { figure } from './extensions/figure.markdoc';
-import { Config } from '@markdoc/markdoc';
-import { heading, image } from './extensions';
-
-export const defaultConfig: Config = {
- nodes: { heading, image },
- tags: { figure },
-};
diff --git a/projects/ngx-markdoc/src/lib/extensions/index.ts b/projects/ngx-markdoc/src/lib/extensions/index.ts
index a170cf1..b9c7810 100644
--- a/projects/ngx-markdoc/src/lib/extensions/index.ts
+++ b/projects/ngx-markdoc/src/lib/extensions/index.ts
@@ -1,3 +1,4 @@
+export * from './figure.markdoc';
export * from './heading.markdoc';
export * from './image.markdoc';
export * from './toc.markdoc';
diff --git a/projects/ngx-markdoc/src/lib/markdoc.component.ts b/projects/ngx-markdoc/src/lib/markdoc.component.ts
index 459e8c3..10c54e0 100644
--- a/projects/ngx-markdoc/src/lib/markdoc.component.ts
+++ b/projects/ngx-markdoc/src/lib/markdoc.component.ts
@@ -21,7 +21,7 @@ import MarkdocRenderer, {
Tag,
} from '@markdoc/markdoc';
import * as yaml from 'js-yaml';
-import { defaultConfig } from './config';
+import { MARKDOC_CONFIG } from './provide-markdoc-options';
@Component({
selector: 'markdoc, [markdoc]',
@@ -31,11 +31,25 @@ import { defaultConfig } from './config';
export class Markdoc implements OnChanges, AfterViewInit {
private element = inject(ElementRef);
private http = inject(HttpClient);
+ private _defaultConfig = inject(MARKDOC_CONFIG, { optional: true });
+
+ private get defaultConfig(): Config {
+ return this._defaultConfig ?? {};
+ }
@Input() content: string | undefined;
@Input() src: string | undefined;
- @Input() config: Config | undefined;
+ private _config: Config | undefined;
+ @Input()
+ public get config(): Config | undefined {
+ return this._config;
+ }
+ public set config(value: Config | undefined) {
+ this._config = value
+ ? { ...this.defaultConfig, ...value }
+ : this.defaultConfig;
+ }
private _contentNode: RenderableTreeNode | undefined;
/**
@@ -80,6 +94,10 @@ export class Markdoc implements OnChanges, AfterViewInit {
}
@Output() frontmatterChange = new EventEmitter>();
+ constructor() {
+ this.config = this.defaultConfig;
+ }
+
ngOnChanges(changes: SimpleChanges): void {
if (this.content != undefined) {
this.render(this.content);
@@ -142,10 +160,9 @@ export class Markdoc implements OnChanges, AfterViewInit {
const variables = { ...(config?.variables || {}), markdoc };
const nodes = {
- ...defaultConfig.nodes,
...(config?.nodes || {}),
};
- return { ...config, tags: { ...defaultConfig.tags }, nodes, variables };
+ return { ...config, tags: { ...config?.tags }, nodes, variables };
}
private loadFrontmatter(ast: Node) {
diff --git a/projects/ngx-markdoc/src/lib/provide-markdoc-options.ts b/projects/ngx-markdoc/src/lib/provide-markdoc-options.ts
new file mode 100644
index 0000000..ec6bcc2
--- /dev/null
+++ b/projects/ngx-markdoc/src/lib/provide-markdoc-options.ts
@@ -0,0 +1,29 @@
+import {
+ EnvironmentProviders,
+ InjectionToken,
+ makeEnvironmentProviders,
+} from '@angular/core';
+import { Config } from '@markdoc/markdoc';
+import { figure, heading, image } from './extensions';
+
+export const MARKDOC_CONFIG = new InjectionToken('MARKDOC_CONFIG');
+
+export interface MarkdocOptions {
+ config?: Config;
+}
+
+const defaultConfig: Config = {
+ nodes: { heading, image },
+ tags: { figure },
+};
+
+export function provideMarkdocOptions(
+ markdocOptions?: MarkdocOptions,
+): EnvironmentProviders {
+ return makeEnvironmentProviders([
+ {
+ provide: MARKDOC_CONFIG,
+ useValue: markdocOptions?.config ?? defaultConfig,
+ },
+ ]);
+}
diff --git a/projects/ngx-markdoc/src/public-api.ts b/projects/ngx-markdoc/src/public-api.ts
index 0615e8a..0231227 100644
--- a/projects/ngx-markdoc/src/public-api.ts
+++ b/projects/ngx-markdoc/src/public-api.ts
@@ -1,3 +1,3 @@
export * from './lib/extensions';
-export * from './lib/config';
export * from './lib/markdoc.component';
+export * from './lib/provide-markdoc-options';
diff --git a/src/app/app.config.ts b/src/app/app.config.ts
index 63cbbca..89671fb 100644
--- a/src/app/app.config.ts
+++ b/src/app/app.config.ts
@@ -4,6 +4,14 @@ import { provideRouter, withInMemoryScrolling } from '@angular/router';
import { routes } from './app.routes';
import { provideHttpClient, withFetch } from '@angular/common/http';
import { provideClientHydration } from '@angular/platform-browser';
+import {
+ figure,
+ heading,
+ image,
+ provideMarkdocOptions,
+} from '@notiz/ngx-markdoc';
+import { Config, Node, Tag } from '@markdoc/markdoc';
+import { markdocExample } from './markdoc-example';
export const appConfig: ApplicationConfig = {
providers: [
@@ -15,5 +23,11 @@ export const appConfig: ApplicationConfig = {
),
provideHttpClient(withFetch()),
provideClientHydration(),
+ provideMarkdocOptions({
+ config: {
+ nodes: { heading, image },
+ tags: { figure, markdocExample },
+ },
+ }),
],
};
diff --git a/src/app/markdoc-example.ts b/src/app/markdoc-example.ts
new file mode 100644
index 0000000..c48dabe
--- /dev/null
+++ b/src/app/markdoc-example.ts
@@ -0,0 +1,13 @@
+import { Schema, Tag } from '@markdoc/markdoc';
+
+export const markdocExample: Schema = {
+ render: 'pre',
+ attributes: {},
+ transform(node, config) {
+ console.log('test');
+ const attributes = node.transformAttributes(config);
+ const { content, language } = node.children[0].attributes;
+
+ return new Tag('pre', { ...attributes, language }, [content]);
+ },
+};
diff --git a/src/app/pages/landing.component.ts b/src/app/pages/landing.component.ts
index 3f8162f..b947bda 100644
--- a/src/app/pages/landing.component.ts
+++ b/src/app/pages/landing.component.ts
@@ -1,5 +1,5 @@
import { HttpClient } from '@angular/common/http';
-import { Component, OnInit } from '@angular/core';
+import { Component, inject } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { Markdoc } from '@notiz/ngx-markdoc';
import { Prose } from '../components/prose.component';
@@ -7,6 +7,8 @@ import { Hero } from '../components/hero.component';
@Component({
selector: 'app-landing',
+ standalone: true,
+ imports: [Hero, Prose, Markdoc, AsyncPipe],
template: `
@@ -34,13 +36,9 @@ import { Hero } from '../components/hero.component';
`,
- styles: [],
- standalone: true,
- imports: [Hero, Prose, Markdoc, AsyncPipe],
})
-export class LandingComponent implements OnInit {
- example$ = this.http.get('assets/md/example.md', { responseType: 'text' });
- constructor(private http: HttpClient) {}
+export class LandingComponent {
+ private http = inject(HttpClient);
- ngOnInit(): void {}
+ example$ = this.http.get('assets/md/example.md', { responseType: 'text' });
}
diff --git a/src/assets/md/docs/installation.md b/src/assets/md/docs/installation.md
index 54280d2..0fedbee 100644
--- a/src/assets/md/docs/installation.md
+++ b/src/assets/md/docs/installation.md
@@ -39,9 +39,7 @@ import { Markdoc } from '@notiz/ngx-markdoc';
selector: 'app-docs',
standalone: true,
imports: [Markdoc],
- template: `
-
- `,
+ template: ` `,
})
export class DocsComponent {}
```
@@ -63,3 +61,55 @@ export class DocsComponent {}
```html
```
+
+## Options
+
+Use `provideMarkdocOptions` to optionally pass a Markdoc configuration options.
+
+```ts
+import { ApplicationConfig } from '@angular/core';
+import { provideRouter } from '@angular/router';
+
+import { routes } from './app.routes';
+import { provideHttpClient } from '@angular/common/http';
+
+import { provideMarkdocOptions } from '@notiz/ngx-markdoc';
+import { Config, Node, Tag } from '@markdoc/markdoc';
+
+export const appConfig: ApplicationConfig = {
+ providers: [
+ provideRouter(routes),
+ provideHttpClient(),
+ provideMarkdocOptions({
+ config: {
+ tags: {
+ figure: {
+ selfClosing: true,
+ attributes: {
+ src: { type: String, required: true },
+ alt: { type: String, required: true },
+ caption: { type: String, required: true },
+ },
+ transform: (node: Node, config: Config) => {
+ const { src, alt, caption } = node.transformAttributes(config);
+ const imageTag = new Tag('img', { src, alt });
+ const captionTag = new Tag('figcaption', {}, [caption]);
+ return new Tag('figure', {}, [imageTag, captionTag]);
+ },
+ },
+ },
+ },
+ }),
+ ],
+};
+```
+
+Now you can use `{% figure %}` tag in your Markdown file
+
+{% markdocExample %}
+
+```md
+{% figure src="https://images.unsplash.com/photo-1610296669228-602fa827fc1f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1675&q=80" alt="Pelican nebulae mosaic" caption="Pelican nebulae mosaic" /%}
+```
+
+{% /markdocExample %}