@analogjs/content 3.0.0-beta.40 → 3.0.0-beta.42

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.
@@ -15,14 +15,14 @@ var Md4xContentRendererService = class Md4xContentRendererService extends Conten
15
15
  this.options = inject(MD4X_RENDERER_OPTIONS, { optional: true });
16
16
  }
17
17
  async render(content) {
18
- const { renderToHtml, parseMeta } = await import("md4x/napi");
18
+ const { renderToHtml, parseMeta, heal } = await import("md4x/napi");
19
+ const source = this.options?.heal ? heal(content) : content;
19
20
  return {
20
- content: renderToHtml(content, {
21
- heal: this.options?.heal,
21
+ content: renderToHtml(source, {
22
22
  highlighter: this.options?.highlighter,
23
23
  headingIds: true
24
24
  }),
25
- toc: parseMeta(content).headings
25
+ toc: parseMeta(source).headings
26
26
  };
27
27
  }
28
28
  static {
@@ -69,13 +69,13 @@ var Md4xWasmContentRendererService = class Md4xWasmContentRendererService extend
69
69
  const wasm = await import("md4x/wasm");
70
70
  if (!this.initPromise) this.initPromise = wasm.init();
71
71
  await this.initPromise;
72
+ const source = this.options?.heal ? wasm.heal(content) : content;
72
73
  return {
73
- content: wasm.renderToHtml(content, {
74
- heal: this.options?.heal,
74
+ content: wasm.renderToHtml(source, {
75
75
  highlighter: this.options?.highlighter,
76
76
  headingIds: true
77
77
  }),
78
- toc: wasm.parseMeta(content).headings
78
+ toc: wasm.parseMeta(source).headings
79
79
  };
80
80
  }
81
81
  static {
@@ -181,7 +181,8 @@ async function streamMarkdown(input, options) {
181
181
  let buffer = "";
182
182
  let closed = false;
183
183
  const reader = input.getReader();
184
- const release = (reason) => {
184
+ const release = async (reason) => {
185
+ if (closed) return;
185
186
  closed = true;
186
187
  return reader.cancel(reason).finally(() => reader.releaseLock());
187
188
  };
@@ -1 +1 @@
1
- {"version":3,"file":"analogjs-content-md4x.mjs","names":[],"sources":["../../md4x/src/lib/md4x-content-renderer.service.ts","../../md4x/src/lib/md4x-wasm-content-renderer.service.ts","../../md4x/src/lib/provide-md4x.ts","../../md4x/src/lib/streaming-markdown-renderer.ts"],"sourcesContent":["import { Injectable, inject, InjectionToken } from '@angular/core';\n\nimport {\n ContentRenderer,\n RenderedContent,\n} from '../../../src/lib/content-renderer';\n\n/**\n * Options for the experimental md4x-based content renderer.\n *\n * @experimental md4x integration is experimental and may change in future releases.\n */\nexport interface Md4xRendererOptions {\n /** Heal incomplete markdown (useful for streaming/LLM content). */\n heal?: boolean;\n /** Custom code block highlighter. Receives raw code and block metadata,\n * returns highlighted HTML or undefined to keep default rendering. */\n highlighter?: (\n code: string,\n block: { lang: string; filename?: string; highlights?: number[] },\n ) => string | undefined;\n}\n\nexport const MD4X_RENDERER_OPTIONS: InjectionToken<Md4xRendererOptions> =\n new InjectionToken<Md4xRendererOptions>('md4x_renderer_options');\n\n/**\n * Content renderer backed by md4x (C-based CommonMark parser compiled with Zig).\n * 50-70x faster than marked for complex documents.\n *\n * @experimental md4x integration is experimental and may change in future releases.\n */\n@Injectable()\nexport class Md4xContentRendererService extends ContentRenderer {\n private options = inject(MD4X_RENDERER_OPTIONS, { optional: true });\n\n override async render(content: string): Promise<RenderedContent> {\n const { renderToHtml, parseMeta } = await import('md4x/napi');\n const html = renderToHtml(content, {\n heal: this.options?.heal,\n highlighter: this.options?.highlighter,\n headingIds: true,\n });\n return { content: html, toc: parseMeta(content).headings };\n }\n}\n","import { Injectable, inject } from '@angular/core';\n\nimport {\n ContentRenderer,\n RenderedContent,\n} from '../../../src/lib/content-renderer';\nimport {\n MD4X_RENDERER_OPTIONS,\n Md4xRendererOptions,\n} from './md4x-content-renderer.service';\n\n/**\n * Content renderer backed by md4x/wasm for client-side (browser) rendering.\n * ~100KB gzip, 3-6x faster than marked in the browser.\n *\n * @experimental md4x integration is experimental and may change in future releases.\n */\n@Injectable()\nexport class Md4xWasmContentRendererService extends ContentRenderer {\n private options = inject(MD4X_RENDERER_OPTIONS, { optional: true });\n private initPromise: Promise<void> | null = null;\n\n override async render(content: string): Promise<RenderedContent> {\n const wasm = await import('md4x/wasm');\n if (!this.initPromise) {\n this.initPromise = wasm.init();\n }\n await this.initPromise;\n\n const html = wasm.renderToHtml(content, {\n heal: this.options?.heal,\n highlighter: this.options?.highlighter,\n headingIds: true,\n });\n return { content: html, toc: wasm.parseMeta(content).headings };\n }\n}\n","import { Provider } from '@angular/core';\n\nimport { ContentRenderer } from '../../../src/lib/content-renderer';\nimport { withContentFileLoader } from '../../../src/lib/content-file-loader';\nimport { withContentListLoader } from '../../../src/lib/content-list-loader';\nimport {\n Md4xContentRendererService,\n MD4X_RENDERER_OPTIONS,\n} from './md4x-content-renderer.service';\nimport { Md4xWasmContentRendererService } from './md4x-wasm-content-renderer.service';\nimport type { Md4xRendererOptions } from './md4x-content-renderer.service';\n\n/**\n * Provides the experimental md4x-based content renderer (NAPI, server/build-time).\n *\n * @experimental md4x integration is experimental and may change in future releases.\n *\n * @example\n * ```typescript\n * provideContent(withMd4xRenderer());\n * provideContent(withMd4xRenderer({ heal: true }));\n * ```\n */\nexport function withMd4xRenderer(options?: Md4xRendererOptions): Provider {\n return [\n { provide: ContentRenderer, useClass: Md4xContentRendererService },\n options ? { provide: MD4X_RENDERER_OPTIONS, useValue: options } : [],\n withContentFileLoader(),\n withContentListLoader(),\n ];\n}\n\n/**\n * Provides the experimental md4x WASM content renderer (browser/CSR).\n * ~100KB gzip, 3-6x faster than marked in the browser.\n *\n * @experimental md4x integration is experimental and may change in future releases.\n *\n * @example\n * ```typescript\n * provideContent(withMd4xWasmRenderer());\n * ```\n */\nexport function withMd4xWasmRenderer(options?: Md4xRendererOptions): Provider {\n return [\n { provide: ContentRenderer, useClass: Md4xWasmContentRendererService },\n options ? { provide: MD4X_RENDERER_OPTIONS, useValue: options } : [],\n withContentFileLoader(),\n withContentListLoader(),\n ];\n}\n","/**\n * Transforms a stream of markdown chunks into a stream of rendered HTML.\n * Uses md4x's `heal()` to fix incomplete markdown from streaming sources\n * (LLMs, collaborative editing) so each emitted HTML chunk is valid.\n *\n * @experimental Streaming markdown support is experimental and may change in future releases.\n *\n * @example\n * ```typescript\n * // In a Nitro API route\n * import { streamMarkdown } from '@analogjs/content';\n *\n * export default defineEventHandler(async (event) => {\n * const llmStream = getAIStream(prompt);\n * return streamMarkdown(llmStream, { heal: true });\n * });\n * ```\n */\nexport async function streamMarkdown(\n input: ReadableStream<string>,\n options?: { heal?: boolean },\n): Promise<ReadableStream<string>> {\n const { renderToHtml, heal } = await import('md4x/napi');\n\n let buffer = '';\n let closed = false;\n const reader = input.getReader();\n\n const release = (reason?: unknown) => {\n closed = true;\n return reader.cancel(reason).finally(() => reader.releaseLock());\n };\n\n return new ReadableStream<string>({\n async pull(controller) {\n try {\n const { done, value } = await reader.read();\n if (closed) {\n return;\n }\n\n if (done) {\n closed = true;\n reader.releaseLock();\n if (buffer) {\n const source = options?.heal ? heal(buffer) : buffer;\n controller.enqueue(renderToHtml(source));\n }\n controller.close();\n return;\n }\n\n buffer += value;\n const source = options?.heal ? heal(buffer) : buffer;\n controller.enqueue(renderToHtml(source));\n } catch (error) {\n controller.error(error);\n await release(error).catch(() => undefined);\n }\n },\n cancel(reason) {\n return release(reason);\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"analogjs-content-md4x.mjs","names":[],"sources":["../../md4x/src/lib/md4x-content-renderer.service.ts","../../md4x/src/lib/md4x-wasm-content-renderer.service.ts","../../md4x/src/lib/provide-md4x.ts","../../md4x/src/lib/streaming-markdown-renderer.ts"],"sourcesContent":["import { Injectable, inject, InjectionToken } from '@angular/core';\n\nimport {\n ContentRenderer,\n RenderedContent,\n} from '../../../src/lib/content-renderer';\n\n/**\n * Options for the experimental md4x-based content renderer.\n *\n * @experimental md4x integration is experimental and may change in future releases.\n */\nexport interface Md4xRendererOptions {\n /** Heal incomplete markdown (useful for streaming/LLM content). */\n heal?: boolean;\n /** Custom code block highlighter. Receives raw code and block metadata,\n * returns highlighted HTML or undefined to keep default rendering. */\n highlighter?: (\n code: string,\n block: { lang: string; filename?: string; highlights?: number[] },\n ) => string | undefined;\n}\n\nexport const MD4X_RENDERER_OPTIONS: InjectionToken<Md4xRendererOptions> =\n new InjectionToken<Md4xRendererOptions>('md4x_renderer_options');\n\n/**\n * Content renderer backed by md4x (C-based CommonMark parser compiled with Zig).\n * 50-70x faster than marked for complex documents.\n *\n * @experimental md4x integration is experimental and may change in future releases.\n */\n@Injectable()\nexport class Md4xContentRendererService extends ContentRenderer {\n private options = inject(MD4X_RENDERER_OPTIONS, { optional: true });\n\n override async render(content: string): Promise<RenderedContent> {\n const { renderToHtml, parseMeta, heal } = await import('md4x/napi');\n const source = this.options?.heal ? heal(content) : content;\n const html = renderToHtml(source, {\n highlighter: this.options?.highlighter,\n headingIds: true,\n });\n return { content: html, toc: parseMeta(source).headings };\n }\n}\n","import { Injectable, inject } from '@angular/core';\n\nimport {\n ContentRenderer,\n RenderedContent,\n} from '../../../src/lib/content-renderer';\nimport {\n MD4X_RENDERER_OPTIONS,\n Md4xRendererOptions,\n} from './md4x-content-renderer.service';\n\n/**\n * Content renderer backed by md4x/wasm for client-side (browser) rendering.\n * ~100KB gzip, 3-6x faster than marked in the browser.\n *\n * @experimental md4x integration is experimental and may change in future releases.\n */\n@Injectable()\nexport class Md4xWasmContentRendererService extends ContentRenderer {\n private options = inject(MD4X_RENDERER_OPTIONS, { optional: true });\n private initPromise: Promise<void> | null = null;\n\n override async render(content: string): Promise<RenderedContent> {\n const wasm = await import('md4x/wasm');\n if (!this.initPromise) {\n this.initPromise = wasm.init();\n }\n await this.initPromise;\n\n const source = this.options?.heal ? wasm.heal(content) : content;\n const html = wasm.renderToHtml(source, {\n highlighter: this.options?.highlighter,\n headingIds: true,\n });\n return { content: html, toc: wasm.parseMeta(source).headings };\n }\n}\n","import { Provider } from '@angular/core';\n\nimport { ContentRenderer } from '../../../src/lib/content-renderer';\nimport { withContentFileLoader } from '../../../src/lib/content-file-loader';\nimport { withContentListLoader } from '../../../src/lib/content-list-loader';\nimport {\n Md4xContentRendererService,\n MD4X_RENDERER_OPTIONS,\n} from './md4x-content-renderer.service';\nimport { Md4xWasmContentRendererService } from './md4x-wasm-content-renderer.service';\nimport type { Md4xRendererOptions } from './md4x-content-renderer.service';\n\n/**\n * Provides the experimental md4x-based content renderer (NAPI, server/build-time).\n *\n * @experimental md4x integration is experimental and may change in future releases.\n *\n * @example\n * ```typescript\n * provideContent(withMd4xRenderer());\n * provideContent(withMd4xRenderer({ heal: true }));\n * ```\n */\nexport function withMd4xRenderer(options?: Md4xRendererOptions): Provider {\n return [\n { provide: ContentRenderer, useClass: Md4xContentRendererService },\n options ? { provide: MD4X_RENDERER_OPTIONS, useValue: options } : [],\n withContentFileLoader(),\n withContentListLoader(),\n ];\n}\n\n/**\n * Provides the experimental md4x WASM content renderer (browser/CSR).\n * ~100KB gzip, 3-6x faster than marked in the browser.\n *\n * @experimental md4x integration is experimental and may change in future releases.\n *\n * @example\n * ```typescript\n * provideContent(withMd4xWasmRenderer());\n * ```\n */\nexport function withMd4xWasmRenderer(options?: Md4xRendererOptions): Provider {\n return [\n { provide: ContentRenderer, useClass: Md4xWasmContentRendererService },\n options ? { provide: MD4X_RENDERER_OPTIONS, useValue: options } : [],\n withContentFileLoader(),\n withContentListLoader(),\n ];\n}\n","/**\n * Transforms a stream of markdown chunks into a stream of rendered HTML.\n * Uses md4x's `heal()` to fix incomplete markdown from streaming sources\n * (LLMs, collaborative editing) so each emitted HTML chunk is valid.\n *\n * @experimental Streaming markdown support is experimental and may change in future releases.\n *\n * @example\n * ```typescript\n * // In a Nitro API route\n * import { streamMarkdown } from '@analogjs/content';\n *\n * export default defineEventHandler(async (event) => {\n * const llmStream = getAIStream(prompt);\n * return streamMarkdown(llmStream, { heal: true });\n * });\n * ```\n */\nexport async function streamMarkdown(\n input: ReadableStream<string>,\n options?: { heal?: boolean },\n): Promise<ReadableStream<string>> {\n const { renderToHtml, heal } = await import('md4x/napi');\n\n let buffer = '';\n let closed = false;\n const reader = input.getReader();\n\n const release = async (reason?: unknown) => {\n if (closed) {\n return;\n }\n closed = true;\n return reader.cancel(reason).finally(() => reader.releaseLock());\n };\n\n return new ReadableStream<string>({\n async pull(controller) {\n try {\n const { done, value } = await reader.read();\n if (closed) {\n return;\n }\n\n if (done) {\n closed = true;\n reader.releaseLock();\n if (buffer) {\n const source = options?.heal ? heal(buffer) : buffer;\n controller.enqueue(renderToHtml(source));\n }\n controller.close();\n return;\n }\n\n buffer += value;\n const source = options?.heal ? heal(buffer) : buffer;\n controller.enqueue(renderToHtml(source));\n } catch (error) {\n controller.error(error);\n await release(error).catch(() => undefined);\n }\n },\n cancel(reason) {\n return release(reason);\n },\n });\n}\n"],"mappings":";;;;AAuBA,IAAa,wBACX,IAAI,eAAoC,uBAAuB;;;;;;;AASjE,IAAa,6BAAb,MAAa,mCAAmC,gBAAe;CAD/D,cAAA;;EAEU,KAAA,UAAU,OAAO,uBAAuB,EAAE,UAAU,KAAI,CAAE;CAWnE;CATU,MAAM,OAAO,SAAe;EACnC,MAAM,EAAE,cAAc,WAAW,SAAS,MAAM,OAAO;EACvD,MAAM,SAAS,KAAK,SAAS,OAAO,KAAK,OAAO,IAAI;EAKpD,OAAO;GAAE,SAJI,aAAa,QAAQ;IAChC,aAAa,KAAK,SAAS;IAC3B,YAAY;GACb,CACiB;GAAM,KAAK,UAAU,MAAM,CAAC,CAAC;EAAQ;CACzD;CAXW;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAA0B,MAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAA1B;EAAA,KAAA,QAAA,GAAA,sBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;EAA0B,CAAA;CAAA;;AAA1B,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAA0B,YAAA,CAAA,EADtC,MAAA,WAAA,CAAA;;;;;;;;;;ACdD,IAAa,iCAAb,MAAa,uCAAuC,gBAAe;CADnE,cAAA;;EAEU,KAAA,UAAU,OAAO,uBAAuB,EAAE,UAAU,KAAI,CAAE;EAC1D,KAAA,cAAoC;CAgB7C;CAdU,MAAM,OAAO,SAAe;EACnC,MAAM,OAAO,MAAM,OAAO;EAC1B,IAAI,CAAC,KAAK,aACR,KAAK,cAAc,KAAK,KAAI;EAE9B,MAAM,KAAK;EAEX,MAAM,SAAS,KAAK,SAAS,OAAO,KAAK,KAAK,OAAO,IAAI;EAKzD,OAAO;GAAE,SAJI,KAAK,aAAa,QAAQ;IACrC,aAAa,KAAK,SAAS;IAC3B,YAAY;GACb,CACiB;GAAM,KAAK,KAAK,UAAU,MAAM,CAAC,CAAC;EAAQ;CAC9D;CAjBW;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAA8B,MAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAA9B;EAAA,KAAA,QAAA,GAAA,sBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;EAA8B,CAAA;CAAA;;AAA9B,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAA8B,YAAA,CAAA,EAD1C,MAAA,WAAA,CAAA;;;;;;;;;;;;;;;ACMD,SAAgB,iBAAiB,SAA6B;CAC5D,OAAO;EACL;GAAE,SAAS;GAAiB,UAAU;EAA0B;EAChE,UAAU;GAAE,SAAS;GAAuB,UAAU;EAAO,IAAK,CAAA;EAClE,sBAAqB;EACrB,sBAAqB;CACtB;AACH;;;;;;;;;;;;AAaA,SAAgB,qBAAqB,SAA6B;CAChE,OAAO;EACL;GAAE,SAAS;GAAiB,UAAU;EAA8B;EACpE,UAAU;GAAE,SAAS;GAAuB,UAAU;EAAO,IAAK,CAAA;EAClE,sBAAqB;EACrB,sBAAqB;CACtB;AACH;;;;;;;;;;;;;;;;;;;;;AChCA,eAAsB,eACpB,OACA,SAA4B;CAE5B,MAAM,EAAE,cAAc,SAAS,MAAM,OAAO;CAE5C,IAAI,SAAS;CACb,IAAI,SAAS;CACb,MAAM,SAAS,MAAM,UAAS;CAE9B,MAAM,UAAU,OAAO,WAAoB;EACzC,IAAI,QACF;EAEF,SAAS;EACT,OAAO,OAAO,OAAO,MAAM,CAAC,CAAC,cAAc,OAAO,YAAW,CAAE;CACjE;CAEA,OAAO,IAAI,eAAuB;EAChC,MAAM,KAAK,YAAU;GACnB,IAAI;IACF,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAI;IACzC,IAAI,QACF;IAGF,IAAI,MAAM;KACR,SAAS;KACT,OAAO,YAAW;KAClB,IAAI,QAAQ;MACV,MAAM,SAAS,SAAS,OAAO,KAAK,MAAM,IAAI;MAC9C,WAAW,QAAQ,aAAa,MAAM,CAAC;KACzC;KACA,WAAW,MAAK;KAChB;IACF;IAEA,UAAU;IACV,MAAM,SAAS,SAAS,OAAO,KAAK,MAAM,IAAI;IAC9C,WAAW,QAAQ,aAAa,MAAM,CAAC;GACzC,SAAS,OAAO;IACd,WAAW,MAAM,KAAK;IACtB,MAAM,QAAQ,KAAK,CAAC,CAAC,YAAY,KAAA,CAAS;GAC5C;EACF;EACA,OAAO,QAAM;GACX,OAAO,QAAQ,MAAM;EACvB;CACD,CAAA;AACH"}
@@ -2,7 +2,7 @@ import { n as parseRawContentFileAsync, t as parseRawContentFile } from "./parse
2
2
  import { _ as NoopContentRenderer, a as injectContentFileLoader, c as injectContentFilesMap, d as CONTENT_LOCALE, f as filterByLocale, g as ContentRenderer, h as withLocaleCandidates, i as CONTENT_FILE_LOADER, l as RenderTaskService, m as withLocale, n as injectContentListLoader, o as withContentFileLoader, p as injectContentLocale, r as withContentListLoader, s as injectContentFiles, t as CONTENT_LIST_LOADER, u as CONTENT_FILES_TOKEN } from "./content-list-loader.mjs";
3
3
  import { t as FrontmatterValidationError } from "./frontmatter-validation-error.mjs";
4
4
  import * as i0 from "@angular/core";
5
- import { Component, Directive, HostListener, InjectionToken, Input, NgZone, PLATFORM_ID, ViewEncapsulation, computed, inject, input } from "@angular/core";
5
+ import { Component, Directive, ElementRef, HostListener, InjectionToken, Input, NgZone, PLATFORM_ID, ViewEncapsulation, computed, inject, input } from "@angular/core";
6
6
  import { DOCUMENT, Location, isPlatformBrowser } from "@angular/common";
7
7
  import { ActivatedRoute, Router } from "@angular/router";
8
8
  import { Observable, firstValueFrom, from, isObservable, of } from "rxjs";
@@ -13,11 +13,14 @@ import { takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop";
13
13
  var AnchorNavigationDirective = class AnchorNavigationDirective {
14
14
  constructor() {
15
15
  this.document = inject(DOCUMENT);
16
+ this.host = inject(ElementRef);
16
17
  this.location = inject(Location);
17
18
  this.router = inject(Router);
18
19
  }
19
- handleNavigation(element) {
20
- if (element instanceof HTMLAnchorElement && isInternalUrl(element, this.document) && hasTargetSelf(element) && !hasDownloadAttribute(element)) {
20
+ handleNavigation(target, event) {
21
+ if (event && shouldSkipClick(event)) return true;
22
+ const element = target instanceof Element ? target.closest("a") : null;
23
+ if (element && this.host.nativeElement.contains(element) && isInternalUrl(element, this.document) && hasTargetSelf(element) && !hasDownloadAttribute(element)) {
21
24
  const { pathname, search, hash } = element;
22
25
  const isFragmentOnly = element.getAttribute("href")?.startsWith("#");
23
26
  const resolvedPathname = isFragmentOnly ? this.document.location.pathname : pathname;
@@ -45,7 +48,7 @@ var AnchorNavigationDirective = class AnchorNavigationDirective {
45
48
  type: AnchorNavigationDirective,
46
49
  isStandalone: true,
47
50
  selector: "[analogAnchorNavigation]",
48
- host: { listeners: { "click": "handleNavigation($event.target)" } },
51
+ host: { listeners: { "click": "handleNavigation($event.target,$event)" } },
49
52
  ngImport: i0
50
53
  });
51
54
  }
@@ -64,9 +67,12 @@ i0.ɵɵngDeclareClassMetadata({
64
67
  }],
65
68
  propDecorators: { handleNavigation: [{
66
69
  type: HostListener,
67
- args: ["click", ["$event.target"]]
70
+ args: ["click", ["$event.target", "$event"]]
68
71
  }] }
69
72
  });
73
+ function shouldSkipClick(event) {
74
+ return event.defaultPrevented || event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey;
75
+ }
70
76
  function hasDownloadAttribute(anchorElement) {
71
77
  return anchorElement.getAttribute("download") !== null;
72
78
  }
@@ -1 +1 @@
1
- {"version":3,"file":"analogjs-content.mjs","names":[],"sources":["../../src/lib/anchor-navigation.directive.ts","../../src/lib/parse-content-file.ts","../../src/lib/utils/zone-wait-for.ts","../../src/lib/content.ts","../../src/lib/provide-content.ts","../../src/lib/markdown-route.component.ts","../../src/lib/markdown.component.ts"],"sourcesContent":["import { Directive, HostListener, inject } from '@angular/core';\nimport { DOCUMENT, Location } from '@angular/common';\nimport { Router } from '@angular/router';\n\n@Directive({\n selector: '[analogAnchorNavigation]',\n standalone: true,\n})\nexport class AnchorNavigationDirective {\n private readonly document = inject(DOCUMENT);\n private readonly location = inject(Location);\n private readonly router = inject(Router);\n\n @HostListener('click', ['$event.target'])\n handleNavigation(element: EventTarget | null): boolean {\n if (\n element instanceof HTMLAnchorElement &&\n isInternalUrl(element, this.document) &&\n hasTargetSelf(element) &&\n !hasDownloadAttribute(element)\n ) {\n const { pathname, search, hash } = element;\n // A `<base>` element makes the browser resolve fragment-only hrefs\n // (e.g. `href=\"#some-id\"`) against the base URL rather than the\n // current document, so `pathname`/`search` come back as the base's\n // rather than the current page's. Fall back to the document's own\n // location in that case, or an in-page anchor link would navigate\n // to the base route and lose the fragment there.\n const isFragmentOnly = element.getAttribute('href')?.startsWith('#');\n const resolvedPathname = isFragmentOnly\n ? this.document.location.pathname\n : pathname;\n const resolvedSearch = isFragmentOnly\n ? this.document.location.search\n : search;\n const url = this.location.normalize(\n `${resolvedPathname}${resolvedSearch}${hash}`,\n );\n this.router.navigateByUrl(url);\n\n return false;\n }\n\n return true;\n }\n}\n\nfunction hasDownloadAttribute(anchorElement: HTMLAnchorElement): boolean {\n return anchorElement.getAttribute('download') !== null;\n}\n\nfunction hasTargetSelf(anchorElement: HTMLAnchorElement): boolean {\n return !anchorElement.target || anchorElement.target === '_self';\n}\n\nfunction isInternalUrl(\n anchorElement: HTMLAnchorElement,\n document: Document,\n): boolean {\n return (\n anchorElement.host === document.location.host &&\n anchorElement.protocol === document.location.protocol\n );\n}\n","export type ContentFileModule<\n Attributes extends Record<string, any> = Record<string, any>,\n> = {\n default: string;\n metadata: Attributes;\n};\n\nexport type ContentFileLoader = () => Promise<string | ContentFileModule>;\n\n/**\n * Reads a loaded content file. Build-time content modules carry their\n * frontmatter as `metadata`; raw strings from custom loaders are parsed\n * with a lazily loaded frontmatter parser.\n */\nexport async function parseContentFile<\n Attributes extends Record<string, any> = Record<string, any>,\n>(\n file: string | ContentFileModule,\n): Promise<{ content: string; attributes: Attributes }> {\n if (typeof file !== 'string') {\n return { content: file.default, attributes: file.metadata as Attributes };\n }\n\n const { parseRawContentFile } = await import('./parse-raw-content-file');\n return parseRawContentFile<Attributes>(file);\n}\n","import { firstValueFrom, isObservable, Observable } from 'rxjs';\n\ndeclare const Zone: any;\n\nexport async function waitFor<T>(prom: Promise<T> | Observable<T>): Promise<T> {\n if (isObservable(prom)) {\n prom = firstValueFrom(prom);\n }\n\n if (typeof Zone === 'undefined') {\n return prom;\n }\n\n const macroTask = Zone.current.scheduleMacroTask(\n `AnalogContentResolve-${Math.random()}`,\n () => {\n /* noop */\n },\n {},\n () => {\n /* noop */\n },\n );\n return prom.then((p: T) => {\n macroTask.invoke();\n return p;\n });\n}\n","/// <reference types=\"vite/client\" />\n\nimport { inject } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { from, Observable, of } from 'rxjs';\nimport { finalize, map, switchMap, tap } from 'rxjs/operators';\n\nimport { ContentFile } from './content-file';\nimport { ContentRenderer } from './content-renderer';\nimport { CONTENT_LOCALE, withLocaleCandidates } from './content-locale';\nimport { CONTENT_FILES_TOKEN } from './content-files-token';\nimport {\n type ContentFileLoader,\n type ContentFileModule,\n parseContentFile,\n} from './parse-content-file';\nimport { waitFor } from './utils/zone-wait-for';\nimport { RenderTaskService } from './render-task.service';\n\nfunction getContentFile<\n Attributes extends Record<string, any> = Record<string, any>,\n>(\n contentFiles: Record<string, ContentFileLoader>,\n prefix: string,\n slug: string,\n fallback: string,\n renderTaskService: RenderTaskService,\n contentRenderer: ContentRenderer,\n locale?: string | null,\n): Observable<ContentFile<Attributes | Record<string, never>>> {\n // Normalize file keys so both \"/src/content/...\" and \"/<project>/src/content/...\" resolve.\n const normalizedFiles: Record<string, ContentFileLoader> = {};\n for (const [key, resolver] of Object.entries(contentFiles)) {\n const normalizedKey = key\n .replace(/^(?:.*?)\\/content(?=\\/)/, '/src/content')\n .replace(/\\/{2,}/g, '/');\n normalizedFiles[normalizedKey] = resolver;\n }\n\n const base = `/src/content/${prefix}${slug}`.replace(/\\/{2,}/g, '/');\n const candidates = [`${base}.md`, `${base}/index.md`];\n\n const allCandidates = withLocaleCandidates(candidates, locale);\n const matchKey = allCandidates.find((k) => k in normalizedFiles);\n const contentFile = matchKey ? normalizedFiles[matchKey] : undefined;\n const resolvedBase = (matchKey || `${base}.md`).replace(/\\.md$/, '');\n\n if (!contentFile) {\n return of({\n filename: resolvedBase,\n attributes: {},\n slug: '',\n content: fallback,\n toc: [],\n });\n }\n\n const contentTask = renderTaskService.addRenderTask();\n return new Observable<string | ContentFileModule>((observer) => {\n // `waitFor` is a no-op in non-Zone environments (browser without zone.js),\n // so this branch works for both SSR and client. Keeping a single path also\n // avoids Rolldown evaluating `import.meta.env.SSR` at library-build time\n // and eliminating the task-clearing branch, which caused SSR to hang once\n // the content map resolved a real lazy import.\n waitFor(contentFile()).then((content) => {\n observer.next(content);\n observer.complete();\n\n setTimeout(() => renderTaskService.clearRenderTask(contentTask), 10);\n });\n }).pipe(\n switchMap((contentFile) => parseContentFile<Attributes>(contentFile)),\n switchMap(({ content, attributes }) =>\n from(contentRenderer.render(content)).pipe(\n map((rendered) => ({\n filename: resolvedBase,\n slug,\n attributes,\n content,\n toc: rendered.toc ?? [],\n })),\n ),\n ),\n finalize(() => {\n if (import.meta.env.SSR !== true) {\n renderTaskService.clearRenderTask(contentTask);\n }\n }),\n );\n}\n\n/**\n * Retrieves the static content using the provided param and/or prefix.\n *\n * @param param route parameter (default: 'slug')\n * @param fallback fallback text if content file is not found (default: 'No Content Found')\n */\nexport function injectContent<\n Attributes extends Record<string, any> = Record<string, any>,\n>(\n param:\n | string\n | {\n param: string;\n subdirectory: string;\n }\n | {\n customFilename: string;\n } = 'slug',\n fallback = 'No Content Found',\n): Observable<ContentFile<Attributes | Record<string, never>>> {\n const contentFiles = inject(CONTENT_FILES_TOKEN);\n const contentRenderer = inject(ContentRenderer);\n const renderTaskService = inject(RenderTaskService);\n const locale = inject(CONTENT_LOCALE, { optional: true });\n const task = renderTaskService.addRenderTask();\n\n if (typeof param === 'string' || 'param' in param) {\n const prefix = typeof param === 'string' ? '' : `${param.subdirectory}/`;\n const route = inject(ActivatedRoute);\n const paramKey = typeof param === 'string' ? param : param.param;\n return route.paramMap.pipe(\n map((params) => params.get(paramKey)),\n switchMap((slug) => {\n if (slug) {\n return getContentFile<Attributes>(\n contentFiles,\n prefix,\n slug,\n fallback,\n renderTaskService,\n contentRenderer,\n locale,\n );\n }\n return of({\n filename: '',\n slug: '',\n attributes: {},\n content: fallback,\n toc: [],\n });\n }),\n tap(() => renderTaskService.clearRenderTask(task)),\n );\n } else {\n return getContentFile<Attributes>(\n contentFiles,\n '',\n param.customFilename,\n fallback,\n renderTaskService,\n contentRenderer,\n locale,\n ).pipe(tap(() => renderTaskService.clearRenderTask(task)));\n }\n}\n","import { Provider, InjectionToken } from '@angular/core';\nimport { ContentRenderer, NoopContentRenderer } from './content-renderer';\nimport { RenderTaskService } from './render-task.service';\nimport { withContentFileLoader } from './content-file-loader';\nimport { withContentListLoader } from './content-list-loader';\n\nexport interface MarkdownRendererOptions {\n loadMermaid?: () => Promise<typeof import('mermaid')>;\n}\n\nconst CONTENT_RENDERER_PROVIDERS: Provider[] = [\n {\n provide: ContentRenderer,\n useClass: NoopContentRenderer,\n },\n withContentFileLoader(),\n withContentListLoader(),\n];\n\nexport function withMarkdownRenderer(\n options?: MarkdownRendererOptions,\n): Provider {\n return [\n CONTENT_RENDERER_PROVIDERS,\n options?.loadMermaid\n ? [\n {\n provide: MERMAID_IMPORT_TOKEN,\n useFactory: options.loadMermaid,\n },\n ]\n : [],\n ];\n}\n\nexport function provideContent(...features: Provider[]): Provider[] {\n return [\n { provide: RenderTaskService, useClass: RenderTaskService },\n ...features,\n ];\n}\n\nexport const MERMAID_IMPORT_TOKEN: InjectionToken<\n Promise<typeof import('mermaid')>\n> = new InjectionToken<Promise<typeof import('mermaid')>>('mermaid_import');\n","import {\n AfterViewChecked,\n Component,\n inject,\n Input,\n ViewEncapsulation,\n} from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { ActivatedRoute } from '@angular/router';\n\nimport { ContentRenderer } from './content-renderer';\nimport { AnchorNavigationDirective } from './anchor-navigation.directive';\n\n@Component({\n selector: 'analog-markdown-route',\n standalone: true,\n imports: [],\n hostDirectives: [AnchorNavigationDirective],\n preserveWhitespaces: true,\n encapsulation: ViewEncapsulation.None,\n template: `<div [innerHTML]=\"content\" [class]=\"classes\"></div>`,\n})\nexport default class AnalogMarkdownRouteComponent implements AfterViewChecked {\n private sanitizer = inject(DomSanitizer);\n private route = inject(ActivatedRoute);\n contentRenderer: ContentRenderer = inject(ContentRenderer);\n\n protected content: SafeHtml = this.sanitizer.bypassSecurityTrustHtml(\n this.route.snapshot.data['renderedAnalogContent'],\n );\n\n @Input() classes = 'analog-markdown-route';\n\n ngAfterViewChecked(): void {\n this.contentRenderer.enhance();\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n AfterViewChecked,\n Component,\n InputSignal,\n NgZone,\n PLATFORM_ID,\n Signal,\n ViewEncapsulation,\n computed,\n inject,\n input,\n} from '@angular/core';\nimport { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { ActivatedRoute, Data } from '@angular/router';\nimport { from, Observable, of } from 'rxjs';\nimport { catchError, map, switchMap } from 'rxjs/operators';\n\nimport { AnchorNavigationDirective } from './anchor-navigation.directive';\nimport { ContentRenderer } from './content-renderer';\nimport { MERMAID_IMPORT_TOKEN } from './provide-content';\n\n@Component({\n selector: 'analog-markdown',\n standalone: true,\n hostDirectives: [AnchorNavigationDirective],\n preserveWhitespaces: true,\n encapsulation: ViewEncapsulation.None,\n template: ` <div [innerHTML]=\"htmlContent()\" [class]=\"classes()\"></div> `,\n})\nexport default class AnalogMarkdownComponent implements AfterViewChecked {\n private sanitizer = inject(DomSanitizer);\n private route = inject(ActivatedRoute);\n private zone = inject(NgZone);\n private readonly platformId = inject(PLATFORM_ID);\n private readonly mermaidImport = inject(MERMAID_IMPORT_TOKEN, {\n optional: true,\n });\n private mermaid: typeof import('mermaid') | undefined;\n\n private contentSource: Signal<SafeHtml | string | undefined> = toSignal(\n this.getContentSource(),\n );\n readonly htmlContent: Signal<SafeHtml | string | undefined> = computed(() => {\n const inputContent = this.content();\n\n if (inputContent) {\n return this.sanitizer.bypassSecurityTrustHtml(inputContent as string);\n }\n\n return this.contentSource();\n });\n readonly content: InputSignal<string | object | null | undefined> = input<\n string | object | null\n >();\n readonly classes: InputSignal<string> = input('analog-markdown');\n\n contentRenderer: ContentRenderer = inject(ContentRenderer);\n\n constructor() {\n if (isPlatformBrowser(this.platformId) && this.mermaidImport) {\n // Mermaid can only be loaded on client side\n this.loadMermaid(this.mermaidImport);\n }\n }\n\n getContentSource(): Observable<SafeHtml | string> {\n return this.route.data.pipe(\n map<Data, string>((data) => data['_analogContent'] ?? ''),\n switchMap((contentString) => this.renderContent(contentString)),\n map((content) => this.sanitizer.bypassSecurityTrustHtml(content)),\n catchError((e) => of(`There was an error ${e}`)),\n );\n }\n\n async renderContent(content: string): Promise<string> {\n const rendered = await this.contentRenderer.render(content);\n return rendered.content;\n }\n\n ngAfterViewChecked(): void {\n this.contentRenderer.enhance();\n this.zone.runOutsideAngular(() => this.mermaid?.default.run());\n }\n\n private loadMermaid(mermaidImport: Promise<typeof import('mermaid')>) {\n this.zone.runOutsideAngular(() =>\n // Wrap into an observable to avoid redundant initialization once\n // the markdown component is destroyed before the promise is resolved.\n from(mermaidImport)\n .pipe(takeUntilDestroyed())\n .subscribe((mermaid) => {\n this.mermaid = mermaid;\n this.mermaid.default.initialize({ startOnLoad: false });\n // Explicitly running mermaid as ngAfterViewChecked\n // has probably already been called\n this.mermaid?.default.run();\n }),\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"analogjs-content.mjs","names":[],"sources":["../../src/lib/anchor-navigation.directive.ts","../../src/lib/parse-content-file.ts","../../src/lib/utils/zone-wait-for.ts","../../src/lib/content.ts","../../src/lib/provide-content.ts","../../src/lib/markdown-route.component.ts","../../src/lib/markdown.component.ts"],"sourcesContent":["import { Directive, ElementRef, HostListener, inject } from '@angular/core';\nimport { DOCUMENT, Location } from '@angular/common';\nimport { Router } from '@angular/router';\n\n@Directive({\n selector: '[analogAnchorNavigation]',\n standalone: true,\n})\nexport class AnchorNavigationDirective {\n private readonly document = inject(DOCUMENT);\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly location = inject(Location);\n private readonly router = inject(Router);\n\n @HostListener('click', ['$event.target', '$event'])\n handleNavigation(target: EventTarget | null, event?: MouseEvent): boolean {\n if (event && shouldSkipClick(event)) {\n return true;\n }\n\n const element = target instanceof Element ? target.closest('a') : null;\n if (\n element &&\n this.host.nativeElement.contains(element) &&\n isInternalUrl(element, this.document) &&\n hasTargetSelf(element) &&\n !hasDownloadAttribute(element)\n ) {\n const { pathname, search, hash } = element;\n // A `<base>` element makes the browser resolve fragment-only hrefs\n // (e.g. `href=\"#some-id\"`) against the base URL rather than the\n // current document, so `pathname`/`search` come back as the base's\n // rather than the current page's. Fall back to the document's own\n // location in that case, or an in-page anchor link would navigate\n // to the base route and lose the fragment there.\n const isFragmentOnly = element.getAttribute('href')?.startsWith('#');\n const resolvedPathname = isFragmentOnly\n ? this.document.location.pathname\n : pathname;\n const resolvedSearch = isFragmentOnly\n ? this.document.location.search\n : search;\n const url = this.location.normalize(\n `${resolvedPathname}${resolvedSearch}${hash}`,\n );\n this.router.navigateByUrl(url);\n\n return false;\n }\n\n return true;\n }\n}\n\nfunction shouldSkipClick(event: MouseEvent): boolean {\n return (\n event.defaultPrevented ||\n event.button !== 0 ||\n event.ctrlKey ||\n event.metaKey ||\n event.shiftKey ||\n event.altKey\n );\n}\n\nfunction hasDownloadAttribute(anchorElement: HTMLAnchorElement): boolean {\n return anchorElement.getAttribute('download') !== null;\n}\n\nfunction hasTargetSelf(anchorElement: HTMLAnchorElement): boolean {\n return !anchorElement.target || anchorElement.target === '_self';\n}\n\nfunction isInternalUrl(\n anchorElement: HTMLAnchorElement,\n document: Document,\n): boolean {\n return (\n anchorElement.host === document.location.host &&\n anchorElement.protocol === document.location.protocol\n );\n}\n","export type ContentFileModule<\n Attributes extends Record<string, any> = Record<string, any>,\n> = {\n default: string;\n metadata: Attributes;\n};\n\nexport type ContentFileLoader = () => Promise<string | ContentFileModule>;\n\n/**\n * Reads a loaded content file. Build-time content modules carry their\n * frontmatter as `metadata`; raw strings from custom loaders are parsed\n * with a lazily loaded frontmatter parser.\n */\nexport async function parseContentFile<\n Attributes extends Record<string, any> = Record<string, any>,\n>(\n file: string | ContentFileModule,\n): Promise<{ content: string; attributes: Attributes }> {\n if (typeof file !== 'string') {\n return { content: file.default, attributes: file.metadata as Attributes };\n }\n\n const { parseRawContentFile } = await import('./parse-raw-content-file');\n return parseRawContentFile<Attributes>(file);\n}\n","import { firstValueFrom, isObservable, Observable } from 'rxjs';\n\ndeclare const Zone: any;\n\nexport async function waitFor<T>(prom: Promise<T> | Observable<T>): Promise<T> {\n if (isObservable(prom)) {\n prom = firstValueFrom(prom);\n }\n\n if (typeof Zone === 'undefined') {\n return prom;\n }\n\n const macroTask = Zone.current.scheduleMacroTask(\n `AnalogContentResolve-${Math.random()}`,\n () => {\n /* noop */\n },\n {},\n () => {\n /* noop */\n },\n );\n return prom.then((p: T) => {\n macroTask.invoke();\n return p;\n });\n}\n","/// <reference types=\"vite/client\" />\n\nimport { inject } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { from, Observable, of } from 'rxjs';\nimport { finalize, map, switchMap, tap } from 'rxjs/operators';\n\nimport { ContentFile } from './content-file';\nimport { ContentRenderer } from './content-renderer';\nimport { CONTENT_LOCALE, withLocaleCandidates } from './content-locale';\nimport { CONTENT_FILES_TOKEN } from './content-files-token';\nimport {\n type ContentFileLoader,\n type ContentFileModule,\n parseContentFile,\n} from './parse-content-file';\nimport { waitFor } from './utils/zone-wait-for';\nimport { RenderTaskService } from './render-task.service';\n\nfunction getContentFile<\n Attributes extends Record<string, any> = Record<string, any>,\n>(\n contentFiles: Record<string, ContentFileLoader>,\n prefix: string,\n slug: string,\n fallback: string,\n renderTaskService: RenderTaskService,\n contentRenderer: ContentRenderer,\n locale?: string | null,\n): Observable<ContentFile<Attributes | Record<string, never>>> {\n // Normalize file keys so both \"/src/content/...\" and \"/<project>/src/content/...\" resolve.\n const normalizedFiles: Record<string, ContentFileLoader> = {};\n for (const [key, resolver] of Object.entries(contentFiles)) {\n const normalizedKey = key\n .replace(/^(?:.*?)\\/content(?=\\/)/, '/src/content')\n .replace(/\\/{2,}/g, '/');\n normalizedFiles[normalizedKey] = resolver;\n }\n\n const base = `/src/content/${prefix}${slug}`.replace(/\\/{2,}/g, '/');\n const candidates = [`${base}.md`, `${base}/index.md`];\n\n const allCandidates = withLocaleCandidates(candidates, locale);\n const matchKey = allCandidates.find((k) => k in normalizedFiles);\n const contentFile = matchKey ? normalizedFiles[matchKey] : undefined;\n const resolvedBase = (matchKey || `${base}.md`).replace(/\\.md$/, '');\n\n if (!contentFile) {\n return of({\n filename: resolvedBase,\n attributes: {},\n slug: '',\n content: fallback,\n toc: [],\n });\n }\n\n const contentTask = renderTaskService.addRenderTask();\n return new Observable<string | ContentFileModule>((observer) => {\n // `waitFor` is a no-op in non-Zone environments (browser without zone.js),\n // so this branch works for both SSR and client. Keeping a single path also\n // avoids Rolldown evaluating `import.meta.env.SSR` at library-build time\n // and eliminating the task-clearing branch, which caused SSR to hang once\n // the content map resolved a real lazy import.\n waitFor(contentFile()).then((content) => {\n observer.next(content);\n observer.complete();\n\n setTimeout(() => renderTaskService.clearRenderTask(contentTask), 10);\n });\n }).pipe(\n switchMap((contentFile) => parseContentFile<Attributes>(contentFile)),\n switchMap(({ content, attributes }) =>\n from(contentRenderer.render(content)).pipe(\n map((rendered) => ({\n filename: resolvedBase,\n slug,\n attributes,\n content,\n toc: rendered.toc ?? [],\n })),\n ),\n ),\n finalize(() => {\n if (import.meta.env.SSR !== true) {\n renderTaskService.clearRenderTask(contentTask);\n }\n }),\n );\n}\n\n/**\n * Retrieves the static content using the provided param and/or prefix.\n *\n * @param param route parameter (default: 'slug')\n * @param fallback fallback text if content file is not found (default: 'No Content Found')\n */\nexport function injectContent<\n Attributes extends Record<string, any> = Record<string, any>,\n>(\n param:\n | string\n | {\n param: string;\n subdirectory: string;\n }\n | {\n customFilename: string;\n } = 'slug',\n fallback = 'No Content Found',\n): Observable<ContentFile<Attributes | Record<string, never>>> {\n const contentFiles = inject(CONTENT_FILES_TOKEN);\n const contentRenderer = inject(ContentRenderer);\n const renderTaskService = inject(RenderTaskService);\n const locale = inject(CONTENT_LOCALE, { optional: true });\n const task = renderTaskService.addRenderTask();\n\n if (typeof param === 'string' || 'param' in param) {\n const prefix = typeof param === 'string' ? '' : `${param.subdirectory}/`;\n const route = inject(ActivatedRoute);\n const paramKey = typeof param === 'string' ? param : param.param;\n return route.paramMap.pipe(\n map((params) => params.get(paramKey)),\n switchMap((slug) => {\n if (slug) {\n return getContentFile<Attributes>(\n contentFiles,\n prefix,\n slug,\n fallback,\n renderTaskService,\n contentRenderer,\n locale,\n );\n }\n return of({\n filename: '',\n slug: '',\n attributes: {},\n content: fallback,\n toc: [],\n });\n }),\n tap(() => renderTaskService.clearRenderTask(task)),\n );\n } else {\n return getContentFile<Attributes>(\n contentFiles,\n '',\n param.customFilename,\n fallback,\n renderTaskService,\n contentRenderer,\n locale,\n ).pipe(tap(() => renderTaskService.clearRenderTask(task)));\n }\n}\n","import { Provider, InjectionToken } from '@angular/core';\nimport { ContentRenderer, NoopContentRenderer } from './content-renderer';\nimport { RenderTaskService } from './render-task.service';\nimport { withContentFileLoader } from './content-file-loader';\nimport { withContentListLoader } from './content-list-loader';\n\nexport interface MarkdownRendererOptions {\n loadMermaid?: () => Promise<typeof import('mermaid')>;\n}\n\nconst CONTENT_RENDERER_PROVIDERS: Provider[] = [\n {\n provide: ContentRenderer,\n useClass: NoopContentRenderer,\n },\n withContentFileLoader(),\n withContentListLoader(),\n];\n\nexport function withMarkdownRenderer(\n options?: MarkdownRendererOptions,\n): Provider {\n return [\n CONTENT_RENDERER_PROVIDERS,\n options?.loadMermaid\n ? [\n {\n provide: MERMAID_IMPORT_TOKEN,\n useFactory: options.loadMermaid,\n },\n ]\n : [],\n ];\n}\n\nexport function provideContent(...features: Provider[]): Provider[] {\n return [\n { provide: RenderTaskService, useClass: RenderTaskService },\n ...features,\n ];\n}\n\nexport const MERMAID_IMPORT_TOKEN: InjectionToken<\n Promise<typeof import('mermaid')>\n> = new InjectionToken<Promise<typeof import('mermaid')>>('mermaid_import');\n","import {\n AfterViewChecked,\n Component,\n inject,\n Input,\n ViewEncapsulation,\n} from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { ActivatedRoute } from '@angular/router';\n\nimport { ContentRenderer } from './content-renderer';\nimport { AnchorNavigationDirective } from './anchor-navigation.directive';\n\n@Component({\n selector: 'analog-markdown-route',\n standalone: true,\n imports: [],\n hostDirectives: [AnchorNavigationDirective],\n preserveWhitespaces: true,\n encapsulation: ViewEncapsulation.None,\n template: `<div [innerHTML]=\"content\" [class]=\"classes\"></div>`,\n})\nexport default class AnalogMarkdownRouteComponent implements AfterViewChecked {\n private sanitizer = inject(DomSanitizer);\n private route = inject(ActivatedRoute);\n contentRenderer: ContentRenderer = inject(ContentRenderer);\n\n protected content: SafeHtml = this.sanitizer.bypassSecurityTrustHtml(\n this.route.snapshot.data['renderedAnalogContent'],\n );\n\n @Input() classes = 'analog-markdown-route';\n\n ngAfterViewChecked(): void {\n this.contentRenderer.enhance();\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n AfterViewChecked,\n Component,\n InputSignal,\n NgZone,\n PLATFORM_ID,\n Signal,\n ViewEncapsulation,\n computed,\n inject,\n input,\n} from '@angular/core';\nimport { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { ActivatedRoute, Data } from '@angular/router';\nimport { from, Observable, of } from 'rxjs';\nimport { catchError, map, switchMap } from 'rxjs/operators';\n\nimport { AnchorNavigationDirective } from './anchor-navigation.directive';\nimport { ContentRenderer } from './content-renderer';\nimport { MERMAID_IMPORT_TOKEN } from './provide-content';\n\n@Component({\n selector: 'analog-markdown',\n standalone: true,\n hostDirectives: [AnchorNavigationDirective],\n preserveWhitespaces: true,\n encapsulation: ViewEncapsulation.None,\n template: ` <div [innerHTML]=\"htmlContent()\" [class]=\"classes()\"></div> `,\n})\nexport default class AnalogMarkdownComponent implements AfterViewChecked {\n private sanitizer = inject(DomSanitizer);\n private route = inject(ActivatedRoute);\n private zone = inject(NgZone);\n private readonly platformId = inject(PLATFORM_ID);\n private readonly mermaidImport = inject(MERMAID_IMPORT_TOKEN, {\n optional: true,\n });\n private mermaid: typeof import('mermaid') | undefined;\n\n private contentSource: Signal<SafeHtml | string | undefined> = toSignal(\n this.getContentSource(),\n );\n readonly htmlContent: Signal<SafeHtml | string | undefined> = computed(() => {\n const inputContent = this.content();\n\n if (inputContent) {\n return this.sanitizer.bypassSecurityTrustHtml(inputContent as string);\n }\n\n return this.contentSource();\n });\n readonly content: InputSignal<string | object | null | undefined> = input<\n string | object | null\n >();\n readonly classes: InputSignal<string> = input('analog-markdown');\n\n contentRenderer: ContentRenderer = inject(ContentRenderer);\n\n constructor() {\n if (isPlatformBrowser(this.platformId) && this.mermaidImport) {\n // Mermaid can only be loaded on client side\n this.loadMermaid(this.mermaidImport);\n }\n }\n\n getContentSource(): Observable<SafeHtml | string> {\n return this.route.data.pipe(\n map<Data, string>((data) => data['_analogContent'] ?? ''),\n switchMap((contentString) => this.renderContent(contentString)),\n map((content) => this.sanitizer.bypassSecurityTrustHtml(content)),\n catchError((e) => of(`There was an error ${e}`)),\n );\n }\n\n async renderContent(content: string): Promise<string> {\n const rendered = await this.contentRenderer.render(content);\n return rendered.content;\n }\n\n ngAfterViewChecked(): void {\n this.contentRenderer.enhance();\n this.zone.runOutsideAngular(() => this.mermaid?.default.run());\n }\n\n private loadMermaid(mermaidImport: Promise<typeof import('mermaid')>) {\n this.zone.runOutsideAngular(() =>\n // Wrap into an observable to avoid redundant initialization once\n // the markdown component is destroyed before the promise is resolved.\n from(mermaidImport)\n .pipe(takeUntilDestroyed())\n .subscribe((mermaid) => {\n this.mermaid = mermaid;\n this.mermaid.default.initialize({ startOnLoad: false });\n // Explicitly running mermaid as ngAfterViewChecked\n // has probably already been called\n this.mermaid?.default.run();\n }),\n );\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@analogjs/content",
3
- "version": "3.0.0-beta.40",
3
+ "version": "3.0.0-beta.42",
4
4
  "description": "Content Rendering for Analog",
5
5
  "type": "module",
6
6
  "author": "Brandon Roberts <robertsbt@gmail.com>",
@@ -34,7 +34,7 @@
34
34
  "satori": "^0.10.14",
35
35
  "satori-html": "^0.3.2",
36
36
  "sharp": "^0.34.5",
37
- "md4x": ">=0.0.28"
37
+ "md4x": ">=0.0.29"
38
38
  },
39
39
  "peerDependenciesMeta": {
40
40
  "@nx/devkit": {
@@ -91,8 +91,8 @@
91
91
  "tslib": "^2.3.0"
92
92
  },
93
93
  "devDependencies": {
94
- "@analogjs/vite-plugin-angular": "3.0.0-beta.40",
95
- "@analogjs/vitest-angular": "3.0.0-beta.40",
94
+ "@analogjs/vite-plugin-angular": "3.0.0-beta.42",
95
+ "@analogjs/vitest-angular": "3.0.0-beta.42",
96
96
  "@nx/devkit": "24.0.0"
97
97
  },
98
98
  "ng-update": {
@@ -1,9 +1,10 @@
1
1
  import * as i0 from "@angular/core";
2
2
  export declare class AnchorNavigationDirective {
3
3
  private readonly document;
4
+ private readonly host;
4
5
  private readonly location;
5
6
  private readonly router;
6
- handleNavigation(element: EventTarget | null): boolean;
7
+ handleNavigation(target: EventTarget | null, event?: MouseEvent): boolean;
7
8
  static ɵfac: i0.ɵɵFactoryDeclaration<AnchorNavigationDirective, never>;
8
9
  static ɵdir: i0.ɵɵDirectiveDeclaration<AnchorNavigationDirective, "[analogAnchorNavigation]", never, {}, {}, never, never, true, never>;
9
10
  }