@analogjs/content 3.0.0-alpha.88 → 3.0.0-alpha.89

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.
@@ -69,7 +69,7 @@ var Md4xContentRendererService = class Md4xContentRendererService extends Conten
69
69
  static {
70
70
  this.ɵfac = i0.ɵɵngDeclareFactory({
71
71
  minVersion: "12.0.0",
72
- version: "22.0.0",
72
+ version: "22.1.5",
73
73
  ngImport: i0,
74
74
  type: Md4xContentRendererService,
75
75
  deps: null,
@@ -79,7 +79,7 @@ var Md4xContentRendererService = class Md4xContentRendererService extends Conten
79
79
  static {
80
80
  this.ɵprov = i0.ɵɵngDeclareInjectable({
81
81
  minVersion: "12.0.0",
82
- version: "22.0.0",
82
+ version: "22.1.5",
83
83
  ngImport: i0,
84
84
  type: Md4xContentRendererService
85
85
  });
@@ -87,7 +87,7 @@ var Md4xContentRendererService = class Md4xContentRendererService extends Conten
87
87
  };
88
88
  i0.ɵɵngDeclareClassMetadata({
89
89
  minVersion: "12.0.0",
90
- version: "22.0.0",
90
+ version: "22.1.5",
91
91
  ngImport: i0,
92
92
  type: Md4xContentRendererService,
93
93
  decorators: [{ type: Injectable }]
@@ -162,7 +162,7 @@ var Md4xWasmContentRendererService = class Md4xWasmContentRendererService extend
162
162
  static {
163
163
  this.ɵfac = i0.ɵɵngDeclareFactory({
164
164
  minVersion: "12.0.0",
165
- version: "22.0.0",
165
+ version: "22.1.5",
166
166
  ngImport: i0,
167
167
  type: Md4xWasmContentRendererService,
168
168
  deps: null,
@@ -172,7 +172,7 @@ var Md4xWasmContentRendererService = class Md4xWasmContentRendererService extend
172
172
  static {
173
173
  this.ɵprov = i0.ɵɵngDeclareInjectable({
174
174
  minVersion: "12.0.0",
175
- version: "22.0.0",
175
+ version: "22.1.5",
176
176
  ngImport: i0,
177
177
  type: Md4xWasmContentRendererService
178
178
  });
@@ -180,7 +180,7 @@ var Md4xWasmContentRendererService = class Md4xWasmContentRendererService extend
180
180
  };
181
181
  i0.ɵɵngDeclareClassMetadata({
182
182
  minVersion: "12.0.0",
183
- version: "22.0.0",
183
+ version: "22.1.5",
184
184
  ngImport: i0,
185
185
  type: Md4xWasmContentRendererService,
186
186
  decorators: [{ type: Injectable }]
@@ -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 TableOfContentItem,\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\nfunction makeSlug(text: string, slugCounts: Map<string, number>): string {\n const baseSlug = text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .trim()\n .replace(/\\s+/g, '-');\n const count = slugCounts.get(baseSlug) ?? 0;\n slugCounts.set(baseSlug, count + 1);\n return count === 0 ? baseSlug : `${baseSlug}-${count}`;\n}\n\nfunction headingsToToc(\n headings: Array<{ level: number; text: string }>,\n): TableOfContentItem[] {\n const slugCounts = new Map<string, number>();\n return headings.map((h) => ({\n id: makeSlug(h.text, slugCounts),\n level: h.level,\n text: h.text,\n }));\n}\n\nfunction injectHeadingIds(html: string, toc: TableOfContentItem[]): string {\n let tocIndex = 0;\n return html.replace(/<h([1-6])>/g, (match, level) => {\n const item = toc[tocIndex++];\n return item ? `<h${level} id=\"${item.id}\">` : match;\n });\n}\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 });\n const meta = parseMeta(content);\n const toc = headingsToToc(meta.headings);\n return {\n content: injectHeadingIds(html, toc),\n toc,\n };\n }\n\n override getContentHeadings(content: string): TableOfContentItem[] {\n // Synchronous fallback — md4x is async-imported so we use regex extraction\n // matching NoopContentRenderer's algorithm for consistency.\n const lines = content.split('\\n');\n const toc: TableOfContentItem[] = [];\n const slugCounts = new Map<string, number>();\n\n for (const line of lines) {\n const match = /^(#{1,6})\\s+(.+?)\\s*$/.exec(line);\n if (!match) continue;\n const level = match[1].length;\n const text = match[2].trim();\n if (!text) continue;\n toc.push({ id: makeSlug(text, slugCounts), level, text });\n }\n\n return toc;\n }\n}\n","import { Injectable, inject } from '@angular/core';\n\nimport {\n ContentRenderer,\n RenderedContent,\n TableOfContentItem,\n} from '../../../src/lib/content-renderer';\nimport {\n MD4X_RENDERER_OPTIONS,\n Md4xRendererOptions,\n} from './md4x-content-renderer.service';\n\nfunction makeSlug(text: string, slugCounts: Map<string, number>): string {\n const baseSlug = text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .trim()\n .replace(/\\s+/g, '-');\n const count = slugCounts.get(baseSlug) ?? 0;\n slugCounts.set(baseSlug, count + 1);\n return count === 0 ? baseSlug : `${baseSlug}-${count}`;\n}\n\nfunction headingsToToc(\n headings: Array<{ level: number; text: string }>,\n): TableOfContentItem[] {\n const slugCounts = new Map<string, number>();\n return headings.map((h) => ({\n id: makeSlug(h.text, slugCounts),\n level: h.level,\n text: h.text,\n }));\n}\n\nfunction injectHeadingIds(html: string, toc: TableOfContentItem[]): string {\n let tocIndex = 0;\n return html.replace(/<h([1-6])>/g, (match, level) => {\n const item = toc[tocIndex++];\n return item ? `<h${level} id=\"${item.id}\">` : match;\n });\n}\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 });\n const meta = wasm.parseMeta(content);\n const toc = headingsToToc(meta.headings);\n return {\n content: injectHeadingIds(html, toc),\n toc,\n };\n }\n\n override getContentHeadings(content: string): TableOfContentItem[] {\n const lines = content.split('\\n');\n const toc: TableOfContentItem[] = [];\n const slugCounts = new Map<string, number>();\n\n for (const line of lines) {\n const match = /^(#{1,6})\\s+(.+?)\\s*$/.exec(line);\n if (!match) continue;\n const level = match[1].length;\n const text = match[2].trim();\n if (!text) continue;\n toc.push({ id: makeSlug(text, slugCounts), level, text });\n }\n\n return toc;\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 const reader = input.getReader();\n\n return new ReadableStream<string>({\n async pull(controller) {\n try {\n const { done, value } = await reader.read();\n\n if (done) {\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 }\n },\n cancel() {\n reader.cancel();\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 TableOfContentItem,\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\nfunction makeSlug(text: string, slugCounts: Map<string, number>): string {\n const baseSlug = text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .trim()\n .replace(/\\s+/g, '-');\n const count = slugCounts.get(baseSlug) ?? 0;\n slugCounts.set(baseSlug, count + 1);\n return count === 0 ? baseSlug : `${baseSlug}-${count}`;\n}\n\nfunction headingsToToc(\n headings: Array<{ level: number; text: string }>,\n): TableOfContentItem[] {\n const slugCounts = new Map<string, number>();\n return headings.map((h) => ({\n id: makeSlug(h.text, slugCounts),\n level: h.level,\n text: h.text,\n }));\n}\n\nfunction injectHeadingIds(html: string, toc: TableOfContentItem[]): string {\n let tocIndex = 0;\n return html.replace(/<h([1-6])>/g, (match, level) => {\n const item = toc[tocIndex++];\n return item ? `<h${level} id=\"${item.id}\">` : match;\n });\n}\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 });\n const meta = parseMeta(content);\n const toc = headingsToToc(meta.headings);\n return {\n content: injectHeadingIds(html, toc),\n toc,\n };\n }\n\n override getContentHeadings(content: string): TableOfContentItem[] {\n // Synchronous fallback — md4x is async-imported so we use regex extraction\n // matching NoopContentRenderer's algorithm for consistency.\n const lines = content.split('\\n');\n const toc: TableOfContentItem[] = [];\n const slugCounts = new Map<string, number>();\n\n for (const line of lines) {\n const match = /^(#{1,6})\\s+(.+?)\\s*$/.exec(line);\n if (!match) continue;\n const level = match[1].length;\n const text = match[2].trim();\n if (!text) continue;\n toc.push({ id: makeSlug(text, slugCounts), level, text });\n }\n\n return toc;\n }\n}\n","import { Injectable, inject } from '@angular/core';\n\nimport {\n ContentRenderer,\n RenderedContent,\n TableOfContentItem,\n} from '../../../src/lib/content-renderer';\nimport {\n MD4X_RENDERER_OPTIONS,\n Md4xRendererOptions,\n} from './md4x-content-renderer.service';\n\nfunction makeSlug(text: string, slugCounts: Map<string, number>): string {\n const baseSlug = text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .trim()\n .replace(/\\s+/g, '-');\n const count = slugCounts.get(baseSlug) ?? 0;\n slugCounts.set(baseSlug, count + 1);\n return count === 0 ? baseSlug : `${baseSlug}-${count}`;\n}\n\nfunction headingsToToc(\n headings: Array<{ level: number; text: string }>,\n): TableOfContentItem[] {\n const slugCounts = new Map<string, number>();\n return headings.map((h) => ({\n id: makeSlug(h.text, slugCounts),\n level: h.level,\n text: h.text,\n }));\n}\n\nfunction injectHeadingIds(html: string, toc: TableOfContentItem[]): string {\n let tocIndex = 0;\n return html.replace(/<h([1-6])>/g, (match, level) => {\n const item = toc[tocIndex++];\n return item ? `<h${level} id=\"${item.id}\">` : match;\n });\n}\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 });\n const meta = wasm.parseMeta(content);\n const toc = headingsToToc(meta.headings);\n return {\n content: injectHeadingIds(html, toc),\n toc,\n };\n }\n\n override getContentHeadings(content: string): TableOfContentItem[] {\n const lines = content.split('\\n');\n const toc: TableOfContentItem[] = [];\n const slugCounts = new Map<string, number>();\n\n for (const line of lines) {\n const match = /^(#{1,6})\\s+(.+?)\\s*$/.exec(line);\n if (!match) continue;\n const level = match[1].length;\n const text = match[2].trim();\n if (!text) continue;\n toc.push({ id: makeSlug(text, slugCounts), level, text });\n }\n\n return toc;\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 const reader = input.getReader();\n\n return new ReadableStream<string>({\n async pull(controller) {\n try {\n const { done, value } = await reader.read();\n\n if (done) {\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 }\n },\n cancel() {\n reader.cancel();\n },\n });\n}\n"],"mappings":"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"}
@@ -117,7 +117,7 @@ var MdcRendererDirective = class MdcRendererDirective {
117
117
  static {
118
118
  this.ɵfac = i0.ɵɵngDeclareFactory({
119
119
  minVersion: "12.0.0",
120
- version: "22.0.0",
120
+ version: "22.1.5",
121
121
  ngImport: i0,
122
122
  type: MdcRendererDirective,
123
123
  deps: [],
@@ -127,7 +127,7 @@ var MdcRendererDirective = class MdcRendererDirective {
127
127
  static {
128
128
  this.ɵdir = i0.ɵɵngDeclareDirective({
129
129
  minVersion: "17.1.0",
130
- version: "22.0.0",
130
+ version: "22.1.5",
131
131
  type: MdcRendererDirective,
132
132
  isStandalone: true,
133
133
  selector: "[mdcAst]",
@@ -144,7 +144,7 @@ var MdcRendererDirective = class MdcRendererDirective {
144
144
  };
145
145
  i0.ɵɵngDeclareClassMetadata({
146
146
  minVersion: "12.0.0",
147
- version: "22.0.0",
147
+ version: "22.1.5",
148
148
  ngImport: i0,
149
149
  type: MdcRendererDirective,
150
150
  decorators: [{
@@ -1 +1 @@
1
- {"version":3,"file":"analogjs-content-mdc.mjs","names":[],"sources":["../../mdc/src/lib/mdc-component-registry.ts","../../mdc/src/lib/mdc-renderer.directive.ts"],"sourcesContent":["import { InjectionToken, Type, Provider } from '@angular/core';\n\n/**\n * Registry mapping MDC component names to lazy-loaded Angular components.\n *\n * @experimental MDC component support is experimental and may change in future releases.\n */\nexport const MDC_COMPONENTS: InjectionToken<\n Map<string, () => Promise<Type<unknown>>>\n> = new InjectionToken('mdc_components');\n\n/**\n * Provides a registry of Angular components that can be used in MDC\n * (Markdown Components) syntax within markdown content.\n *\n * @experimental MDC component support is experimental and may change in future releases.\n *\n * @example\n * ```typescript\n * provideContent(\n * withMd4xRenderer(),\n * withMdcComponents({\n * alert: () => import('./components/alert.component').then(m => m.AlertComponent),\n * card: () => import('./components/card.component').then(m => m.CardComponent),\n * }),\n * );\n * ```\n */\nexport function withMdcComponents(\n components: Record<string, () => Promise<Type<unknown>>>,\n): Provider {\n return {\n provide: MDC_COMPONENTS,\n useValue: new Map(Object.entries(components)),\n };\n}\n","import {\n Directive,\n ElementRef,\n effect,\n inject,\n input,\n InputSignal,\n Renderer2,\n ViewContainerRef,\n} from '@angular/core';\n\nimport { MDC_COMPONENTS } from './mdc-component-registry';\n\ntype ComarkNode =\n | string\n | [string | null, Record<string, unknown>, ...ComarkNode[]];\n\n/**\n * Directive that renders MDC (Markdown Components) AST nodes as Angular components.\n *\n * Walks the ComarkTree AST from md4x's `parseAST()`, matches component nodes\n * to the registered MDC_COMPONENTS map, and instantiates them via\n * `ViewContainerRef.createComponent()` with MDC attributes bound as inputs.\n *\n * @experimental MDC component support is experimental and may change in future releases.\n *\n * @example\n * ```html\n * <div [mdcAst]=\"parsedAst\"></div>\n * ```\n */\n@Directive({\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: '[mdcAst]',\n standalone: true,\n})\nexport class MdcRendererDirective {\n readonly ast: InputSignal<{ nodes: ComarkNode[] } | null> = input<{\n nodes: ComarkNode[];\n } | null>(null, {\n alias: 'mdcAst',\n });\n\n private readonly viewContainer = inject(ViewContainerRef);\n private readonly renderer = inject(Renderer2);\n private readonly el = inject(ElementRef);\n private readonly components = inject(MDC_COMPONENTS, { optional: true });\n private renderId = 0;\n\n constructor() {\n effect(() => {\n const ast = this.ast();\n const currentRenderId = ++this.renderId;\n\n this.viewContainer.clear();\n const host = this.el.nativeElement as HTMLElement;\n host.innerHTML = '';\n\n if (!ast?.nodes) return;\n\n // Fire-and-forget: Angular doesn't await effects, so we schedule\n // the async rendering and let it complete in the background.\n void this.renderNodes(ast.nodes, host, currentRenderId);\n });\n }\n\n private async renderNodes(\n nodes: ComarkNode[],\n parent: HTMLElement,\n renderId: number,\n ): Promise<void> {\n for (const node of nodes) {\n if (this.renderId !== renderId) return;\n await this.renderNode(node, parent, renderId);\n }\n }\n\n private async renderNode(\n node: ComarkNode,\n parent: HTMLElement,\n renderId: number,\n ): Promise<void> {\n if (this.renderId !== renderId) return;\n\n if (typeof node === 'string') {\n const text = this.renderer.createText(node);\n this.renderer.appendChild(parent, text);\n return;\n }\n\n const [tag, props, ...children] = node;\n\n if (!tag) {\n for (const child of children) {\n if (this.renderId !== renderId) return;\n await this.renderNode(child, parent, renderId);\n }\n return;\n }\n\n // Check if this tag is a registered MDC component\n const componentLoader = this.components?.get(tag);\n if (componentLoader) {\n try {\n const componentType = await componentLoader();\n if (this.renderId !== renderId) return;\n\n const tempContainer = this.renderer.createElement('div');\n for (const child of children) {\n if (this.renderId !== renderId) return;\n await this.renderNode(child, tempContainer, renderId);\n }\n if (this.renderId !== renderId) return;\n\n const projectableNodes = [\n Array.from((tempContainer as HTMLElement).childNodes),\n ];\n const componentRef = this.viewContainer.createComponent(componentType, {\n projectableNodes,\n });\n\n // Bind MDC attributes as component inputs\n for (const [key, value] of Object.entries(props)) {\n componentRef.setInput(key, value);\n }\n\n const componentEl = componentRef.location.nativeElement as HTMLElement;\n this.renderer.appendChild(parent, componentEl);\n } catch (e) {\n console.error(`[MdcRenderer] Failed to load component \"${tag}\":`, e);\n }\n return;\n }\n\n // Fall back to rendering as a standard HTML element\n const el = this.renderer.createElement(tag);\n for (const [key, value] of Object.entries(props)) {\n this.renderer.setAttribute(el, key, String(value));\n }\n for (const child of children) {\n if (this.renderId !== renderId) return;\n await this.renderNode(child, el, renderId);\n }\n this.renderer.appendChild(parent, el);\n }\n}\n"],"mappings":";;;;;;;;AAOA,IAAa,iBAET,IAAI,eAAe,iBAAiB;;;;;;;;;;;;;;;;;;AAmBxC,SAAgB,kBACd,YACU;AACV,QAAO;EACL,SAAS;EACT,UAAc,IAAI,IAAA,OAAO,QAAQ,WAAW,CAAA;EAC7C;;;;;;;;;;;;;;;;;;ACsBG,IAAK,uBAAL,MAAK,qBAAY;CAEjB,cAAU;AAIL,OAAK,MAAA,MAAY,MAAI,EAA6B,OAAA,UAAA,CAAA;AACvD,OAAA,gBAAA,OAAA,iBAAA;;AAGU,OAAA,KAAA,OACZ,WAEA;AAEK,OAAM,aAAQ,OAAO,gBAAA,EAAA,UAAA,MAAA,CAAA;AACpB,OAAK,WAAA;AACT,eAAW;;;AAID,QAAA,cAEZ,OACA;GAES,MAAA,OAAa,KAAA,GAAU;AAE5B,QAAO,YAAS;AACZ,OAAO,CAAA,KAAK,MACb;AAIK,QAAO,YAAG,IAAY,OAAA,MAAA,gBAAA;IAE7B;;CAED,MAAI,YAAK,OAAa,QAAU,UAAA;AAChC,OAAM,MAAK,QAAW,OAAO;kCAE/B;;;;CAMA,MAAI,WAAA,MAAA,QAAA,UAAA;AACF,MAAM,KAAA,aAAgB,SAClB;AAEJ,MAAM,OAAA,SAAgB,UAAK;GACtB,MAAM,OAAS,KAAA,SAAU,WAAA,KAAA;AACxB,QAAK,SAAA,YAAuB,QAAA,KAAA;AAC1B;;EAEJ,MAAK,CAAA,KAAA,OAAa,GAAA,YAAU;AAEhC,MAAM,CAAA,KAAA;AAGA,QAAA,MAAA,SAAoB,UAAA;AAKd,QAAA,KAAK,aAAiB,SACnB;;;AAIV;;;AAIP,MAAA,iBAAA;;IAIS,MAAK,gBAAS,MAAc,iBAAI;AAC/B,QAAK,KAAA,aAAiB,SAClB;;AAEL,SAAA,MAAS,SAAU,UAAA;AACnB,SAAA,KAAa,aAAU,SACrB;;;;6BA9GhB,CAEW,MAAA,KAAA,cAAA,WAAA,CACE;IACZ,MAAA,eAAA,KAAA,cAAA,gBAAA,eAAA,EAAA,kBAAA,CAAA"}
1
+ {"version":3,"file":"analogjs-content-mdc.mjs","names":[],"sources":["../../mdc/src/lib/mdc-component-registry.ts","../../mdc/src/lib/mdc-renderer.directive.ts"],"sourcesContent":["import { InjectionToken, Type, Provider } from '@angular/core';\n\n/**\n * Registry mapping MDC component names to lazy-loaded Angular components.\n *\n * @experimental MDC component support is experimental and may change in future releases.\n */\nexport const MDC_COMPONENTS: InjectionToken<\n Map<string, () => Promise<Type<unknown>>>\n> = new InjectionToken('mdc_components');\n\n/**\n * Provides a registry of Angular components that can be used in MDC\n * (Markdown Components) syntax within markdown content.\n *\n * @experimental MDC component support is experimental and may change in future releases.\n *\n * @example\n * ```typescript\n * provideContent(\n * withMd4xRenderer(),\n * withMdcComponents({\n * alert: () => import('./components/alert.component').then(m => m.AlertComponent),\n * card: () => import('./components/card.component').then(m => m.CardComponent),\n * }),\n * );\n * ```\n */\nexport function withMdcComponents(\n components: Record<string, () => Promise<Type<unknown>>>,\n): Provider {\n return {\n provide: MDC_COMPONENTS,\n useValue: new Map(Object.entries(components)),\n };\n}\n","import {\n Directive,\n ElementRef,\n effect,\n inject,\n input,\n InputSignal,\n Renderer2,\n ViewContainerRef,\n} from '@angular/core';\n\nimport { MDC_COMPONENTS } from './mdc-component-registry';\n\ntype ComarkNode =\n | string\n | [string | null, Record<string, unknown>, ...ComarkNode[]];\n\n/**\n * Directive that renders MDC (Markdown Components) AST nodes as Angular components.\n *\n * Walks the ComarkTree AST from md4x's `parseAST()`, matches component nodes\n * to the registered MDC_COMPONENTS map, and instantiates them via\n * `ViewContainerRef.createComponent()` with MDC attributes bound as inputs.\n *\n * @experimental MDC component support is experimental and may change in future releases.\n *\n * @example\n * ```html\n * <div [mdcAst]=\"parsedAst\"></div>\n * ```\n */\n@Directive({\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: '[mdcAst]',\n standalone: true,\n})\nexport class MdcRendererDirective {\n readonly ast: InputSignal<{ nodes: ComarkNode[] } | null> = input<{\n nodes: ComarkNode[];\n } | null>(null, {\n alias: 'mdcAst',\n });\n\n private readonly viewContainer = inject(ViewContainerRef);\n private readonly renderer = inject(Renderer2);\n private readonly el = inject(ElementRef);\n private readonly components = inject(MDC_COMPONENTS, { optional: true });\n private renderId = 0;\n\n constructor() {\n effect(() => {\n const ast = this.ast();\n const currentRenderId = ++this.renderId;\n\n this.viewContainer.clear();\n const host = this.el.nativeElement as HTMLElement;\n host.innerHTML = '';\n\n if (!ast?.nodes) return;\n\n // Fire-and-forget: Angular doesn't await effects, so we schedule\n // the async rendering and let it complete in the background.\n void this.renderNodes(ast.nodes, host, currentRenderId);\n });\n }\n\n private async renderNodes(\n nodes: ComarkNode[],\n parent: HTMLElement,\n renderId: number,\n ): Promise<void> {\n for (const node of nodes) {\n if (this.renderId !== renderId) return;\n await this.renderNode(node, parent, renderId);\n }\n }\n\n private async renderNode(\n node: ComarkNode,\n parent: HTMLElement,\n renderId: number,\n ): Promise<void> {\n if (this.renderId !== renderId) return;\n\n if (typeof node === 'string') {\n const text = this.renderer.createText(node);\n this.renderer.appendChild(parent, text);\n return;\n }\n\n const [tag, props, ...children] = node;\n\n if (!tag) {\n for (const child of children) {\n if (this.renderId !== renderId) return;\n await this.renderNode(child, parent, renderId);\n }\n return;\n }\n\n // Check if this tag is a registered MDC component\n const componentLoader = this.components?.get(tag);\n if (componentLoader) {\n try {\n const componentType = await componentLoader();\n if (this.renderId !== renderId) return;\n\n const tempContainer = this.renderer.createElement('div');\n for (const child of children) {\n if (this.renderId !== renderId) return;\n await this.renderNode(child, tempContainer, renderId);\n }\n if (this.renderId !== renderId) return;\n\n const projectableNodes = [\n Array.from((tempContainer as HTMLElement).childNodes),\n ];\n const componentRef = this.viewContainer.createComponent(componentType, {\n projectableNodes,\n });\n\n // Bind MDC attributes as component inputs\n for (const [key, value] of Object.entries(props)) {\n componentRef.setInput(key, value);\n }\n\n const componentEl = componentRef.location.nativeElement as HTMLElement;\n this.renderer.appendChild(parent, componentEl);\n } catch (e) {\n console.error(`[MdcRenderer] Failed to load component \"${tag}\":`, e);\n }\n return;\n }\n\n // Fall back to rendering as a standard HTML element\n const el = this.renderer.createElement(tag);\n for (const [key, value] of Object.entries(props)) {\n this.renderer.setAttribute(el, key, String(value));\n }\n for (const child of children) {\n if (this.renderId !== renderId) return;\n await this.renderNode(child, el, renderId);\n }\n this.renderer.appendChild(parent, el);\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"analogjs-content-og.mjs","names":[],"sources":["../../og/src/lib/og.ts"],"sourcesContent":["// Credit for modified source: https://github.com/etherCorps/sveltekit-og/blob/main/src/lib/api.ts\n\nimport satori from 'satori';\nimport { html as toReactElement } from 'satori-html';\nimport sharp from 'sharp';\n\nimport { ImageResponseOptions } from './options';\n\nexport const generateImage = async (\n element: string,\n options: ImageResponseOptions,\n): Promise<Buffer> => {\n const elementHtml = toReactElement(element);\n const svg = await satori(elementHtml as any, {\n width: options.width || 1200,\n height: options.height || 630,\n fonts: options.fonts?.length ? options.fonts : [],\n tailwindConfig: options.tailwindConfig,\n });\n const svgBuffer = Buffer.from(svg);\n const png = sharp(svgBuffer).png().toBuffer();\n\n const pngBuffer = await png;\n\n return pngBuffer;\n};\n\nexport class ImageResponse extends Response {\n constructor(element: string, options: ImageResponseOptions = {}) {\n super();\n\n const body = new ReadableStream({\n async start(controller) {\n const buffer = await generateImage(element, options);\n controller.enqueue(buffer);\n controller.close();\n },\n });\n\n return new Response(body, {\n headers: {\n 'Content-Type': 'image/png',\n 'Cache-Control': options.debug\n ? 'no-cache, no-store'\n : 'public, immutable, no-transform, max-age=31536000',\n ...options.headers,\n },\n status: options.status || 200,\n statusText: options.statusText,\n });\n }\n}\n"],"mappings":";;;;IAYQ,gBAAc,OAAe,SAAQ,YAAA;CAEzC,MAAO,MAAA,MAAQ,OADC,KAA2B,QAAA,EACnB;EACxB,OAAQ,QAAQ,SAAU;EAC1B,QAAO,QAAe,UAAS;EAC/B,OAAA,QAAgB,OAAQ,SAAA,QAAA,QAAA,EAAA;EACxB,gBAAA,QAAA;EACF,CAAA;QAKO,MAFD,MAFY,OAAU,KAAC,IAAM,CAEX,CAAA,KAAA,CAAA,UAAA;;IAMZ,gBAAA,cAAmD,SAAE;CAC/D,YAAO,SAAA,UAAA,EAAA,EAAA;AAED,SAAO;EAET,MAAM,OAAS,IAAM,eAAc,EACnC,MAAW,MAAQ,YAAO;GACf,MAAO,SAAA,MAAA,cAAA,SAAA,QAAA;AAEpB,cAAA,QAAA,OAAA;AAES,cAAS,OAAM;KAEtB,CAAA;AACA,SAAA,IAAA,SAAiB,MAAQ;GAGtB,SAAQ;IACZ,gBAAA;IACO,iBAAkB,QAAA,QACd,uBACZ"}
1
+ {"version":3,"file":"analogjs-content-og.mjs","names":[],"sources":["../../og/src/lib/og.ts"],"sourcesContent":["// Credit for modified source: https://github.com/etherCorps/sveltekit-og/blob/main/src/lib/api.ts\n\nimport satori from 'satori';\nimport { html as toReactElement } from 'satori-html';\nimport sharp from 'sharp';\n\nimport { ImageResponseOptions } from './options';\n\nexport const generateImage = async (\n element: string,\n options: ImageResponseOptions,\n): Promise<Buffer> => {\n const elementHtml = toReactElement(element);\n const svg = await satori(elementHtml as any, {\n width: options.width || 1200,\n height: options.height || 630,\n fonts: options.fonts?.length ? options.fonts : [],\n tailwindConfig: options.tailwindConfig,\n });\n const svgBuffer = Buffer.from(svg);\n const png = sharp(svgBuffer).png().toBuffer();\n\n const pngBuffer = await png;\n\n return pngBuffer;\n};\n\nexport class ImageResponse extends Response {\n constructor(element: string, options: ImageResponseOptions = {}) {\n super();\n\n const body = new ReadableStream({\n async start(controller) {\n const buffer = await generateImage(element, options);\n controller.enqueue(buffer);\n controller.close();\n },\n });\n\n return new Response(body, {\n headers: {\n 'Content-Type': 'image/png',\n 'Cache-Control': options.debug\n ? 'no-cache, no-store'\n : 'public, immutable, no-transform, max-age=31536000',\n ...options.headers,\n },\n status: options.status || 200,\n statusText: options.statusText,\n });\n }\n}\n"],"mappings":";;;;AAQA,IAAa,gBAAgB,OAC3B,SACA,YACmB;CAEnB,MAAM,MAAM,MAAM,OADE,KAAe,QAAQ,EACE;EAC3C,OAAO,QAAQ,SAAS;EACxB,QAAQ,QAAQ,UAAU;EAC1B,OAAO,QAAQ,OAAO,SAAS,QAAQ,QAAQ,EAAE;EACjD,gBAAgB,QAAQ;EACzB,CAAC;AAMF,QAFkB,MAFN,MADM,OAAO,KAAK,IAAI,CACN,CAAC,KAAK,CAAC,UAAU;;AAO/C,IAAa,gBAAb,cAAmC,SAAQ;CACzC,YAAY,SAAiB,UAAgC,EAAE,EAAA;AAC7D,SAAO;EAEP,MAAM,OAAO,IAAI,eAAe,EAC9B,MAAM,MAAM,YAAU;GACpB,MAAM,SAAS,MAAM,cAAc,SAAS,QAAQ;AACpD,cAAW,QAAQ,OAAO;AAC1B,cAAW,OAAO;KAErB,CAAC;AAEF,SAAO,IAAI,SAAS,MAAM;GACxB,SAAS;IACP,gBAAgB;IAChB,iBAAiB,QAAQ,QACrB,uBACA;IACJ,GAAG,QAAQ;IACZ;GACD,QAAQ,QAAQ,UAAU;GAC1B,YAAY,QAAQ;GACrB,CAAC"}
@@ -46,7 +46,7 @@ var PrismHighlighter = class PrismHighlighter extends MarkedContentHighlighter {
46
46
  static {
47
47
  this.ɵfac = i0.ɵɵngDeclareFactory({
48
48
  minVersion: "12.0.0",
49
- version: "22.0.0",
49
+ version: "22.1.5",
50
50
  ngImport: i0,
51
51
  type: PrismHighlighter,
52
52
  deps: null,
@@ -56,7 +56,7 @@ var PrismHighlighter = class PrismHighlighter extends MarkedContentHighlighter {
56
56
  static {
57
57
  this.ɵprov = i0.ɵɵngDeclareInjectable({
58
58
  minVersion: "12.0.0",
59
- version: "22.0.0",
59
+ version: "22.1.5",
60
60
  ngImport: i0,
61
61
  type: PrismHighlighter
62
62
  });
@@ -64,7 +64,7 @@ var PrismHighlighter = class PrismHighlighter extends MarkedContentHighlighter {
64
64
  };
65
65
  i0.ɵɵngDeclareClassMetadata({
66
66
  minVersion: "12.0.0",
67
- version: "22.0.0",
67
+ version: "22.1.5",
68
68
  ngImport: i0,
69
69
  type: PrismHighlighter,
70
70
  decorators: [{ type: Injectable }]
@@ -1 +1 @@
1
- {"version":3,"file":"analogjs-content-prism-highlighter.mjs","names":[],"sources":["../../prism-highlighter/src/lib/prism-highlighter.ts","../../prism-highlighter/src/lib/prism/angular.ts","../../prism-highlighter/src/index.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { markedHighlight } from 'marked-highlight';\n\nimport { MarkedContentHighlighter } from '../../../src/lib/marked-content-highlighter';\n\ndeclare const Prism: typeof import('prismjs');\n\ntype HighlightExtension = ReturnType<\n MarkedContentHighlighter['getHighlightExtension']\n>;\n\n@Injectable()\nexport class PrismHighlighter extends MarkedContentHighlighter {\n override augmentCodeBlock(code: string, lang: string): string {\n const classes =\n lang.startsWith('diff') && Prism.languages['diff']\n ? `language-${lang} diff-highlight`\n : `language-${lang.replace('diff-', '')}`;\n return `<pre class=\"${classes}\"><code class=\"${classes}\">${code}</code></pre>`;\n }\n\n override getHighlightExtension(): import('marked').MarkedExtension {\n return markedHighlight({\n async: true,\n highlight: (code: string, lang: string) => {\n let diff = lang?.startsWith('diff-');\n lang = diff ? lang.replace('diff-', '') : lang || 'typescript';\n\n if (diff && !Prism.languages['diff']) {\n diff = false;\n console.warn(`Notice:\n ---------------------------------------------------------------------------------------\n The \\`diff\\` language and plugin are not available in the provided setup.\n To enable it, add the following imports your \\`app.config.ts\\`:\n import 'prismjs/components/prism-diff';\n import 'prismjs/plugins/diff-highlight/prism-diff-highlight';\n ---------------------------------------------------------------------------------------\n `);\n }\n\n if (!Prism.languages[lang]) {\n if (lang !== 'mermaid') {\n console.warn(`Notice:\n ---------------------------------------------------------------------------------------\n The requested language '${lang}' is not available in the provided setup.\n To enable it, add the following import your \\`app.config.ts\\`:\n import 'prismjs/components/prism-${lang}';\n ---------------------------------------------------------------------------------------\n `);\n }\n return code;\n }\n return Prism.highlight(\n code,\n diff ? Prism.languages['diff'] : Prism.languages[lang],\n lang,\n );\n },\n }) as HighlightExtension;\n }\n}\n","import Prism from 'prismjs';\n\n(function () {\n if (typeof Prism === 'undefined') {\n return;\n }\n\n Prism.languages['angular'] = Prism.languages.extend('markup', {\n keyword:\n /(?:@if|@for|@switch|@defer|@loading|@error|@placeholder|prefetch)\\b/,\n operator: /\\b(?:on|when)\\b/,\n number: {\n pattern: /\\b(minimum|after)\\s+\\d+(?:s|ms|)/gi,\n lookbehind: true,\n },\n builtin: {\n pattern:\n /\\b(?:viewport|timer|minimum|after|hover|idle|immediate|interaction)/,\n },\n function:\n /#?(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\s*(?:\\.\\s*(?:apply|bind|call)\\s*)?\\()/,\n });\n\n Prism.languages['ng'] = Prism.languages['angular'];\n})();\n","import { Provider } from '@angular/core';\nimport { PrismHighlighter } from './lib/prism-highlighter';\n\nimport {\n ContentRenderer,\n NoopContentRenderer,\n} from '../../src/lib/content-renderer';\nimport 'prismjs';\nimport 'prismjs/plugins/toolbar/prism-toolbar';\nimport 'prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard';\nimport './lib/prism/angular';\n\nexport { PrismHighlighter };\n\nexport function withPrismHighlighter(): Provider[] {\n return [{ provide: ContentRenderer, useClass: NoopContentRenderer }];\n}\n"],"mappings":";;;;;;;;;AAYO,IAAA,mBAAA,MAAA,yBAAM,yBAAyB;CACpC,iBAA0B,MAAc,MAAsB;EACtD,MAAA,UACC,KAAA,WAAW,OAAW,IAAM,MAAA,UAAU,UAGtC,YAAe,KAAA,mBAAA,YAAA,KAAA,QAAA,SAAA,GAAA;AAGxB,SAAA,eAAmE,QAAA,iBAAA,QAAA,IAAA,KAAA;;CAE/D,wBAAO;AACP,SAAY,gBAA+B;GACrC,OAAO;GACJ,YAAY,MAAA,SAAQ;IAEf,IAAC,OAAM,MAAU,WAAS,QAAA;AAC7B,WAAA,OAAA,KAAA,QAAA,SAAA,GAAA,GAAA,QAAA;AACC,QAAK,QAAA,CAAA,MAAA,UAAA,SAAA;;;;;;;;;cAUV;;AAEO,QAAK,CAAA,MAAA,UAAA,OAAA;qCAEK,KAAK;;;;yCAInB,KAAA;;gBAEC;AAEI,YACX;;AAKJ,WAAA,MAAA,UAAA,MAAA,OAAA,MAAA,UAAA,UAAA,MAAA,UAAA,OAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CCxDL,WAAY;AACX,KAAI,OAAO,YAAU,YACnB;AAGF,SAAM,UAAU,aAAa,QAAM,UAAU,OAAO,UAAU;EAC5D,SACE;EACF,UAAU;EACV,QAAQ;GACG,SAAA;GACT,YAAY;GACb;EACD,SACE,EAIA,SAAA,uEACF;EAEI,UAAU;EACd,CAAA;;;;;ACVJ,SAAgB,uBAAmC;AACjD,QAAQ,CAAA;EAAA,SAAA;EAAA,UAAA;EAAA,CAAA"}
1
+ {"version":3,"file":"analogjs-content-prism-highlighter.mjs","names":[],"sources":["../../prism-highlighter/src/lib/prism-highlighter.ts","../../prism-highlighter/src/lib/prism/angular.ts","../../prism-highlighter/src/index.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { markedHighlight } from 'marked-highlight';\n\nimport { MarkedContentHighlighter } from '../../../src/lib/marked-content-highlighter';\n\ndeclare const Prism: typeof import('prismjs');\n\ntype HighlightExtension = ReturnType<\n MarkedContentHighlighter['getHighlightExtension']\n>;\n\n@Injectable()\nexport class PrismHighlighter extends MarkedContentHighlighter {\n override augmentCodeBlock(code: string, lang: string): string {\n const classes =\n lang.startsWith('diff') && Prism.languages['diff']\n ? `language-${lang} diff-highlight`\n : `language-${lang.replace('diff-', '')}`;\n return `<pre class=\"${classes}\"><code class=\"${classes}\">${code}</code></pre>`;\n }\n\n override getHighlightExtension(): import('marked').MarkedExtension {\n return markedHighlight({\n async: true,\n highlight: (code: string, lang: string) => {\n let diff = lang?.startsWith('diff-');\n lang = diff ? lang.replace('diff-', '') : lang || 'typescript';\n\n if (diff && !Prism.languages['diff']) {\n diff = false;\n console.warn(`Notice:\n ---------------------------------------------------------------------------------------\n The \\`diff\\` language and plugin are not available in the provided setup.\n To enable it, add the following imports your \\`app.config.ts\\`:\n import 'prismjs/components/prism-diff';\n import 'prismjs/plugins/diff-highlight/prism-diff-highlight';\n ---------------------------------------------------------------------------------------\n `);\n }\n\n if (!Prism.languages[lang]) {\n if (lang !== 'mermaid') {\n console.warn(`Notice:\n ---------------------------------------------------------------------------------------\n The requested language '${lang}' is not available in the provided setup.\n To enable it, add the following import your \\`app.config.ts\\`:\n import 'prismjs/components/prism-${lang}';\n ---------------------------------------------------------------------------------------\n `);\n }\n return code;\n }\n return Prism.highlight(\n code,\n diff ? Prism.languages['diff'] : Prism.languages[lang],\n lang,\n );\n },\n }) as HighlightExtension;\n }\n}\n","import Prism from 'prismjs';\n\n(function () {\n if (typeof Prism === 'undefined') {\n return;\n }\n\n Prism.languages['angular'] = Prism.languages.extend('markup', {\n keyword:\n /(?:@if|@for|@switch|@defer|@loading|@error|@placeholder|prefetch)\\b/,\n operator: /\\b(?:on|when)\\b/,\n number: {\n pattern: /\\b(minimum|after)\\s+\\d+(?:s|ms|)/gi,\n lookbehind: true,\n },\n builtin: {\n pattern:\n /\\b(?:viewport|timer|minimum|after|hover|idle|immediate|interaction)/,\n },\n function:\n /#?(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\s*(?:\\.\\s*(?:apply|bind|call)\\s*)?\\()/,\n });\n\n Prism.languages['ng'] = Prism.languages['angular'];\n})();\n","import { Provider } from '@angular/core';\nimport { PrismHighlighter } from './lib/prism-highlighter';\n\nimport {\n ContentRenderer,\n NoopContentRenderer,\n} from '../../src/lib/content-renderer';\nimport 'prismjs';\nimport 'prismjs/plugins/toolbar/prism-toolbar';\nimport 'prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard';\nimport './lib/prism/angular';\n\nexport { PrismHighlighter };\n\nexport function withPrismHighlighter(): Provider[] {\n return [{ provide: ContentRenderer, useClass: NoopContentRenderer }];\n}\n"],"mappings":";;;;;;;;;AAYA,IAAa,mBAAb,MAAa,yBAAyB,yBAAwB;CACnD,iBAAiB,MAAc,MAAY;EAClD,MAAM,UACJ,KAAK,WAAW,OAAO,IAAI,MAAM,UAAU,UACvC,YAAY,KAAI,mBAChB,YAAY,KAAK,QAAQ,SAAS,GAAG;AAC3C,SAAO,eAAe,QAAO,iBAAkB,QAAO,IAAK,KAAI;;CAGxD,wBAAqB;AAC5B,SAAO,gBAAgB;GACrB,OAAO;GACP,YAAY,MAAc,SAAgB;IACxC,IAAI,OAAO,MAAM,WAAW,QAAQ;AACpC,WAAO,OAAO,KAAK,QAAQ,SAAS,GAAG,GAAG,QAAQ;AAElD,QAAI,QAAQ,CAAC,MAAM,UAAU,SAAS;AACpC,YAAO;AACP,aAAQ,KAAK;;;;;;;cAOT;;AAGN,QAAI,CAAC,MAAM,UAAU,OAAO;AAC1B,SAAI,SAAS,UACX,SAAQ,KAAK;;8BAEK,KAAI;;yCAEO,KAAI;;gBAE7B;AAEN,YAAO;;AAET,WAAO,MAAM,UACX,MACA,OAAO,MAAM,UAAU,UAAU,MAAM,UAAU,OACjD,KACD;;GAEJ,CAAuB;;;;;;;SA9Cf;GAAgB,MAAA;GAAA,QAAA,GAAA,gBAAA;GAAA,CAAA;;;;;;;SAAhB;GAAgB,CAAA;;;;;;;OAAhB;CAAgB,YAAA,CAAA,EAAA,MAD5B,YAAA,CAAA;;;;CCTA,WAAA;AACC,KAAI,OAAO,YAAU,YACnB;AAGF,SAAM,UAAU,aAAa,QAAM,UAAU,OAAO,UAAU;EAC5D,SACE;EACF,UAAU;EACV,QAAQ;GACN,SAAS;GACT,YAAY;GACb;EACD,SAAS,EACP,SACE,uEACH;EACD,UACE;EACH,CAAC;AAEF,SAAM,UAAU,QAAQ,QAAM,UAAU;IACtC;;;ACVJ,SAAgB,uBAAoB;AAClC,QAAO,CAAC;EAAE,SAAS;EAAiB,UAAU;EAAqB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"analogjs-content-resources.mjs","names":[],"sources":["../../resources/src/content-files-resource.ts","../../resources/src/content-file-resource.ts"],"sourcesContent":["import { resource, ResourceRef } from '@angular/core';\nimport type { ContentFile } from '../../src/lib/content-file';\nimport {\n filterByLocale,\n injectContentLocale,\n} from '../../src/lib/content-locale';\nimport type { InjectContentFilesFilterFunction } from '../../src/lib/inject-content-files';\nimport { injectContentListLoader } from '../../src/lib/content-list-loader';\n\nexport function contentFilesResource<Attributes extends Record<string, any>>(\n filterFn?: InjectContentFilesFilterFunction<Attributes> | undefined,\n): ResourceRef<ContentFile<Attributes>[] | undefined> {\n const contentListLoader = injectContentListLoader<Attributes>();\n const locale = injectContentLocale();\n const contentList = contentListLoader().then((items) => {\n let results = locale ? filterByLocale(items, locale) : items;\n if (filterFn) {\n results = results.filter(filterFn);\n }\n return results;\n });\n\n return resource({\n loader: () => contentList,\n });\n}\n","import type { StandardSchemaV1 } from '@standard-schema/spec';\nimport {\n computed,\n inject,\n resource,\n Signal,\n type ResourceRef,\n} from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\n\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { from } from 'rxjs';\nimport { map } from 'rxjs/operators';\nimport type { ContentFile } from '../../src/lib/content-file';\nimport { injectContentLocale } from '../../src/lib/content-locale';\nimport { ContentRenderer } from '../../src/lib/content-renderer';\nimport {\n FrontmatterValidationError,\n parseRawContentFile,\n parseRawContentFileAsync,\n} from '../../src/lib/parse-raw-content-file';\nimport { injectContentFileLoader } from '../../src/lib/content-file-loader';\n\nexport interface ContentFileResourceResult<\n Attributes extends Record<string, any> = Record<string, any>,\n> extends ContentFile<Attributes | Record<string, never>> {\n toc: Array<{ id: string; level: number; text: string }>;\n}\n\ntype ContentFileParams =\n | Signal<string | { customFilename: string }>\n | Signal<string>\n | Signal<{ customFilename: string }>;\n\nasync function validateAttributes<TSchema extends StandardSchemaV1>(\n schema: TSchema,\n attributes: unknown,\n filename?: string,\n) {\n const result = await schema['~standard'].validate(attributes);\n if (result.issues) {\n throw new FrontmatterValidationError(result.issues, filename);\n }\n\n return result.value;\n}\n\nfunction getValidationFilename(filename: string): string {\n return filename.replace(/^\\/src\\/content\\//, '');\n}\n\nasync function getContentFile<\n Attributes extends Record<string, any> = Record<string, any>,\n>(\n contentFiles: Record<string, () => Promise<string>>,\n slug: string,\n fallback: string,\n locale?: string | null,\n schema?: StandardSchemaV1,\n): Promise<ContentFile<Attributes | Record<string, never>>> {\n // Normalize file keys so both \"/src/content/...\" and \"/<project>/src/content/...\" resolve.\n // This mirrors normalization used elsewhere in the content pipeline.\n const normalizedFiles: Record<string, () => Promise<string>> = {};\n const stemToKey: Record<string, string> = {};\n for (const [key, resolver] of Object.entries(contentFiles)) {\n const normalizedKey = key\n // replace any prefix up to the content directory with /src/content\n // use a non-greedy match so nested paths containing \"/content\" are preserved\n .replace(/^(?:.*?)\\/content(?=\\/)/, '/src/content')\n // normalize duplicate slashes\n .replace(/\\/{2,}/g, '/');\n normalizedFiles[normalizedKey] = resolver;\n // Index by bare filename stem so slug-only lookups work\n const stem = normalizedKey\n .split('/')\n .pop()\n ?.replace(/\\.[^.]+$/, '');\n if (stem && !stemToKey[stem]) {\n stemToKey[stem] = normalizedKey;\n }\n }\n\n // Try direct file first, then directory index variants, then bare slug via stem\n const base = `/src/content/${slug}`.replace(/\\/{2,}/g, '/');\n const candidates = [`${base}.md`, `${base}/index.md`];\n\n // Try locale-prefixed paths first, then fall back to unprefixed, then bare slug via stem\n const localeCandidates = locale\n ? candidates.map((c) =>\n c.replace('/src/content/', `/src/content/${locale}/`),\n )\n : [];\n const allCandidates = [...localeCandidates, ...candidates];\n const matchKey =\n allCandidates.find((k) => k in normalizedFiles) ?? stemToKey[slug];\n const contentFile = matchKey ? normalizedFiles[matchKey] : undefined;\n\n if (!contentFile) {\n return {\n filename: base,\n attributes: {},\n slug: '',\n content: fallback,\n } as ContentFile<Attributes | Record<string, never>>;\n }\n\n const resolvedBase = matchKey!.replace(/\\.md$/, '');\n const validationFilename = getValidationFilename(matchKey!);\n\n return contentFile().then(\n async (contentFile: string | { default: any; metadata: any }) => {\n if (typeof contentFile === 'string') {\n const { content, attributes } = schema\n ? await parseRawContentFileAsync(\n contentFile,\n schema,\n validationFilename,\n )\n : parseRawContentFile<Attributes>(contentFile);\n\n return {\n filename: resolvedBase,\n slug,\n attributes,\n content,\n } as ContentFile<Attributes | Record<string, never>>;\n }\n\n const attributes = schema\n ? await validateAttributes(\n schema,\n contentFile.metadata,\n validationFilename,\n )\n : contentFile.metadata;\n\n return {\n filename: resolvedBase,\n slug,\n attributes,\n content: contentFile.default,\n } as ContentFile<Attributes | Record<string, never>>;\n },\n );\n}\n\n/**\n * Resource for requesting an individual content file.\n *\n * @example\n * ```typescript\n * // Without schema (existing behavior)\n * const post = contentFileResource<BlogAttributes>();\n *\n * // With schema validation\n * import * as v from 'valibot';\n * const BlogSchema = v.object({\n * title: v.string(),\n * date: v.pipe(v.string(), v.isoDate()),\n * });\n * const post = contentFileResource({ schema: BlogSchema });\n * ```\n */\nexport function contentFileResource<\n Attributes extends Record<string, any> = Record<string, any>,\n>(\n params?: ContentFileParams,\n fallback?: string,\n): ResourceRef<ContentFileResourceResult<Attributes> | undefined>;\n\nexport function contentFileResource<TSchema extends StandardSchemaV1>(options: {\n params?: ContentFileParams;\n fallback?: string;\n schema: TSchema;\n}): ResourceRef<\n | ContentFileResourceResult<\n StandardSchemaV1.InferOutput<TSchema> & Record<string, any>\n >\n | undefined\n>;\n\nexport function contentFileResource(\n paramsOrOptions?:\n | ContentFileParams\n | {\n params?: ContentFileParams;\n fallback?: string;\n schema?: StandardSchemaV1;\n },\n fallbackArg = 'No Content Found',\n) {\n // Detect options-object form vs legacy positional form\n const isOptionsObject =\n paramsOrOptions &&\n typeof paramsOrOptions === 'object' &&\n !('set' in paramsOrOptions) && // not a Signal\n ('schema' in paramsOrOptions ||\n 'params' in paramsOrOptions ||\n 'fallback' in paramsOrOptions);\n\n const params: ContentFileParams | undefined = isOptionsObject\n ? (paramsOrOptions as { params?: ContentFileParams }).params\n : (paramsOrOptions as ContentFileParams | undefined);\n const fallback: string = isOptionsObject\n ? ((paramsOrOptions as { fallback?: string }).fallback ??\n 'No Content Found')\n : fallbackArg;\n const schema: StandardSchemaV1 | undefined = isOptionsObject\n ? (paramsOrOptions as { schema?: StandardSchemaV1 }).schema\n : undefined;\n\n const loaderPromise = injectContentFileLoader();\n const contentRenderer = inject(ContentRenderer);\n const locale = injectContentLocale();\n const contentFilesMap = toSignal(from(loaderPromise()));\n const input =\n params ||\n toSignal(\n inject(ActivatedRoute).paramMap.pipe(\n map((params) => params.get('slug') as string),\n ),\n { requireSync: true },\n );\n\n return resource({\n params: computed(() => ({ input: input(), files: contentFilesMap() })),\n loader: async ({ params: resourceParams }) => {\n const { input: param, files } = resourceParams;\n\n if (typeof param === 'string') {\n if (param) {\n const file = await getContentFile(\n files!,\n param,\n fallback,\n locale,\n schema,\n );\n if (typeof file.content === 'string') {\n const rendered = (await contentRenderer.render(file.content)) as {\n toc?: Array<{ id: string; level: number; text: string }>;\n };\n return {\n ...file,\n toc: rendered.toc ?? [],\n };\n }\n return {\n ...file,\n toc: [],\n };\n }\n\n return {\n filename: '',\n slug: '',\n attributes: {},\n content: fallback,\n toc: [],\n };\n } else {\n const file = await getContentFile(\n files!,\n param.customFilename,\n fallback,\n locale,\n schema,\n );\n if (typeof file.content === 'string') {\n const rendered = (await contentRenderer.render(file.content)) as {\n toc?: Array<{ id: string; level: number; text: string }>;\n };\n return {\n ...file,\n toc: rendered.toc ?? [],\n };\n }\n return {\n ...file,\n toc: [],\n };\n }\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"analogjs-content-resources.mjs","names":[],"sources":["../../resources/src/content-files-resource.ts","../../resources/src/content-file-resource.ts"],"sourcesContent":["import { resource, ResourceRef } from '@angular/core';\nimport type { ContentFile } from '../../src/lib/content-file';\nimport {\n filterByLocale,\n injectContentLocale,\n} from '../../src/lib/content-locale';\nimport type { InjectContentFilesFilterFunction } from '../../src/lib/inject-content-files';\nimport { injectContentListLoader } from '../../src/lib/content-list-loader';\n\nexport function contentFilesResource<Attributes extends Record<string, any>>(\n filterFn?: InjectContentFilesFilterFunction<Attributes> | undefined,\n): ResourceRef<ContentFile<Attributes>[] | undefined> {\n const contentListLoader = injectContentListLoader<Attributes>();\n const locale = injectContentLocale();\n const contentList = contentListLoader().then((items) => {\n let results = locale ? filterByLocale(items, locale) : items;\n if (filterFn) {\n results = results.filter(filterFn);\n }\n return results;\n });\n\n return resource({\n loader: () => contentList,\n });\n}\n","import type { StandardSchemaV1 } from '@standard-schema/spec';\nimport {\n computed,\n inject,\n resource,\n Signal,\n type ResourceRef,\n} from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\n\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { from } from 'rxjs';\nimport { map } from 'rxjs/operators';\nimport type { ContentFile } from '../../src/lib/content-file';\nimport { injectContentLocale } from '../../src/lib/content-locale';\nimport { ContentRenderer } from '../../src/lib/content-renderer';\nimport {\n FrontmatterValidationError,\n parseRawContentFile,\n parseRawContentFileAsync,\n} from '../../src/lib/parse-raw-content-file';\nimport { injectContentFileLoader } from '../../src/lib/content-file-loader';\n\nexport interface ContentFileResourceResult<\n Attributes extends Record<string, any> = Record<string, any>,\n> extends ContentFile<Attributes | Record<string, never>> {\n toc: Array<{ id: string; level: number; text: string }>;\n}\n\ntype ContentFileParams =\n | Signal<string | { customFilename: string }>\n | Signal<string>\n | Signal<{ customFilename: string }>;\n\nasync function validateAttributes<TSchema extends StandardSchemaV1>(\n schema: TSchema,\n attributes: unknown,\n filename?: string,\n) {\n const result = await schema['~standard'].validate(attributes);\n if (result.issues) {\n throw new FrontmatterValidationError(result.issues, filename);\n }\n\n return result.value;\n}\n\nfunction getValidationFilename(filename: string): string {\n return filename.replace(/^\\/src\\/content\\//, '');\n}\n\nasync function getContentFile<\n Attributes extends Record<string, any> = Record<string, any>,\n>(\n contentFiles: Record<string, () => Promise<string>>,\n slug: string,\n fallback: string,\n locale?: string | null,\n schema?: StandardSchemaV1,\n): Promise<ContentFile<Attributes | Record<string, never>>> {\n // Normalize file keys so both \"/src/content/...\" and \"/<project>/src/content/...\" resolve.\n // This mirrors normalization used elsewhere in the content pipeline.\n const normalizedFiles: Record<string, () => Promise<string>> = {};\n const stemToKey: Record<string, string> = {};\n for (const [key, resolver] of Object.entries(contentFiles)) {\n const normalizedKey = key\n // replace any prefix up to the content directory with /src/content\n // use a non-greedy match so nested paths containing \"/content\" are preserved\n .replace(/^(?:.*?)\\/content(?=\\/)/, '/src/content')\n // normalize duplicate slashes\n .replace(/\\/{2,}/g, '/');\n normalizedFiles[normalizedKey] = resolver;\n // Index by bare filename stem so slug-only lookups work\n const stem = normalizedKey\n .split('/')\n .pop()\n ?.replace(/\\.[^.]+$/, '');\n if (stem && !stemToKey[stem]) {\n stemToKey[stem] = normalizedKey;\n }\n }\n\n // Try direct file first, then directory index variants, then bare slug via stem\n const base = `/src/content/${slug}`.replace(/\\/{2,}/g, '/');\n const candidates = [`${base}.md`, `${base}/index.md`];\n\n // Try locale-prefixed paths first, then fall back to unprefixed, then bare slug via stem\n const localeCandidates = locale\n ? candidates.map((c) =>\n c.replace('/src/content/', `/src/content/${locale}/`),\n )\n : [];\n const allCandidates = [...localeCandidates, ...candidates];\n const matchKey =\n allCandidates.find((k) => k in normalizedFiles) ?? stemToKey[slug];\n const contentFile = matchKey ? normalizedFiles[matchKey] : undefined;\n\n if (!contentFile) {\n return {\n filename: base,\n attributes: {},\n slug: '',\n content: fallback,\n } as ContentFile<Attributes | Record<string, never>>;\n }\n\n const resolvedBase = matchKey!.replace(/\\.md$/, '');\n const validationFilename = getValidationFilename(matchKey!);\n\n return contentFile().then(\n async (contentFile: string | { default: any; metadata: any }) => {\n if (typeof contentFile === 'string') {\n const { content, attributes } = schema\n ? await parseRawContentFileAsync(\n contentFile,\n schema,\n validationFilename,\n )\n : parseRawContentFile<Attributes>(contentFile);\n\n return {\n filename: resolvedBase,\n slug,\n attributes,\n content,\n } as ContentFile<Attributes | Record<string, never>>;\n }\n\n const attributes = schema\n ? await validateAttributes(\n schema,\n contentFile.metadata,\n validationFilename,\n )\n : contentFile.metadata;\n\n return {\n filename: resolvedBase,\n slug,\n attributes,\n content: contentFile.default,\n } as ContentFile<Attributes | Record<string, never>>;\n },\n );\n}\n\n/**\n * Resource for requesting an individual content file.\n *\n * @example\n * ```typescript\n * // Without schema (existing behavior)\n * const post = contentFileResource<BlogAttributes>();\n *\n * // With schema validation\n * import * as v from 'valibot';\n * const BlogSchema = v.object({\n * title: v.string(),\n * date: v.pipe(v.string(), v.isoDate()),\n * });\n * const post = contentFileResource({ schema: BlogSchema });\n * ```\n */\nexport function contentFileResource<\n Attributes extends Record<string, any> = Record<string, any>,\n>(\n params?: ContentFileParams,\n fallback?: string,\n): ResourceRef<ContentFileResourceResult<Attributes> | undefined>;\n\nexport function contentFileResource<TSchema extends StandardSchemaV1>(options: {\n params?: ContentFileParams;\n fallback?: string;\n schema: TSchema;\n}): ResourceRef<\n | ContentFileResourceResult<\n StandardSchemaV1.InferOutput<TSchema> & Record<string, any>\n >\n | undefined\n>;\n\nexport function contentFileResource(\n paramsOrOptions?:\n | ContentFileParams\n | {\n params?: ContentFileParams;\n fallback?: string;\n schema?: StandardSchemaV1;\n },\n fallbackArg = 'No Content Found',\n) {\n // Detect options-object form vs legacy positional form\n const isOptionsObject =\n paramsOrOptions &&\n typeof paramsOrOptions === 'object' &&\n !('set' in paramsOrOptions) && // not a Signal\n ('schema' in paramsOrOptions ||\n 'params' in paramsOrOptions ||\n 'fallback' in paramsOrOptions);\n\n const params: ContentFileParams | undefined = isOptionsObject\n ? (paramsOrOptions as { params?: ContentFileParams }).params\n : (paramsOrOptions as ContentFileParams | undefined);\n const fallback: string = isOptionsObject\n ? ((paramsOrOptions as { fallback?: string }).fallback ??\n 'No Content Found')\n : fallbackArg;\n const schema: StandardSchemaV1 | undefined = isOptionsObject\n ? (paramsOrOptions as { schema?: StandardSchemaV1 }).schema\n : undefined;\n\n const loaderPromise = injectContentFileLoader();\n const contentRenderer = inject(ContentRenderer);\n const locale = injectContentLocale();\n const contentFilesMap = toSignal(from(loaderPromise()));\n const input =\n params ||\n toSignal(\n inject(ActivatedRoute).paramMap.pipe(\n map((params) => params.get('slug') as string),\n ),\n { requireSync: true },\n );\n\n return resource({\n params: computed(() => ({ input: input(), files: contentFilesMap() })),\n loader: async ({ params: resourceParams }) => {\n const { input: param, files } = resourceParams;\n\n if (typeof param === 'string') {\n if (param) {\n const file = await getContentFile(\n files!,\n param,\n fallback,\n locale,\n schema,\n );\n if (typeof file.content === 'string') {\n const rendered = (await contentRenderer.render(file.content)) as {\n toc?: Array<{ id: string; level: number; text: string }>;\n };\n return {\n ...file,\n toc: rendered.toc ?? [],\n };\n }\n return {\n ...file,\n toc: [],\n };\n }\n\n return {\n filename: '',\n slug: '',\n attributes: {},\n content: fallback,\n toc: [],\n };\n } else {\n const file = await getContentFile(\n files!,\n param.customFilename,\n fallback,\n locale,\n schema,\n );\n if (typeof file.content === 'string') {\n const rendered = (await contentRenderer.render(file.content)) as {\n toc?: Array<{ id: string; level: number; text: string }>;\n };\n return {\n ...file,\n toc: rendered.toc ?? [],\n };\n }\n return {\n ...file,\n toc: [],\n };\n }\n },\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"analogjs-content-shiki-highlighter.mjs","names":[],"sources":["../../shiki-highlighter/src/index.ts"],"sourcesContent":["import { Provider } from '@angular/core';\nimport type {\n BundledLanguage,\n BundledTheme,\n CodeOptionsMeta,\n CodeOptionsMultipleThemes,\n CodeOptionsSingleTheme,\n CodeToHastOptionsCommon,\n} from 'shiki';\n\nimport {\n ContentRenderer,\n NoopContentRenderer,\n} from '../../src/lib/content-renderer';\n\nexport type ShikiHighlightOptions = Partial<\n Omit<CodeToHastOptionsCommon<BundledLanguage>, 'lang'>\n> &\n CodeOptionsMeta &\n Partial<CodeOptionsSingleTheme<BundledTheme>> &\n Partial<CodeOptionsMultipleThemes<BundledTheme>>;\n\nexport type WithShikiHighlighterOptions = ShikiHighlightOptions & {\n container?: string;\n};\n\nexport function withShikiHighlighter(\n _opts: WithShikiHighlighterOptions = {},\n): Provider[] {\n return [\n {\n provide: ContentRenderer,\n useClass: NoopContentRenderer,\n },\n ];\n}\n"],"mappings":";;AA0BA,SAAgB,qBACd,QAAqC,EAAE,EAC3B;AACZ,QACE,CACE;EACU,SAAA;EAEb,UAAA"}
1
+ {"version":3,"file":"analogjs-content-shiki-highlighter.mjs","names":[],"sources":["../../shiki-highlighter/src/index.ts"],"sourcesContent":["import { Provider } from '@angular/core';\nimport type {\n BundledLanguage,\n BundledTheme,\n CodeOptionsMeta,\n CodeOptionsMultipleThemes,\n CodeOptionsSingleTheme,\n CodeToHastOptionsCommon,\n} from 'shiki';\n\nimport {\n ContentRenderer,\n NoopContentRenderer,\n} from '../../src/lib/content-renderer';\n\nexport type ShikiHighlightOptions = Partial<\n Omit<CodeToHastOptionsCommon<BundledLanguage>, 'lang'>\n> &\n CodeOptionsMeta &\n Partial<CodeOptionsSingleTheme<BundledTheme>> &\n Partial<CodeOptionsMultipleThemes<BundledTheme>>;\n\nexport type WithShikiHighlighterOptions = ShikiHighlightOptions & {\n container?: string;\n};\n\nexport function withShikiHighlighter(\n _opts: WithShikiHighlighterOptions = {},\n): Provider[] {\n return [\n {\n provide: ContentRenderer,\n useClass: NoopContentRenderer,\n },\n ];\n}\n"],"mappings":";;AA0BA,SAAgB,qBACd,QAAqC,EAAE,EAAA;AAEvC,QAAO,CACL;EACE,SAAS;EACT,UAAU;EACX,CACF"}
@@ -23,7 +23,10 @@ var AnchorNavigationDirective = class AnchorNavigationDirective {
23
23
  handleNavigation(element) {
24
24
  if (element instanceof HTMLAnchorElement && isInternalUrl(element, this.document) && hasTargetSelf(element) && !hasDownloadAttribute(element)) {
25
25
  const { pathname, search, hash } = element;
26
- const url = this.location.normalize(`${pathname}${search}${hash}`);
26
+ const isFragmentOnly = element.getAttribute("href")?.startsWith("#");
27
+ const resolvedPathname = isFragmentOnly ? this.document.location.pathname : pathname;
28
+ const resolvedSearch = isFragmentOnly ? this.document.location.search : search;
29
+ const url = this.location.normalize(`${resolvedPathname}${resolvedSearch}${hash}`);
27
30
  this.router.navigateByUrl(url);
28
31
  return false;
29
32
  }
@@ -32,7 +35,7 @@ var AnchorNavigationDirective = class AnchorNavigationDirective {
32
35
  static {
33
36
  this.ɵfac = i0.ɵɵngDeclareFactory({
34
37
  minVersion: "12.0.0",
35
- version: "22.0.0",
38
+ version: "22.1.5",
36
39
  ngImport: i0,
37
40
  type: AnchorNavigationDirective,
38
41
  deps: [],
@@ -42,7 +45,7 @@ var AnchorNavigationDirective = class AnchorNavigationDirective {
42
45
  static {
43
46
  this.ɵdir = i0.ɵɵngDeclareDirective({
44
47
  minVersion: "14.0.0",
45
- version: "22.0.0",
48
+ version: "22.1.5",
46
49
  type: AnchorNavigationDirective,
47
50
  isStandalone: true,
48
51
  selector: "[analogAnchorNavigation]",
@@ -53,7 +56,7 @@ var AnchorNavigationDirective = class AnchorNavigationDirective {
53
56
  };
54
57
  i0.ɵɵngDeclareClassMetadata({
55
58
  minVersion: "12.0.0",
56
- version: "22.0.0",
59
+ version: "22.1.5",
57
60
  ngImport: i0,
58
61
  type: AnchorNavigationDirective,
59
62
  decorators: [{
@@ -163,6 +166,19 @@ function injectContent(param = "slug", fallback = "No Content Found") {
163
166
  } else return getContentFile(contentFiles, "", param.customFilename, fallback, renderTaskService, contentRenderer, locale).pipe(tap(() => renderTaskService.clearRenderTask(task)));
164
167
  }
165
168
  //#endregion
169
+ //#region packages/content/src/lib/utils/escape-html.ts
170
+ function escapeHtml(value) {
171
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
172
+ }
173
+ /**
174
+ * Reduces a fenced code-block info string to a safe language identifier so it
175
+ * cannot break out of the `class="language-..."` attribute it is interpolated
176
+ * into.
177
+ */
178
+ function sanitizeLanguage(lang) {
179
+ return (lang ?? "").match(/^[a-zA-Z0-9-+#.]*/)?.[0] ?? "";
180
+ }
181
+ //#endregion
166
182
  //#region packages/content/src/lib/marked-setup.service.ts
167
183
  /**
168
184
  * Credit goes to Scully for original implementation
@@ -173,10 +189,11 @@ var MarkedSetupService = class MarkedSetupService {
173
189
  this.highlighter = inject(MarkedContentHighlighter, { optional: true });
174
190
  const renderer = new marked.Renderer();
175
191
  renderer.code = ({ text, lang }) => {
176
- if (lang === "mermaid") return "<pre class=\"mermaid\">" + text + "</pre>";
177
- if (!lang) return "<pre><code>" + text + "</code></pre>";
178
- if (this.highlighter?.augmentCodeBlock) return this.highlighter?.augmentCodeBlock(text, lang);
179
- return `<pre class="language-${lang}"><code class="language-${lang}">${text}</code></pre>`;
192
+ const language = sanitizeLanguage(lang);
193
+ if (language === "mermaid") return "<pre class=\"mermaid\">" + escapeHtml(text) + "</pre>";
194
+ if (!language) return "<pre><code>" + escapeHtml(text) + "</code></pre>";
195
+ if (this.highlighter?.augmentCodeBlock) return this.highlighter?.augmentCodeBlock(text, language);
196
+ return `<pre class="language-${language}"><code class="language-${language}">${escapeHtml(text)}</code></pre>`;
180
197
  };
181
198
  const extensions = [gfmHeadingId(), mangle()];
182
199
  if (this.highlighter) extensions.push(this.highlighter.getHighlightExtension());
@@ -194,7 +211,7 @@ var MarkedSetupService = class MarkedSetupService {
194
211
  static {
195
212
  this.ɵfac = i0.ɵɵngDeclareFactory({
196
213
  minVersion: "12.0.0",
197
- version: "22.0.0",
214
+ version: "22.1.5",
198
215
  ngImport: i0,
199
216
  type: MarkedSetupService,
200
217
  deps: [],
@@ -204,7 +221,7 @@ var MarkedSetupService = class MarkedSetupService {
204
221
  static {
205
222
  this.ɵprov = i0.ɵɵngDeclareInjectable({
206
223
  minVersion: "12.0.0",
207
- version: "22.0.0",
224
+ version: "22.1.5",
208
225
  ngImport: i0,
209
226
  type: MarkedSetupService
210
227
  });
@@ -212,7 +229,7 @@ var MarkedSetupService = class MarkedSetupService {
212
229
  };
213
230
  i0.ɵɵngDeclareClassMetadata({
214
231
  minVersion: "12.0.0",
215
- version: "22.0.0",
232
+ version: "22.1.5",
216
233
  ngImport: i0,
217
234
  type: MarkedSetupService,
218
235
  decorators: [{ type: Injectable }],
@@ -239,7 +256,7 @@ var MarkdownContentRendererService = class MarkdownContentRendererService {
239
256
  static {
240
257
  this.ɵfac = i0.ɵɵngDeclareFactory({
241
258
  minVersion: "12.0.0",
242
- version: "22.0.0",
259
+ version: "22.1.5",
243
260
  ngImport: i0,
244
261
  type: MarkdownContentRendererService,
245
262
  deps: [],
@@ -249,7 +266,7 @@ var MarkdownContentRendererService = class MarkdownContentRendererService {
249
266
  static {
250
267
  this.ɵprov = i0.ɵɵngDeclareInjectable({
251
268
  minVersion: "12.0.0",
252
- version: "22.0.0",
269
+ version: "22.1.5",
253
270
  ngImport: i0,
254
271
  type: MarkdownContentRendererService
255
272
  });
@@ -257,7 +274,7 @@ var MarkdownContentRendererService = class MarkdownContentRendererService {
257
274
  };
258
275
  i0.ɵɵngDeclareClassMetadata({
259
276
  minVersion: "12.0.0",
260
- version: "22.0.0",
277
+ version: "22.1.5",
261
278
  ngImport: i0,
262
279
  type: MarkdownContentRendererService,
263
280
  decorators: [{ type: Injectable }]
@@ -301,7 +318,7 @@ var AnalogMarkdownRouteComponent = class AnalogMarkdownRouteComponent {
301
318
  static {
302
319
  this.ɵfac = i0.ɵɵngDeclareFactory({
303
320
  minVersion: "12.0.0",
304
- version: "22.0.0",
321
+ version: "22.1.5",
305
322
  ngImport: i0,
306
323
  type: AnalogMarkdownRouteComponent,
307
324
  deps: [],
@@ -311,7 +328,7 @@ var AnalogMarkdownRouteComponent = class AnalogMarkdownRouteComponent {
311
328
  static {
312
329
  this.ɵcmp = i0.ɵɵngDeclareComponent({
313
330
  minVersion: "14.0.0",
314
- version: "22.0.0",
331
+ version: "22.1.5",
315
332
  type: AnalogMarkdownRouteComponent,
316
333
  isStandalone: true,
317
334
  selector: "analog-markdown-route",
@@ -327,7 +344,7 @@ var AnalogMarkdownRouteComponent = class AnalogMarkdownRouteComponent {
327
344
  };
328
345
  i0.ɵɵngDeclareClassMetadata({
329
346
  minVersion: "12.0.0",
330
- version: "22.0.0",
347
+ version: "22.1.5",
331
348
  ngImport: i0,
332
349
  type: AnalogMarkdownRouteComponent,
333
350
  decorators: [{
@@ -384,7 +401,7 @@ var AnalogMarkdownComponent = class AnalogMarkdownComponent {
384
401
  static {
385
402
  this.ɵfac = i0.ɵɵngDeclareFactory({
386
403
  minVersion: "12.0.0",
387
- version: "22.0.0",
404
+ version: "22.1.5",
388
405
  ngImport: i0,
389
406
  type: AnalogMarkdownComponent,
390
407
  deps: [],
@@ -394,7 +411,7 @@ var AnalogMarkdownComponent = class AnalogMarkdownComponent {
394
411
  static {
395
412
  this.ɵcmp = i0.ɵɵngDeclareComponent({
396
413
  minVersion: "17.1.0",
397
- version: "22.0.0",
414
+ version: "22.1.5",
398
415
  type: AnalogMarkdownComponent,
399
416
  isStandalone: true,
400
417
  selector: "analog-markdown",
@@ -425,7 +442,7 @@ var AnalogMarkdownComponent = class AnalogMarkdownComponent {
425
442
  };
426
443
  i0.ɵɵngDeclareClassMetadata({
427
444
  minVersion: "12.0.0",
428
- version: "22.0.0",
445
+ version: "22.1.5",
429
446
  ngImport: i0,
430
447
  type: AnalogMarkdownComponent,
431
448
  decorators: [{
@@ -1 +1 @@
1
- {"version":3,"file":"analogjs-content.mjs","names":["#marked"],"sources":["../../src/lib/anchor-navigation.directive.ts","../../src/lib/utils/zone-wait-for.ts","../../src/lib/content.ts","../../src/lib/marked-setup.service.ts","../../src/lib/markdown-content-renderer.service.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 const url = this.location.normalize(`${pathname}${search}${hash}`);\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","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 { 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 { parseRawContentFile } from './parse-raw-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, () => Promise<string>>,\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, () => Promise<string>> = {};\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 as () => Promise<string>;\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 | { default: string; metadata: Attributes }>(\n (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 },\n ).pipe(\n switchMap((contentFile) => {\n if (typeof contentFile === 'string') {\n const { content, attributes } =\n parseRawContentFile<Attributes>(contentFile);\n return 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 return of({\n filename: resolvedBase,\n slug,\n attributes: contentFile.metadata,\n content: contentFile.default,\n toc: [],\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","/**\n * Credit goes to Scully for original implementation\n * https://github.com/scullyio/scully/blob/main/libs/scully/src/lib/fileHanderPlugins/markdown.ts\n */\nimport { inject, Injectable } from '@angular/core';\nimport { marked } from 'marked';\nimport { gfmHeadingId } from 'marked-gfm-heading-id';\nimport { mangle } from 'marked-mangle';\nimport { MarkedContentHighlighter } from './marked-content-highlighter';\n\n@Injectable()\nexport class MarkedSetupService {\n private readonly marked: typeof marked;\n private readonly highlighter = inject(MarkedContentHighlighter, {\n optional: true,\n });\n\n constructor() {\n const renderer = new marked.Renderer();\n renderer.code = ({ text, lang }) => {\n // Let's do a language based detection like on GitHub\n // So we can still have non-interpreted mermaid code\n if (lang === 'mermaid') {\n return '<pre class=\"mermaid\">' + text + '</pre>';\n }\n\n if (!lang) {\n return '<pre><code>' + text + '</code></pre>';\n }\n\n if (this.highlighter?.augmentCodeBlock) {\n return this.highlighter?.augmentCodeBlock(text, lang);\n }\n\n return `<pre class=\"language-${lang}\"><code class=\"language-${lang}\">${text}</code></pre>`;\n };\n\n const extensions = [gfmHeadingId(), mangle()];\n\n if (this.highlighter) {\n extensions.push(this.highlighter.getHighlightExtension());\n }\n\n marked.use(...extensions, {\n renderer,\n pedantic: false,\n gfm: true,\n breaks: false,\n });\n\n this.marked = marked;\n }\n\n getMarkedInstance(): typeof marked {\n return this.marked;\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { getHeadingList } from 'marked-gfm-heading-id';\n\nimport {\n ContentRenderer,\n RenderedContent,\n TableOfContentItem,\n} from './content-renderer';\nimport { MarkedSetupService } from './marked-setup.service';\n\n@Injectable()\nexport class MarkdownContentRendererService implements ContentRenderer {\n #marked = inject(MarkedSetupService, { self: true });\n\n async render(content: string): Promise<RenderedContent> {\n const renderedContent = await this.#marked\n .getMarkedInstance()\n .parse(content);\n return {\n content: renderedContent,\n toc: getHeadingList(),\n };\n }\n\n getContentHeadings(content: string): TableOfContentItem[] {\n return [...content.matchAll(/^(#{1,6})\\s+(.+?)\\s*$/gm)].map((match) => ({\n id: match[2]\n .trim()\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .replace(/\\s+/g, '-'),\n level: match[1].length,\n text: match[2].trim(),\n }));\n }\n\n // eslint-disable-next-line\n enhance(): void {}\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":";;;;;;;;;;;;;;;;;CAQO,cAAA;;kBACuB,OAAO,SAAS;gBAChB,OAAO,OAAS;;;AAG5C,MAAA,mBACuD,qBAEnD,cAAmB,SAAA,KAAA,SACnB,IAIQ,cAAU,QAAQ,IACpB,CAAA,qBAAoB,QAAa,EAAA;GAClC,MAAO,EAAA,UAAc,QAAI,SAAA;GAEvB,MAAA,MAAA,KAAA,SAAA,UAAA,GAAA,WAAA,SAAA,OAAA;;AAGF,UAAA;;;;CAfR;AAAA,OAAa,OAAU,GAAA,mBAAiB;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAA,MAAA,EAAA;GAAA,QAAA,GAAA,gBAAA;GAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;GAR/B,UAAA;GACE,YAAA;GACZ,CAAA;;;EAyBO,MAAA;EACA,MAAc,CAAA,SAAA,CAAA,gBAAwB,CAAA;;;AAG/C,SAAS,qBAAc,eAA2C;AAChE,QAAQ,cAAc,aAAU,WAAc,KAAA;;AAGhD,SAAS,cACP,eACA;AAEA,QACE,CAAA,cAAc,UAAS,cAAkB,WACzC;;;;;;;AC1CJ,eAAsB,QAAW,MAA8C;AAC7E,KAAI,aAAa,KAAO,CACtB,QAAO,eAAoB,KAAA;AAG7B,KAAI,OAAO,SAAS,YAClB,QAAO;CAGT,MAAM,YAAY,KAAK,QAAQ,kBAC7B,wBAAwB,KAAK,QAAQ,UAC/B,IASN,EAAA,QAAU,GAEV;;;;;;;;SCCI,eAAA,cAA2D,QAAA,MAAA,UAAA,mBAAA,iBAAA,QAAA;CAE/D,MAAM,kBACH,EAAA;AAEH,MAAA,MAAA,CAAA,KAAgB,aAAA,OAAiB,QAAA,aAAA,EAAA;4BAG7B,QAAO,2BAAgC,eAAQ,CAC/C,QAAc,WAAQ,IAAM;AAE5B,kBAAgB,iBAAA;;CAEtB,MAAM,OAAA,gBAAyB,SAAA,OAAgB,QAAA,WAAY,IAAA;CAIzD,MAAO,WADS,qBAFI,CAAA,GAAA,KAAA,MAAe,GAAA,KAAK,WAAc,EAEtC,OAAA,CACN,MAAA,MAAA,KAAA,gBAAA;CACR,MAAA,cAAU,WAAA,gBAAA,YAAA,KAAA;CACV,MAAA,gBAAc,YAAA,GAAA,KAAA,MAAA,QAAA,SAAA,GAAA;AACd,KAAA,CAAM,YACN,QAAS,GAAA;EACJ,UAAA;EACL,YAAA,EAAA;;EAGE,SAAc;EACT,KAAA,EAAA;EAOP,CAAQ;CAEN,MAAA,cAAmB,kBAAA,eAAA;AAEnB,QAAA,IAAA,YAAiB,aAAkB;AAU/B,UAAU,aAAA,CAAA,CAAA,MAAA,YAAA;AACV,YAAA,KAAA,QAAA;AACA,YAAA,UAAA;AACA,oBAAA,kBAAA,gBAAA,YAAA,EAAA,GAAA;IACK;GACJ,CACJ,KAAA,WAAA,gBAAA;;GAEO,MAAA,EAAA,SAAA,eAAA,oBAAA,YAAA;AACR,UAAU,KAAA,gBAAA,OAAA,QAAA,CAAA,CAAA,KAAA,KAAA,cAAA;IACV,UAAA;IACY;IACH;IACJ;IACL,KAAA,SAAA,OAAA,EAAA;IAEL,EAAA,CAAA;;;;;;;;GASI,CAAA;GAcL,CAAM;;;;;;;;SASE,cAAkB,QAAU,QAAA,WAAmB,oBAAM;CAC3D,MAAO,eAAe,OACpB,oBAAuB;CAErB,MAAI,kBAAM,OAAA,gBAAA;CACR,MAAA,oBACE,OAAA,kBAGA;;CAMJ,MAAO,OAAG,kBAAA,eAAA;AACR,KAAA,OAAU,UAAA,YAAA,WAAA,OAAA;EACV,MAAM,SAAA,OAAA,UAAA,WAAA,KAAA,GAAA,MAAA,aAAA;EACN,MAAA,QAAc,OAAA,eAAA;EACd,MAAS,WAAA,OAAA,UAAA,WAAA,QAAA,MAAA;AACT,SAAK,MAAA,SAAA,KAAA,KAAA,WAAA,OAAA,IAAA,SAAA,CAAA,EAAA,WAAA,SAAA;AACL,OAAA,KAEM,QAAA,eAAkB,cAAgB,QAC7C,MAAA,UAAA,mBAAA,iBAAA,OAAA;AAEM,UAAA,GAAA;;;;;;;;;;;;;;;;;CC1IJ,cAAA;AAML,OAAA,cAAc,OAAA,0BAAA,EAAA,UAJiB,MAKvB,CAAA;EACN,MAAS,WAAU,IAAM,OAAA,UAAW;AAG9B,WAAS,QAAA,EAAW,MAAA,WAAA;AAInB,OAAM,SAAA,UACF,QAAA,4BAAuB,OAAA;AAGvB,OAAA,CAAA,KACK,QAAA,gBAAa,OAAiB;AAGrC,OAAA,KAAA,aAAwB,iBAAA,QAAA,KAAA,aAAA,iBAAA,MAAA,KAAA;AAKxB,UAAA,wBAAa,KAAA,0BAAA,KAAA,IAAA,KAAA;;;AAItB,MAAO,KAAO,YACZ,YAAA,KAAA,KAAA,YAAA,uBAAA,CAAA;AAEK,SAAA,IAAA,GAAA,YAAA;GACG;GACR,UAAA;GAEG,KAAS;;GAGhB,CAAA;AACE,OAAO,SAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3CT,IAAA,iCAAA,MAAA,+BAAM;CACX,UAAU,OAAO,oBAAsB,EAAA,MAAM,MAAO,CAAA;CAEpD,MAAM,OAAO,SAA2C;AAM/C,SAAA;GACN,SAN6B,MAAKA,MAAAA,OAG5B,mBAAA,CACI,MAAA,QAAA;;GAKb;;CAEI,mBAEG,SAAA;AAGH,SAAO,CAAM,GAAG,QAAA,SAAA,0BAAA,CAAA,CAAA,KAAA,WAAA;GACV,IAAM,MAAG,GACd,MAAA,CAAA,aAAA,CAIW,QAAA,aAAA,GAAA,CAAA,QAAA,QAAA,IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3BlB,IAAM,6BAAyC;CAC7C;EACE,SAAS;EACT,UAAU;EACX;CACD,uBAAuB;CACvB,uBAAA;CACD;AAED,SAAgB,qBACd,SACU;AACV,QACE,CAIQ,4BACA,SAAY,cAIrB,CAAA;EAGa,SAAA;EAEZ,YAAA,QAAA;EAAW,CAA6B,GACrC,EACJ,CAAA;;;;;;;;;;;ACjBY,IAAA,+BAAA,MAAA,6BAAM;;mBACC,OAAO,aAAa;eACxB,OAAO,eAAe;yBACH,OAAO,gBAAgB;iBAEvB,KAAA,UAAU,wBACtC,KAAM,MAAA,SAAc,KAAA,yBAC1B;iBAEkB;;CAEnB,qBAA2B;AACpB,OAAA,gBAAgB,SAAS;;;;;;;;;;;;;AAH/B,OAAO,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAAA,cAAA;GAAA,UAAA;GAAA,QAAA,EAAA,SAAA,WAAA;GAAA,gBAAA,CAAA,EAAA,WAAA,2BAAA,CAAA;GAAA,UAAA;GAAA,UAAA;GAAA,UAAA;GAAA,eAAA,GAAA,kBAAA;GAAA,qBAAA;GAAA,CAAA;;;GAjBR,yBAAU;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAA,YAAA,CAAA;EACV,MAAY;EACD,MAAA,CAAA;GACM,UAAA;GACjB,YAAqB;GACN,SAAA,EAAA;GACL,gBAAA,CAAA,0BAAA;GACV,qBAAA;;;;;;;;;ACUa,IAAA,0BAAA,MAAA,wBAAM;CA6BnB,cAAc;mBA5BM,OAAO,aAAa;eACxB,OAAO,eAAe;cACvB,OAAO,OAAO;oBACC,OAAO,YAAY;uBAChB,OAAO,sBACtC,EAAA,UAAA,MAAA,CAAA;AAQA,OAAM,gBAAoB,SAAS,KAAA,kBAAA,CAAA;AAE/B,OAAA,cAAc,eAAA;GACT,MAAK,eAAU,KAAA,SAAA;oBAGZ,QAAA,KAAA,UAAe,wBAAA,aAAA;UAEuC,KAEjE,eAAA;QAGuD,EAAA,CAAA;AAGpD,OAAA,UAAA,MAEG,GAA+B,EAAA,CAAA;;AAIxC,OAAA,kBAAkD,OAAA,gBAAA;AAChD,MAAO,kBACL,KAAmB,WAAS,IAAK,KAAA,cAO/B,MAAA,YAAc,KAAkC,cAAA;;;AAKtD,SAAA,KAAA,MAA2B,KAAA,KAAA,KAAA,SAAA,KAAA,qBAAA,GAAA,EAAA,WAAA,kBAAA,KAAA,cAAA,cAAA,CAAA,EAAA,KAAA,YAAA,KAAA,UAAA,wBAAA,QAAA,CAAA,EAAA,YAAA,MAAA,GAAA,sBAAA,IAAA,CAAA,CAAA;;CAEzB,MAAK,cAAK,SAAA;AAGZ,UAAA,MAAA,KAAA,gBAAA,OAAA,QAAA,EAAoB;;CAOZ,qBAAe;AACV,OAAA,gBAAgB,SAAa;AAG7B,OAAA,KAAS,wBAAa,KAAA,SAAA,QAAA,KAAA,CAAA;;;oCAxEnC,KAAY,cAAA,CACZ,KAAiB,oBAAA,CAAA,CACjB,WAAqB,YAAA;AACrB,QAAe,UAAA;AACL,QAAA,QAAA,QAAA,WAAA,EAAA,aAAA,OAAA,CAAA"}
1
+ {"version":3,"file":"analogjs-content.mjs","names":[],"sources":["../../src/lib/anchor-navigation.directive.ts","../../src/lib/utils/zone-wait-for.ts","../../src/lib/content.ts","../../src/lib/utils/escape-html.ts","../../src/lib/marked-setup.service.ts","../../src/lib/markdown-content-renderer.service.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","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 { 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 { parseRawContentFile } from './parse-raw-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, () => Promise<string>>,\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, () => Promise<string>> = {};\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 as () => Promise<string>;\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 | { default: string; metadata: Attributes }>(\n (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 },\n ).pipe(\n switchMap((contentFile) => {\n if (typeof contentFile === 'string') {\n const { content, attributes } =\n parseRawContentFile<Attributes>(contentFile);\n return 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 return of({\n filename: resolvedBase,\n slug,\n attributes: contentFile.metadata,\n content: contentFile.default,\n toc: [],\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","export function escapeHtml(value: string): string {\n return value\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&#39;');\n}\n\n/**\n * Reduces a fenced code-block info string to a safe language identifier so it\n * cannot break out of the `class=\"language-...\"` attribute it is interpolated\n * into.\n */\nexport function sanitizeLanguage(lang: string | undefined): string {\n return (lang ?? '').match(/^[a-zA-Z0-9-+#.]*/)?.[0] ?? '';\n}\n","/**\n * Credit goes to Scully for original implementation\n * https://github.com/scullyio/scully/blob/main/libs/scully/src/lib/fileHanderPlugins/markdown.ts\n */\nimport { inject, Injectable } from '@angular/core';\nimport { marked } from 'marked';\nimport { gfmHeadingId } from 'marked-gfm-heading-id';\nimport { mangle } from 'marked-mangle';\nimport { MarkedContentHighlighter } from './marked-content-highlighter';\nimport { escapeHtml, sanitizeLanguage } from './utils/escape-html';\n\n@Injectable()\nexport class MarkedSetupService {\n private readonly marked: typeof marked;\n private readonly highlighter = inject(MarkedContentHighlighter, {\n optional: true,\n });\n\n constructor() {\n const renderer = new marked.Renderer();\n renderer.code = ({ text, lang }) => {\n // Let's do a language based detection like on GitHub\n // So we can still have non-interpreted mermaid code\n const language = sanitizeLanguage(lang);\n if (language === 'mermaid') {\n return '<pre class=\"mermaid\">' + escapeHtml(text) + '</pre>';\n }\n\n if (!language) {\n return '<pre><code>' + escapeHtml(text) + '</code></pre>';\n }\n\n if (this.highlighter?.augmentCodeBlock) {\n return this.highlighter?.augmentCodeBlock(text, language);\n }\n\n return `<pre class=\"language-${language}\"><code class=\"language-${language}\">${escapeHtml(text)}</code></pre>`;\n };\n\n const extensions = [gfmHeadingId(), mangle()];\n\n if (this.highlighter) {\n extensions.push(this.highlighter.getHighlightExtension());\n }\n\n marked.use(...extensions, {\n renderer,\n pedantic: false,\n gfm: true,\n breaks: false,\n });\n\n this.marked = marked;\n }\n\n getMarkedInstance(): typeof marked {\n return this.marked;\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { getHeadingList } from 'marked-gfm-heading-id';\n\nimport {\n ContentRenderer,\n RenderedContent,\n TableOfContentItem,\n} from './content-renderer';\nimport { MarkedSetupService } from './marked-setup.service';\n\n@Injectable()\nexport class MarkdownContentRendererService implements ContentRenderer {\n #marked = inject(MarkedSetupService, { self: true });\n\n async render(content: string): Promise<RenderedContent> {\n const renderedContent = await this.#marked\n .getMarkedInstance()\n .parse(content);\n return {\n content: renderedContent,\n toc: getHeadingList(),\n };\n }\n\n getContentHeadings(content: string): TableOfContentItem[] {\n return [...content.matchAll(/^(#{1,6})\\s+(.+?)\\s*$/gm)].map((match) => ({\n id: match[2]\n .trim()\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .replace(/\\s+/g, '-'),\n level: match[1].length,\n text: match[2].trim(),\n }));\n }\n\n // eslint-disable-next-line\n enhance(): void {}\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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"}
@@ -187,7 +187,7 @@ var RenderTaskService = class RenderTaskService {
187
187
  static {
188
188
  this.ɵfac = i0.ɵɵngDeclareFactory({
189
189
  minVersion: "12.0.0",
190
- version: "22.0.0",
190
+ version: "22.1.5",
191
191
  ngImport: i0,
192
192
  type: RenderTaskService,
193
193
  deps: [],
@@ -197,7 +197,7 @@ var RenderTaskService = class RenderTaskService {
197
197
  static {
198
198
  this.ɵprov = i0.ɵɵngDeclareInjectable({
199
199
  minVersion: "12.0.0",
200
- version: "22.0.0",
200
+ version: "22.1.5",
201
201
  ngImport: i0,
202
202
  type: RenderTaskService
203
203
  });
@@ -205,7 +205,7 @@ var RenderTaskService = class RenderTaskService {
205
205
  };
206
206
  i0.ɵɵngDeclareClassMetadata({
207
207
  minVersion: "12.0.0",
208
- version: "22.0.0",
208
+ version: "22.1.5",
209
209
  ngImport: i0,
210
210
  type: RenderTaskService,
211
211
  decorators: [{ type: Injectable }]
@@ -1 +1 @@
1
- {"version":3,"file":"content-list-loader.mjs","names":["#pendingTasks"],"sources":["../../src/lib/content-locale.ts","../../src/lib/get-content-files.ts","../../src/lib/content-files-list-token.ts","../../src/lib/content-files-token.ts","../../src/lib/render-task.service.ts","../../src/lib/inject-content-files.ts","../../src/lib/content-file-loader.ts","../../src/lib/content-list-loader.ts"],"sourcesContent":["import { InjectionToken, inject, Provider } from '@angular/core';\n\nimport { ContentFile } from './content-file';\n\n/**\n * Token for the active content locale.\n * Provided via `withLocale()` in `provideContent()`.\n *\n * When set, `injectContentFiles()` filters to content matching this locale,\n * and `injectContent()` resolves locale-prefixed content paths first.\n */\nexport const CONTENT_LOCALE: InjectionToken<string> =\n new InjectionToken<string>('@analogjs/content Locale');\n\n/**\n * Injects the content locale, returning null if not configured.\n */\nexport function injectContentLocale(): string | null {\n return inject(CONTENT_LOCALE, { optional: true });\n}\n\nexport interface ContentLocaleOptions {\n /**\n * Function that returns the active locale.\n * Runs in injection context so `inject()` can be used to read\n * from other tokens (e.g., a LOCALE token from a router package).\n *\n * ```typescript\n * withLocale({ loadLocale: injectLocale })\n * withLocale({ loadLocale: () => navigator.language.split('-')[0] })\n * ```\n */\n loadLocale: () => string | null;\n}\n\n/**\n * Content feature that sets the active locale for content resolution.\n *\n * When provided, content APIs become locale-aware:\n * - `injectContentFiles()` filters to files in the locale subdirectory\n * or with a matching `locale` frontmatter attribute.\n * - `injectContent()` tries locale-prefixed paths first\n * (e.g., `content/fr/blog/post.md` before `content/blog/post.md`).\n *\n * Usage:\n * ```typescript\n * // With loader — runs in injection context\n * provideContent(\n * withMarkdownRenderer(),\n * withLocale({ loadLocale: injectLocale }),\n * )\n *\n * // Static locale\n * provideContent(\n * withMarkdownRenderer(),\n * withLocale('fr'),\n * )\n * ```\n */\nexport function withLocale(locale: string | ContentLocaleOptions): Provider {\n if (typeof locale === 'string') {\n return { provide: CONTENT_LOCALE, useValue: locale };\n }\n\n return { provide: CONTENT_LOCALE, useFactory: locale.loadLocale };\n}\n\n/**\n * Filters content files by locale using map-based key lookup.\n *\n * Matching rules:\n * 1. Frontmatter `locale` attribute matches the active locale.\n * 2. File is in the active locale subdirectory (e.g., `/content/fr/blog/post`).\n * 3. File has no locale marker and no localized variant exists — included as universal content.\n *\n * Files in a different locale's subdirectory are always excluded.\n */\nexport function filterByLocale<T extends Record<string, any>>(\n files: ContentFile<T>[],\n locale: string,\n): ContentFile<T>[] {\n const localePrefix = `/content/${locale}/`;\n\n // Collect all locale prefixes present in the file set\n const allLocalePrefixes = new Set<string>();\n for (const file of files) {\n const match = file.filename.match(/\\/content\\/([a-z]{2}(?:-[a-zA-Z]+)?)\\//);\n if (match) {\n allLocalePrefixes.add(`/content/${match[1]}/`);\n }\n }\n\n // Build set of base paths that have a localized variant for the active locale\n const localizedBasePaths = new Set<string>();\n for (const file of files) {\n if (file.filename.includes(localePrefix)) {\n localizedBasePaths.add(file.filename.replace(localePrefix, '/content/'));\n }\n }\n\n return files.filter((file) => {\n // Frontmatter locale attribute takes priority\n if (file.attributes['locale']) {\n return file.attributes['locale'] === locale;\n }\n // File is in the active locale subdirectory — include\n if (file.filename.includes(localePrefix)) {\n return true;\n }\n // File is in a different locale's subdirectory — exclude\n for (const prefix of allLocalePrefixes) {\n if (prefix !== localePrefix && file.filename.includes(prefix)) {\n return false;\n }\n }\n // Universal content — include only if no localized variant exists\n return !localizedBasePaths.has(file.filename);\n });\n}\n\n/**\n * Prepends locale-prefixed candidates before the standard candidates\n * for content file map key lookup.\n */\nexport function withLocaleCandidates(\n candidates: string[],\n locale: string | null | undefined,\n): string[] {\n if (!locale) {\n return candidates;\n }\n const localeCandidates = candidates.map((c) =>\n c.replace('/src/content/', `/src/content/${locale}/`),\n );\n return [...localeCandidates, ...candidates];\n}\n","/**\n * These bindings are rewritten at build time:\n * - `ANALOG_CONTENT_FILE_LIST` by `@analogjs/platform`'s content plugin,\n * which substitutes the empty-object initializer with the frontmatter\n * map for discovered content files.\n * - `ANALOG_CONTENT_ROUTE_FILES` by `@analogjs/platform`'s router plugin,\n * which substitutes the empty-object initializer with the lazy-import\n * map for discovered content files. The router plugin already rewrites\n * a same-named binding exported from `@analogjs/router/content`; it\n * matches this module for free because its filter keys off `_ROUTE_FILES`.\n *\n * They are declared as `export const` at module scope so the bundler treats\n * them as public module bindings and cannot constant-fold them away during\n * library build. Previously these lived as local consts inside each getter,\n * and Rolldown collapsed `const X = {}; return X;` into `return {};`, which\n * erased the rewrite target the platform plugins rely on.\n */\n\nexport const ANALOG_CONTENT_FILE_LIST = {};\nexport const ANALOG_CONTENT_ROUTE_FILES = {};\n\n/**\n * Returns the list of content files by filename with ?analog-content-list=true.\n * We use the query param to transform the return into an array of\n * just front matter attributes.\n */\nexport const getContentFilesList = () =>\n ANALOG_CONTENT_FILE_LIST as Record<string, Record<string, any>>;\n\n/**\n * Returns the lazy loaded content files for lookups.\n */\nexport const getContentFiles = () =>\n ANALOG_CONTENT_ROUTE_FILES as Record<string, () => Promise<string>>;\n","import { InjectionToken } from '@angular/core';\nimport { ContentFile } from './content-file';\nimport { getContentFilesList } from './get-content-files';\n\nfunction getSlug(filename: string) {\n // Extract the last path segment without its extension.\n // Handles names with dots like [[...slug]].md by stripping only the final extension.\n const lastSegment = (filename.split(/[/\\\\]/).pop() || '').trim();\n const base = lastSegment.replace(/\\.[^./\\\\]+$/, ''); // strip only the final extension\n // Treat index.md as index route => empty slug\n return base === 'index' ? '' : base;\n}\n\nexport const CONTENT_FILES_LIST_TOKEN: InjectionToken<ContentFile[]> =\n new InjectionToken<ContentFile[]>('@analogjs/content Content Files List', {\n providedIn: 'root',\n factory() {\n const contentFiles = getContentFilesList();\n\n return Object.keys(contentFiles).map((filename) => {\n const attributes = contentFiles[filename];\n const slug = attributes['slug'];\n\n return {\n filename,\n attributes,\n slug: slug ? encodeURI(slug) : encodeURI(getSlug(filename)),\n };\n });\n },\n });\n","import { InjectionToken, Signal, inject, signal } from '@angular/core';\n\nimport { getContentFiles } from './get-content-files';\nimport { CONTENT_FILES_LIST_TOKEN } from './content-files-list-token';\n\nexport const CONTENT_FILES_TOKEN: InjectionToken<\n Record<string, () => Promise<string>>\n> = new InjectionToken<Record<string, () => Promise<string>>>(\n '@analogjs/content Content Files',\n {\n providedIn: 'root',\n factory() {\n const contentFiles = getContentFiles();\n const allFiles = { ...contentFiles };\n const contentFilesList = inject(CONTENT_FILES_LIST_TOKEN);\n\n const lookup: Record<string, string> = {};\n contentFilesList.forEach((item) => {\n const contentFilename = item.filename.replace(\n /(.*?)\\/content/,\n '/src/content',\n );\n const fileParts = contentFilename.split('/');\n const filePath = fileParts.slice(0, fileParts.length - 1).join('/');\n const fileNameParts = fileParts[fileParts.length - 1].split('.');\n const ext = fileNameParts[fileNameParts.length - 1];\n let slug = (item.slug ?? '') as string;\n // Default empty slug to 'index'\n if (slug === '') {\n slug = 'index';\n }\n // Slugs with path separators are scoped to the file's top-level\n // subdirectory (e.g. `/src/content/docs`) rather than absolute root,\n // so nested files keep resolving through `subdirectory` lookups.\n // Files directly under `/src/content` keep the original root-relative\n // behavior since they have no subdirectory of their own.\n const subdirRoot =\n fileParts.length > 4\n ? fileParts.slice(0, 4).join('/')\n : '/src/content';\n const newBase = slug.includes('/')\n ? `${subdirRoot}/${slug}`\n : `${filePath}/${slug}`;\n lookup[contentFilename] = `${newBase}.${ext}`.replace(/\\/{2,}/g, '/');\n });\n\n const objectUsingSlugAttribute: Record<string, () => Promise<string>> =\n {};\n Object.entries(allFiles).forEach((entry) => {\n const filename = entry[0];\n const value = entry[1];\n const strippedFilename = filename.replace(\n /^\\/(.*?)\\/content/,\n '/src/content',\n );\n\n const newFilename = lookup[strippedFilename];\n if (newFilename !== undefined) {\n const objectFilename = newFilename.replace(\n /^\\/(.*?)\\/content/,\n '/src/content',\n );\n objectUsingSlugAttribute[objectFilename] =\n value as () => Promise<string>;\n }\n });\n\n return objectUsingSlugAttribute;\n },\n },\n);\n\nexport const CONTENT_FILES_MAP_TOKEN: InjectionToken<\n Signal<Record<string, () => Promise<string>>>\n> = new InjectionToken<Signal<Record<string, () => Promise<string>>>>(\n '@analogjs/content Content Files',\n {\n providedIn: 'root',\n factory() {\n return signal(inject(CONTENT_FILES_TOKEN));\n },\n },\n);\n","import { Injectable, inject } from '@angular/core';\nimport { ɵPendingTasksInternal as ɵPendingTasks } from '@angular/core';\n\n@Injectable()\nexport class RenderTaskService {\n #pendingTasks = inject(ɵPendingTasks);\n\n addRenderTask(): number {\n return this.#pendingTasks.add();\n }\n\n clearRenderTask(clear: number | (() => void)): void {\n if (typeof clear === 'function') {\n clear();\n } else if (typeof (this.#pendingTasks as any).remove === 'function') {\n (this.#pendingTasks as any).remove(clear);\n }\n }\n}\n","import { inject } from '@angular/core';\n\nimport { ContentFile } from './content-file';\nimport { CONTENT_FILES_LIST_TOKEN } from './content-files-list-token';\nimport { CONTENT_FILES_TOKEN } from './content-files-token';\nimport { CONTENT_LOCALE, filterByLocale } from './content-locale';\nimport { RenderTaskService } from './render-task.service';\n\nexport function injectContentFiles<Attributes extends Record<string, any>>(\n filterFn?: InjectContentFilesFilterFunction<Attributes>,\n): ContentFile<Attributes>[] {\n const renderTaskService = inject(RenderTaskService);\n const task = renderTaskService.addRenderTask();\n const allContentFiles = inject(\n CONTENT_FILES_LIST_TOKEN,\n ) as ContentFile<Attributes>[];\n const locale = inject(CONTENT_LOCALE, { optional: true });\n renderTaskService.clearRenderTask(task);\n\n let results = allContentFiles;\n\n if (locale) {\n results = filterByLocale(results, locale);\n }\n\n if (filterFn) {\n results = results.filter(filterFn);\n }\n\n return results;\n}\n\nexport type InjectContentFilesFilterFunction<T extends Record<string, any>> = (\n value: ContentFile<T>,\n index: number,\n array: ContentFile<T>[],\n) => boolean;\n\nexport function injectContentFilesMap(): Record<string, () => Promise<string>> {\n return inject(CONTENT_FILES_TOKEN);\n}\n","import { InjectionToken, Provider } from '@angular/core';\nimport { inject } from '@angular/core';\n\nimport { injectContentFilesMap } from './inject-content-files';\n\ntype ContentFileLoaderFunction = () => Promise<\n Record<string, () => Promise<string>>\n>;\n\nexport const CONTENT_FILE_LOADER: InjectionToken<ContentFileLoaderFunction> =\n new InjectionToken<ContentFileLoaderFunction>(\n '@analogjs/content/resource File Loader',\n );\n\nexport function injectContentFileLoader() {\n return inject(CONTENT_FILE_LOADER) as ContentFileLoaderFunction;\n}\n\nexport function withContentFileLoader(): Provider {\n return {\n provide: CONTENT_FILE_LOADER,\n useFactory() {\n return async () => injectContentFilesMap();\n },\n };\n}\n","import { InjectionToken, Provider } from '@angular/core';\nimport { inject } from '@angular/core';\n\nimport { ContentFile } from './content-file';\nimport { injectContentFiles } from './inject-content-files';\n\ntype ContentListLoaderFunction<Attributes extends Record<string, any>> =\n () => Promise<ContentFile<Attributes>[]>;\n\nexport const CONTENT_LIST_LOADER: InjectionToken<\n ContentListLoaderFunction<any>\n> = new InjectionToken<ContentListLoaderFunction<any>>(\n '@analogjs/content/resource List Loader',\n);\n\nexport function injectContentListLoader<\n Attributes extends Record<string, any>,\n>() {\n return inject(CONTENT_LIST_LOADER) as ContentListLoaderFunction<Attributes>;\n}\n\nexport function withContentListLoader(): Provider {\n return {\n provide: CONTENT_LIST_LOADER,\n useFactory() {\n return async () => injectContentFiles();\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"content-list-loader.mjs","names":[],"sources":["../../src/lib/content-locale.ts","../../src/lib/get-content-files.ts","../../src/lib/content-files-list-token.ts","../../src/lib/content-files-token.ts","../../src/lib/render-task.service.ts","../../src/lib/inject-content-files.ts","../../src/lib/content-file-loader.ts","../../src/lib/content-list-loader.ts"],"sourcesContent":["import { InjectionToken, inject, Provider } from '@angular/core';\n\nimport { ContentFile } from './content-file';\n\n/**\n * Token for the active content locale.\n * Provided via `withLocale()` in `provideContent()`.\n *\n * When set, `injectContentFiles()` filters to content matching this locale,\n * and `injectContent()` resolves locale-prefixed content paths first.\n */\nexport const CONTENT_LOCALE: InjectionToken<string> =\n new InjectionToken<string>('@analogjs/content Locale');\n\n/**\n * Injects the content locale, returning null if not configured.\n */\nexport function injectContentLocale(): string | null {\n return inject(CONTENT_LOCALE, { optional: true });\n}\n\nexport interface ContentLocaleOptions {\n /**\n * Function that returns the active locale.\n * Runs in injection context so `inject()` can be used to read\n * from other tokens (e.g., a LOCALE token from a router package).\n *\n * ```typescript\n * withLocale({ loadLocale: injectLocale })\n * withLocale({ loadLocale: () => navigator.language.split('-')[0] })\n * ```\n */\n loadLocale: () => string | null;\n}\n\n/**\n * Content feature that sets the active locale for content resolution.\n *\n * When provided, content APIs become locale-aware:\n * - `injectContentFiles()` filters to files in the locale subdirectory\n * or with a matching `locale` frontmatter attribute.\n * - `injectContent()` tries locale-prefixed paths first\n * (e.g., `content/fr/blog/post.md` before `content/blog/post.md`).\n *\n * Usage:\n * ```typescript\n * // With loader — runs in injection context\n * provideContent(\n * withMarkdownRenderer(),\n * withLocale({ loadLocale: injectLocale }),\n * )\n *\n * // Static locale\n * provideContent(\n * withMarkdownRenderer(),\n * withLocale('fr'),\n * )\n * ```\n */\nexport function withLocale(locale: string | ContentLocaleOptions): Provider {\n if (typeof locale === 'string') {\n return { provide: CONTENT_LOCALE, useValue: locale };\n }\n\n return { provide: CONTENT_LOCALE, useFactory: locale.loadLocale };\n}\n\n/**\n * Filters content files by locale using map-based key lookup.\n *\n * Matching rules:\n * 1. Frontmatter `locale` attribute matches the active locale.\n * 2. File is in the active locale subdirectory (e.g., `/content/fr/blog/post`).\n * 3. File has no locale marker and no localized variant exists — included as universal content.\n *\n * Files in a different locale's subdirectory are always excluded.\n */\nexport function filterByLocale<T extends Record<string, any>>(\n files: ContentFile<T>[],\n locale: string,\n): ContentFile<T>[] {\n const localePrefix = `/content/${locale}/`;\n\n // Collect all locale prefixes present in the file set\n const allLocalePrefixes = new Set<string>();\n for (const file of files) {\n const match = file.filename.match(/\\/content\\/([a-z]{2}(?:-[a-zA-Z]+)?)\\//);\n if (match) {\n allLocalePrefixes.add(`/content/${match[1]}/`);\n }\n }\n\n // Build set of base paths that have a localized variant for the active locale\n const localizedBasePaths = new Set<string>();\n for (const file of files) {\n if (file.filename.includes(localePrefix)) {\n localizedBasePaths.add(file.filename.replace(localePrefix, '/content/'));\n }\n }\n\n return files.filter((file) => {\n // Frontmatter locale attribute takes priority\n if (file.attributes['locale']) {\n return file.attributes['locale'] === locale;\n }\n // File is in the active locale subdirectory — include\n if (file.filename.includes(localePrefix)) {\n return true;\n }\n // File is in a different locale's subdirectory — exclude\n for (const prefix of allLocalePrefixes) {\n if (prefix !== localePrefix && file.filename.includes(prefix)) {\n return false;\n }\n }\n // Universal content — include only if no localized variant exists\n return !localizedBasePaths.has(file.filename);\n });\n}\n\n/**\n * Prepends locale-prefixed candidates before the standard candidates\n * for content file map key lookup.\n */\nexport function withLocaleCandidates(\n candidates: string[],\n locale: string | null | undefined,\n): string[] {\n if (!locale) {\n return candidates;\n }\n const localeCandidates = candidates.map((c) =>\n c.replace('/src/content/', `/src/content/${locale}/`),\n );\n return [...localeCandidates, ...candidates];\n}\n","/**\n * These bindings are rewritten at build time:\n * - `ANALOG_CONTENT_FILE_LIST` by `@analogjs/platform`'s content plugin,\n * which substitutes the empty-object initializer with the frontmatter\n * map for discovered content files.\n * - `ANALOG_CONTENT_ROUTE_FILES` by `@analogjs/platform`'s router plugin,\n * which substitutes the empty-object initializer with the lazy-import\n * map for discovered content files. The router plugin already rewrites\n * a same-named binding exported from `@analogjs/router/content`; it\n * matches this module for free because its filter keys off `_ROUTE_FILES`.\n *\n * They are declared as `export const` at module scope so the bundler treats\n * them as public module bindings and cannot constant-fold them away during\n * library build. Previously these lived as local consts inside each getter,\n * and Rolldown collapsed `const X = {}; return X;` into `return {};`, which\n * erased the rewrite target the platform plugins rely on.\n */\n\nexport const ANALOG_CONTENT_FILE_LIST = {};\nexport const ANALOG_CONTENT_ROUTE_FILES = {};\n\n/**\n * Returns the list of content files by filename with ?analog-content-list=true.\n * We use the query param to transform the return into an array of\n * just front matter attributes.\n */\nexport const getContentFilesList = () =>\n ANALOG_CONTENT_FILE_LIST as Record<string, Record<string, any>>;\n\n/**\n * Returns the lazy loaded content files for lookups.\n */\nexport const getContentFiles = () =>\n ANALOG_CONTENT_ROUTE_FILES as Record<string, () => Promise<string>>;\n","import { InjectionToken } from '@angular/core';\nimport { ContentFile } from './content-file';\nimport { getContentFilesList } from './get-content-files';\n\nfunction getSlug(filename: string) {\n // Extract the last path segment without its extension.\n // Handles names with dots like [[...slug]].md by stripping only the final extension.\n const lastSegment = (filename.split(/[/\\\\]/).pop() || '').trim();\n const base = lastSegment.replace(/\\.[^./\\\\]+$/, ''); // strip only the final extension\n // Treat index.md as index route => empty slug\n return base === 'index' ? '' : base;\n}\n\nexport const CONTENT_FILES_LIST_TOKEN: InjectionToken<ContentFile[]> =\n new InjectionToken<ContentFile[]>('@analogjs/content Content Files List', {\n providedIn: 'root',\n factory() {\n const contentFiles = getContentFilesList();\n\n return Object.keys(contentFiles).map((filename) => {\n const attributes = contentFiles[filename];\n const slug = attributes['slug'];\n\n return {\n filename,\n attributes,\n slug: slug ? encodeURI(slug) : encodeURI(getSlug(filename)),\n };\n });\n },\n });\n","import { InjectionToken, Signal, inject, signal } from '@angular/core';\n\nimport { getContentFiles } from './get-content-files';\nimport { CONTENT_FILES_LIST_TOKEN } from './content-files-list-token';\n\nexport const CONTENT_FILES_TOKEN: InjectionToken<\n Record<string, () => Promise<string>>\n> = new InjectionToken<Record<string, () => Promise<string>>>(\n '@analogjs/content Content Files',\n {\n providedIn: 'root',\n factory() {\n const contentFiles = getContentFiles();\n const allFiles = { ...contentFiles };\n const contentFilesList = inject(CONTENT_FILES_LIST_TOKEN);\n\n const lookup: Record<string, string> = {};\n contentFilesList.forEach((item) => {\n const contentFilename = item.filename.replace(\n /(.*?)\\/content/,\n '/src/content',\n );\n const fileParts = contentFilename.split('/');\n const filePath = fileParts.slice(0, fileParts.length - 1).join('/');\n const fileNameParts = fileParts[fileParts.length - 1].split('.');\n const ext = fileNameParts[fileNameParts.length - 1];\n let slug = (item.slug ?? '') as string;\n // Default empty slug to 'index'\n if (slug === '') {\n slug = 'index';\n }\n // Slugs with path separators are scoped to the file's top-level\n // subdirectory (e.g. `/src/content/docs`) rather than absolute root,\n // so nested files keep resolving through `subdirectory` lookups.\n // Files directly under `/src/content` keep the original root-relative\n // behavior since they have no subdirectory of their own.\n const subdirRoot =\n fileParts.length > 4\n ? fileParts.slice(0, 4).join('/')\n : '/src/content';\n const newBase = slug.includes('/')\n ? `${subdirRoot}/${slug}`\n : `${filePath}/${slug}`;\n lookup[contentFilename] = `${newBase}.${ext}`.replace(/\\/{2,}/g, '/');\n });\n\n const objectUsingSlugAttribute: Record<string, () => Promise<string>> =\n {};\n Object.entries(allFiles).forEach((entry) => {\n const filename = entry[0];\n const value = entry[1];\n const strippedFilename = filename.replace(\n /^\\/(.*?)\\/content/,\n '/src/content',\n );\n\n const newFilename = lookup[strippedFilename];\n if (newFilename !== undefined) {\n const objectFilename = newFilename.replace(\n /^\\/(.*?)\\/content/,\n '/src/content',\n );\n objectUsingSlugAttribute[objectFilename] =\n value as () => Promise<string>;\n }\n });\n\n return objectUsingSlugAttribute;\n },\n },\n);\n\nexport const CONTENT_FILES_MAP_TOKEN: InjectionToken<\n Signal<Record<string, () => Promise<string>>>\n> = new InjectionToken<Signal<Record<string, () => Promise<string>>>>(\n '@analogjs/content Content Files',\n {\n providedIn: 'root',\n factory() {\n return signal(inject(CONTENT_FILES_TOKEN));\n },\n },\n);\n","import { Injectable, inject } from '@angular/core';\nimport { ɵPendingTasksInternal as ɵPendingTasks } from '@angular/core';\n\n@Injectable()\nexport class RenderTaskService {\n #pendingTasks = inject(ɵPendingTasks);\n\n addRenderTask(): number {\n return this.#pendingTasks.add();\n }\n\n clearRenderTask(clear: number | (() => void)): void {\n if (typeof clear === 'function') {\n clear();\n } else if (typeof (this.#pendingTasks as any).remove === 'function') {\n (this.#pendingTasks as any).remove(clear);\n }\n }\n}\n","import { inject } from '@angular/core';\n\nimport { ContentFile } from './content-file';\nimport { CONTENT_FILES_LIST_TOKEN } from './content-files-list-token';\nimport { CONTENT_FILES_TOKEN } from './content-files-token';\nimport { CONTENT_LOCALE, filterByLocale } from './content-locale';\nimport { RenderTaskService } from './render-task.service';\n\nexport function injectContentFiles<Attributes extends Record<string, any>>(\n filterFn?: InjectContentFilesFilterFunction<Attributes>,\n): ContentFile<Attributes>[] {\n const renderTaskService = inject(RenderTaskService);\n const task = renderTaskService.addRenderTask();\n const allContentFiles = inject(\n CONTENT_FILES_LIST_TOKEN,\n ) as ContentFile<Attributes>[];\n const locale = inject(CONTENT_LOCALE, { optional: true });\n renderTaskService.clearRenderTask(task);\n\n let results = allContentFiles;\n\n if (locale) {\n results = filterByLocale(results, locale);\n }\n\n if (filterFn) {\n results = results.filter(filterFn);\n }\n\n return results;\n}\n\nexport type InjectContentFilesFilterFunction<T extends Record<string, any>> = (\n value: ContentFile<T>,\n index: number,\n array: ContentFile<T>[],\n) => boolean;\n\nexport function injectContentFilesMap(): Record<string, () => Promise<string>> {\n return inject(CONTENT_FILES_TOKEN);\n}\n","import { InjectionToken, Provider } from '@angular/core';\nimport { inject } from '@angular/core';\n\nimport { injectContentFilesMap } from './inject-content-files';\n\ntype ContentFileLoaderFunction = () => Promise<\n Record<string, () => Promise<string>>\n>;\n\nexport const CONTENT_FILE_LOADER: InjectionToken<ContentFileLoaderFunction> =\n new InjectionToken<ContentFileLoaderFunction>(\n '@analogjs/content/resource File Loader',\n );\n\nexport function injectContentFileLoader() {\n return inject(CONTENT_FILE_LOADER) as ContentFileLoaderFunction;\n}\n\nexport function withContentFileLoader(): Provider {\n return {\n provide: CONTENT_FILE_LOADER,\n useFactory() {\n return async () => injectContentFilesMap();\n },\n };\n}\n","import { InjectionToken, Provider } from '@angular/core';\nimport { inject } from '@angular/core';\n\nimport { ContentFile } from './content-file';\nimport { injectContentFiles } from './inject-content-files';\n\ntype ContentListLoaderFunction<Attributes extends Record<string, any>> =\n () => Promise<ContentFile<Attributes>[]>;\n\nexport const CONTENT_LIST_LOADER: InjectionToken<\n ContentListLoaderFunction<any>\n> = new InjectionToken<ContentListLoaderFunction<any>>(\n '@analogjs/content/resource List Loader',\n);\n\nexport function injectContentListLoader<\n Attributes extends Record<string, any>,\n>() {\n return inject(CONTENT_LIST_LOADER) as ContentListLoaderFunction<Attributes>;\n}\n\nexport function withContentListLoader(): Provider {\n return {\n provide: CONTENT_LIST_LOADER,\n useFactory() {\n return async () => injectContentFiles();\n },\n };\n}\n"],"mappings":"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"}
@@ -15,7 +15,7 @@ var ContentRenderer = class ContentRenderer {
15
15
  static {
16
16
  this.ɵfac = i0.ɵɵngDeclareFactory({
17
17
  minVersion: "12.0.0",
18
- version: "22.0.0",
18
+ version: "22.1.5",
19
19
  ngImport: i0,
20
20
  type: ContentRenderer,
21
21
  deps: [],
@@ -25,7 +25,7 @@ var ContentRenderer = class ContentRenderer {
25
25
  static {
26
26
  this.ɵprov = i0.ɵɵngDeclareInjectable({
27
27
  minVersion: "12.0.0",
28
- version: "22.0.0",
28
+ version: "22.1.5",
29
29
  ngImport: i0,
30
30
  type: ContentRenderer
31
31
  });
@@ -33,7 +33,7 @@ var ContentRenderer = class ContentRenderer {
33
33
  };
34
34
  i0.ɵɵngDeclareClassMetadata({
35
35
  minVersion: "12.0.0",
36
- version: "22.0.0",
36
+ version: "22.1.5",
37
37
  ngImport: i0,
38
38
  type: ContentRenderer,
39
39
  decorators: [{ type: Injectable }]
@@ -1 +1 @@
1
- {"version":3,"file":"content-renderer.mjs","names":[],"sources":["../../src/lib/content-renderer.ts"],"sourcesContent":["/// <reference types=\"vite/client\" />\n\nimport { Injectable, TransferState, inject, makeStateKey } from '@angular/core';\n\nexport type TableOfContentItem = {\n id: string;\n level: number; // starts at 1\n text: string;\n};\n\nexport type RenderedContent = {\n content: string;\n toc: TableOfContentItem[];\n};\n\n@Injectable()\nexport abstract class ContentRenderer {\n async render(content: string): Promise<RenderedContent> {\n return { content, toc: [] };\n }\n\n // Backward-compatible API for consumers that read headings directly.\n getContentHeadings(_content: string): TableOfContentItem[] {\n return [];\n }\n\n // eslint-disable-next-line\n enhance(): void {}\n}\n\nexport class NoopContentRenderer implements ContentRenderer {\n private readonly transferState = inject(TransferState);\n private contentId = 0;\n\n /**\n * Generates a hash from the content string\n * to be used with the transfer state\n */\n private generateHash(str: string) {\n let hash = 0;\n for (let i = 0, len = str.length; i < len; i++) {\n const chr = str.charCodeAt(i);\n hash = (hash << 5) - hash + chr;\n hash |= 0; // Convert to 32bit integer\n }\n return hash;\n }\n\n async render(content: string): Promise<RenderedContent> {\n this.contentId = this.generateHash(content);\n const toc = this.getContentHeadings(content);\n const key = makeStateKey<TableOfContentItem[]>(\n `content-headings-${this.contentId}`,\n );\n\n if (import.meta.env.SSR === true) {\n this.transferState.set(key, toc);\n return { content, toc };\n }\n\n return {\n content,\n toc: this.transferState.get(key, toc),\n };\n }\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n enhance(): void {}\n\n getContentHeadings(content: string): TableOfContentItem[] {\n return this.extractHeadings(content);\n }\n\n private extractHeadings(content: string): TableOfContentItem[] {\n const markdownHeadings = this.extractHeadingsFromMarkdown(content);\n if (markdownHeadings.length > 0) {\n return markdownHeadings;\n }\n\n const htmlHeadings = this.extractHeadingsFromHtml(content);\n return htmlHeadings;\n }\n\n private extractHeadingsFromMarkdown(content: string): TableOfContentItem[] {\n const lines = content.split('\\n');\n const toc: TableOfContentItem[] = [];\n const slugCounts = new Map<string, number>();\n\n for (const line of lines) {\n const match = /^(#{1,6})\\s+(.+?)\\s*$/.exec(line);\n if (!match) {\n continue;\n }\n\n const level = match[1].length;\n const text = match[2].trim();\n if (!text) {\n continue;\n }\n\n const baseSlug = text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .trim()\n .replace(/\\s+/g, '-');\n const count = slugCounts.get(baseSlug) ?? 0;\n slugCounts.set(baseSlug, count + 1);\n const id = count === 0 ? baseSlug : `${baseSlug}-${count}`;\n\n toc.push({ id, level, text });\n }\n\n return toc;\n }\n\n private extractHeadingsFromHtml(content: string): TableOfContentItem[] {\n const toc: TableOfContentItem[] = [];\n const slugCounts = new Map<string, number>();\n const headingRegex = /<h([1-6])([^>]*)>([\\s\\S]*?)<\\/h\\1>/gi;\n\n for (const match of content.matchAll(headingRegex)) {\n const level = Number(match[1]);\n const attrs = match[2] ?? '';\n const rawInner = match[3] ?? '';\n const text = rawInner.replace(/<[^>]+>/g, '').trim();\n if (!text) {\n continue;\n }\n\n const idMatch =\n /\\sid=(['\"])(.*?)\\1/i.exec(attrs) ?? /\\sid=([^\\s>]+)/i.exec(attrs);\n let id = idMatch?.[2] ?? idMatch?.[1] ?? '';\n if (!id) {\n id = this.makeSlug(text, slugCounts);\n }\n\n toc.push({ id, level, text });\n }\n\n return toc;\n }\n\n private makeSlug(text: string, slugCounts: Map<string, number>): string {\n const baseSlug = text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .trim()\n .replace(/\\s+/g, '-');\n const count = slugCounts.get(baseSlug) ?? 0;\n slugCounts.set(baseSlug, count + 1);\n return count === 0 ? baseSlug : `${baseSlug}-${count}`;\n }\n}\n"],"mappings":";;;IAiBQ,kBAAA,MAAA,gBAAkD;CACtD,MAAO,OAAA,SAAA;AAAE,SAAA;GAAA;GAAA,KAAA,EAAA;GAAA;;;AAIX,SAAA,EAAA;;CAKA,UAAgB;;;;;;;;;;;;kBAZjB,sBAAY;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAA,CAAA;;;AAeb,GAAA,yBAAa;CAAA,YAA+C;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAA,YAAA,CAAA,EAAA,MAAA,YAAA,CAAA;;gCAEtC;;;;;;;;;CAShB,aAAY,KAAI;EAChB,IAAQ,OAAQ;AAChB,OAAQ,IAAA,IAAA,GAAA,MAAA,IAAA,QAAA,IAAA,KAAA,KAAA;;AAEH,WAAA,QAAA,KAAA,OAAA;;;AAIF,SAAA;;CAEL,MAAM,OAAM,SAAA;AAIR,OAAO,YAAS,KAAQ,aAAM,QAAA;EAC3B,MAAA,MAAA,KAAc,mBAAa,QAAA;EAChC,MAAO,MAAA,aAAA,oBAAA,KAAA,YAAA;AAGT,SAAO;GACL;GACK,KAAK,KAAA,cAAuB,IAAA,KAAA,IAAA;GAClC;;CAKH,UAAA;CACE,mBAAY,SAAgB;;;CAI5B,gBAAM,SAAmB;EACrB,MAAA,mBAA0B,KAAG,4BAAA,QAAA;AAC/B,MAAO,iBAAA,SAAA,EAAA,QAAA;SAIF,KAAA,wBAAA,QAAA;;CAIP,4BAA4B,SAAK;EAC3B,MAA4B,QAAE,QAAA,MAAA,KAAA;EAC9B,MAAA,MAAA,EAAa;EAEd,MAAM,6BAAQ,IAAO,KAAA;AACxB,OAAM,MAAQ,QAAA,OAAA;GACT,MAAO,QAAA,wBAAA,KAAA,KAAA;AACV,OAAA,CAAA,MAAA;GAII,MAAO,QAAS,MAAM,GAAA;GACvB,MAAM,OAAA,MAAA,GAAA,MAAA;AACT,OAAA,CAAA,KAAA;GAQI,MAAQ,WAAW,KACd,aAAc,CACd,QAAU,aAAI,GAAW,CAE3B,MAAA,CAAE,QAAA,QAAA,IAAA;GAAI,MAAA,QAAA,WAAA,IAAA,SAAA,IAAA;AAAO,cAAA,IAAA,UAAA,QAAA,EAAA;GAAO,MAAA,KAAA,UAAA,IAAA,WAAA,GAAA,SAAA,GAAA;;;;;;;;;CAO/B,wBAAoC,SAAA;EAC9B,MAAA,MAAA,EAAa;EACb,MAAA,6BAAe,IAAA,KAAA;AAGnB,OAAM,MAAQ,SAAO,QAAS,SADZ,uCACY,EAAA;GACxB,MAAQ,QAAM,OAAM,MAAA,GAAA;GACpB,MAAA,QAAiB,MAAM,MAAA;GAExB,MAAM,QADW,MAAQ,MAAA,IACnB,QAAA,YAAA,GAAA,CAAA,MAAA;AACT,OAAA,CAAA,KAAA;GAKO,MAAA,UAAgB,sBAAgB,KAAA,MAAA,IAAA,kBAAA,KAAA,MAAA;GAChC,IAAA,KAAA,UAAA,MAAA,UAAA,MAAA;AACF,OAAK,CAAA,GAAA,MAAA,KAAA,SAAA,MAAA,WAAA;AAGD,OAAA,KAAA;IAAA;IAAA;IAAA;IAAA,CAAA;;AAAW,SAAA;;;EAGxB,MAAO,WAAA,KAAA,aAAA,CAGQ,QAAc,aAAyC,GAAA,CAChE,MAAW,CAKX,QAAQ,QAAW,IAAI;EAC7B,MAAW,QAAI,WAAU,IAAU,SAAA,IAAA;AACnC,aAAiB,IAAI,UAAA,QAAc,EAAA"}
1
+ {"version":3,"file":"content-renderer.mjs","names":[],"sources":["../../src/lib/content-renderer.ts"],"sourcesContent":["/// <reference types=\"vite/client\" />\n\nimport { Injectable, TransferState, inject, makeStateKey } from '@angular/core';\n\nexport type TableOfContentItem = {\n id: string;\n level: number; // starts at 1\n text: string;\n};\n\nexport type RenderedContent = {\n content: string;\n toc: TableOfContentItem[];\n};\n\n@Injectable()\nexport abstract class ContentRenderer {\n async render(content: string): Promise<RenderedContent> {\n return { content, toc: [] };\n }\n\n // Backward-compatible API for consumers that read headings directly.\n getContentHeadings(_content: string): TableOfContentItem[] {\n return [];\n }\n\n // eslint-disable-next-line\n enhance(): void {}\n}\n\nexport class NoopContentRenderer implements ContentRenderer {\n private readonly transferState = inject(TransferState);\n private contentId = 0;\n\n /**\n * Generates a hash from the content string\n * to be used with the transfer state\n */\n private generateHash(str: string) {\n let hash = 0;\n for (let i = 0, len = str.length; i < len; i++) {\n const chr = str.charCodeAt(i);\n hash = (hash << 5) - hash + chr;\n hash |= 0; // Convert to 32bit integer\n }\n return hash;\n }\n\n async render(content: string): Promise<RenderedContent> {\n this.contentId = this.generateHash(content);\n const toc = this.getContentHeadings(content);\n const key = makeStateKey<TableOfContentItem[]>(\n `content-headings-${this.contentId}`,\n );\n\n if (import.meta.env.SSR === true) {\n this.transferState.set(key, toc);\n return { content, toc };\n }\n\n return {\n content,\n toc: this.transferState.get(key, toc),\n };\n }\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n enhance(): void {}\n\n getContentHeadings(content: string): TableOfContentItem[] {\n return this.extractHeadings(content);\n }\n\n private extractHeadings(content: string): TableOfContentItem[] {\n const markdownHeadings = this.extractHeadingsFromMarkdown(content);\n if (markdownHeadings.length > 0) {\n return markdownHeadings;\n }\n\n const htmlHeadings = this.extractHeadingsFromHtml(content);\n return htmlHeadings;\n }\n\n private extractHeadingsFromMarkdown(content: string): TableOfContentItem[] {\n const lines = content.split('\\n');\n const toc: TableOfContentItem[] = [];\n const slugCounts = new Map<string, number>();\n\n for (const line of lines) {\n const match = /^(#{1,6})\\s+(.+?)\\s*$/.exec(line);\n if (!match) {\n continue;\n }\n\n const level = match[1].length;\n const text = match[2].trim();\n if (!text) {\n continue;\n }\n\n const baseSlug = text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .trim()\n .replace(/\\s+/g, '-');\n const count = slugCounts.get(baseSlug) ?? 0;\n slugCounts.set(baseSlug, count + 1);\n const id = count === 0 ? baseSlug : `${baseSlug}-${count}`;\n\n toc.push({ id, level, text });\n }\n\n return toc;\n }\n\n private extractHeadingsFromHtml(content: string): TableOfContentItem[] {\n const toc: TableOfContentItem[] = [];\n const slugCounts = new Map<string, number>();\n const headingRegex = /<h([1-6])([^>]*)>([\\s\\S]*?)<\\/h\\1>/gi;\n\n for (const match of content.matchAll(headingRegex)) {\n const level = Number(match[1]);\n const attrs = match[2] ?? '';\n const rawInner = match[3] ?? '';\n const text = rawInner.replace(/<[^>]+>/g, '').trim();\n if (!text) {\n continue;\n }\n\n const idMatch =\n /\\sid=(['\"])(.*?)\\1/i.exec(attrs) ?? /\\sid=([^\\s>]+)/i.exec(attrs);\n let id = idMatch?.[2] ?? idMatch?.[1] ?? '';\n if (!id) {\n id = this.makeSlug(text, slugCounts);\n }\n\n toc.push({ id, level, text });\n }\n\n return toc;\n }\n\n private makeSlug(text: string, slugCounts: Map<string, number>): string {\n const baseSlug = text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .trim()\n .replace(/\\s+/g, '-');\n const count = slugCounts.get(baseSlug) ?? 0;\n slugCounts.set(baseSlug, count + 1);\n return count === 0 ? baseSlug : `${baseSlug}-${count}`;\n }\n}\n"],"mappings":"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"}
@@ -5,7 +5,7 @@ var MarkedContentHighlighter = class MarkedContentHighlighter {
5
5
  static {
6
6
  this.ɵfac = i0.ɵɵngDeclareFactory({
7
7
  minVersion: "12.0.0",
8
- version: "22.0.0",
8
+ version: "22.1.5",
9
9
  ngImport: i0,
10
10
  type: MarkedContentHighlighter,
11
11
  deps: [],
@@ -15,7 +15,7 @@ var MarkedContentHighlighter = class MarkedContentHighlighter {
15
15
  static {
16
16
  this.ɵprov = i0.ɵɵngDeclareInjectable({
17
17
  minVersion: "12.0.0",
18
- version: "22.0.0",
18
+ version: "22.1.5",
19
19
  ngImport: i0,
20
20
  type: MarkedContentHighlighter
21
21
  });
@@ -23,7 +23,7 @@ var MarkedContentHighlighter = class MarkedContentHighlighter {
23
23
  };
24
24
  i0.ɵɵngDeclareClassMetadata({
25
25
  minVersion: "12.0.0",
26
- version: "22.0.0",
26
+ version: "22.1.5",
27
27
  ngImport: i0,
28
28
  type: MarkedContentHighlighter,
29
29
  decorators: [{ type: Injectable }]
@@ -1 +1 @@
1
- {"version":3,"file":"marked-content-highlighter.mjs","names":[],"sources":["../../src/lib/marked-content-highlighter.ts"],"sourcesContent":["import {\n AbstractType,\n Injectable,\n Provider,\n ProviderToken,\n Type,\n} from '@angular/core';\n\n@Injectable()\nexport abstract class MarkedContentHighlighter {\n augmentCodeBlock?(code: string, lang: string): string;\n abstract getHighlightExtension(): import('marked').MarkedExtension;\n}\n\nexport function withHighlighter(\n provider: (\n | { useValue: MarkedContentHighlighter }\n | {\n useClass:\n | Type<MarkedContentHighlighter>\n | AbstractType<MarkedContentHighlighter>;\n }\n | { useFactory: (...deps: any[]) => MarkedContentHighlighter }\n ) & { deps?: ProviderToken<any>[] },\n): Provider {\n return { provide: MarkedContentHighlighter, ...provider } as Provider;\n}\n"],"mappings":";;;AASO,IAAA,2BAAA,MAAA,yBAAe;;oCADrB;GAAA,YAAY;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAA,MAAA,EAAA;GAAA,QAAA,GAAA,gBAAA;GAAA,CAAA;;;;;;;;;;;AAiBX,GAAA,yBAAO;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAA,YAAA,CAAA,EAAW,MAAA,YAA6B,CAAA;CAAA,CAAA;SAAU,gBAAA,UAAA"}
1
+ {"version":3,"file":"marked-content-highlighter.mjs","names":[],"sources":["../../src/lib/marked-content-highlighter.ts"],"sourcesContent":["import {\n AbstractType,\n Injectable,\n Provider,\n ProviderToken,\n Type,\n} from '@angular/core';\n\n@Injectable()\nexport abstract class MarkedContentHighlighter {\n augmentCodeBlock?(code: string, lang: string): string;\n abstract getHighlightExtension(): import('marked').MarkedExtension;\n}\n\nexport function withHighlighter(\n provider: (\n | { useValue: MarkedContentHighlighter }\n | {\n useClass:\n | Type<MarkedContentHighlighter>\n | AbstractType<MarkedContentHighlighter>;\n }\n | { useFactory: (...deps: any[]) => MarkedContentHighlighter }\n ) & { deps?: ProviderToken<any>[] },\n): Provider {\n return { provide: MarkedContentHighlighter, ...provider } as Provider;\n}\n"],"mappings":";;;AASA,IAAsB,2BAAtB,MAAsB,yBAAwB;;;;;;SAAxB;GAAwB,MAAA,EAAA;GAAA,QAAA,GAAA,gBAAA;GAAA,CAAA;;;;;;;SAAxB;GAAwB,CAAA;;;;;;;OAAxB;CAAwB,YAAA,CAAA,EAAA,MAD7C,YAAA,CAAA;;AAMD,SAAgB,gBACd,UAQmC;AAEnC,QAAO;EAAE,SAAS;EAA0B,GAAG;EAAsB"}
@@ -1 +1 @@
1
- {"version":3,"file":"parse-raw-content-file.mjs","names":[],"sources":["../../src/lib/parse-raw-content-file.ts"],"sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec';\nimport fm from 'front-matter';\n\nexport class FrontmatterValidationError extends Error {\n constructor(\n public readonly issues: ReadonlyArray<StandardSchemaV1.Issue>,\n public readonly filename?: string,\n ) {\n const issueMessages = issues\n .map((i) => {\n const path = i.path\n ? ` at \"${i.path.map((p) => (typeof p === 'object' ? p.key : p)).join('.')}\"`\n : '';\n return ` - ${i.message}${path}`;\n })\n .join('\\n');\n const prefix = filename ? `\"${filename}\" f` : 'F';\n super(`${prefix}rontmatter validation failed:\\n${issueMessages}`);\n this.name = 'FrontmatterValidationError';\n }\n}\n\nexport function parseRawContentFile<\n Attributes extends Record<string, any> = Record<string, any>,\n>(rawContentFile: string): { content: string; attributes: Attributes };\n\nexport function parseRawContentFile<TSchema extends StandardSchemaV1>(\n rawContentFile: string,\n schema: TSchema,\n filename?: string,\n): { content: string; attributes: StandardSchemaV1.InferOutput<TSchema> };\n\nexport function parseRawContentFile(\n rawContentFile: string,\n schema?: StandardSchemaV1,\n filename?: string,\n): { content: string; attributes: unknown } {\n const { body, attributes } = fm(rawContentFile);\n\n if (schema) {\n const result = schema['~standard'].validate(attributes);\n if (\n result != null &&\n typeof (result as PromiseLike<unknown>).then === 'function'\n ) {\n throw new Error(\n 'parseRawContentFile does not support async schema validation. ' +\n 'Use parseRawContentFileAsync() for async schemas.',\n );\n }\n const syncResult = result as StandardSchemaV1.Result<\n StandardSchemaV1.InferOutput<typeof schema>\n >;\n if (syncResult.issues) {\n throw new FrontmatterValidationError(syncResult.issues, filename);\n }\n return { content: body, attributes: syncResult.value };\n }\n\n return { content: body, attributes };\n}\n\nexport async function parseRawContentFileAsync<\n TSchema extends StandardSchemaV1,\n>(\n rawContentFile: string,\n schema: TSchema,\n filename?: string,\n): Promise<{\n content: string;\n attributes: StandardSchemaV1.InferOutput<TSchema>;\n}> {\n const { body, attributes } = fm(rawContentFile);\n const result = await schema['~standard'].validate(attributes);\n if (result.issues) {\n throw new FrontmatterValidationError(result.issues, filename);\n }\n return { content: body, attributes: result.value };\n}\n"],"mappings":";;AAGA,IAAa,6BAAb,cAAgD,MAAM;CACpD,YACE,QACA,UACA;EACM,MAAA,gBAAgB,OAEZ,KAAS,MAAA;GAGR,MAAO,OAAE,EAAA,OAEP,QAAA,EAAA,KAAA,KAAA,MAAA,OAAA,MAAA,WAAA,EAAA,MAAA,EAAA,CAAA,KAAA,IAAA,CAAA,KACE;AACN,UAAO,OAAA,EAAA,UAAA;IAZA,CACA,KAAA,KAAA;EAYX,MAAO,SAAA,WAAA,IAAA,SAAA,OAAA;;;AAcT,OAAA,WAAS;AAKR,OAAE,OAAM;;;AAIZ,SACY,oBACF,gBAAyC,QACjD,UAAA;CACA,MAAM,EAAA,MAAI,eACR,GAAA,eAAA;;EAIE,MAAA,SAAa,OAAA,aAAA,SAAA,WAAA;AAGf,MAAA,UAAW,QACP,OAAI,OAAA,SAAA,WAAA,OAAA,IAAA,MAAA,kHAEL;EAAiB,MAAA,aAAuB;AAAO,MAAA,WAAA,OAAA,OAAA,IAAA,2BAAA,WAAA,QAAA,SAAA;AAG/C,SAAS;GAAA,SAAA;GAAA,YAAA,WAAA;GAAA;;AAAkB,QAAA;EAAA,SAAA;EAAA;EAAA;;AAGtC,eAAsB,yBAGpB,gBACA,QACA,UAIC;CACD,MAAQ,EAAA,MAAM,eAAkB,GAAA,eAAe;CAC/C,MAAM,SAAS,MAAM,OAAO,aAAa,SAAS,WAAW;AAC7D,KAAI,OAAO,OACH,OAAI,IAAA,2BAA2B,OAAO,QAAQ,SAAS;AAE/D,QAAO;EAAA,SAAA;EAAA,YAAA,OAAA;EAAA"}
1
+ {"version":3,"file":"parse-raw-content-file.mjs","names":[],"sources":["../../src/lib/parse-raw-content-file.ts"],"sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec';\nimport fm from 'front-matter';\n\nexport class FrontmatterValidationError extends Error {\n constructor(\n public readonly issues: ReadonlyArray<StandardSchemaV1.Issue>,\n public readonly filename?: string,\n ) {\n const issueMessages = issues\n .map((i) => {\n const path = i.path\n ? ` at \"${i.path.map((p) => (typeof p === 'object' ? p.key : p)).join('.')}\"`\n : '';\n return ` - ${i.message}${path}`;\n })\n .join('\\n');\n const prefix = filename ? `\"${filename}\" f` : 'F';\n super(`${prefix}rontmatter validation failed:\\n${issueMessages}`);\n this.name = 'FrontmatterValidationError';\n }\n}\n\nexport function parseRawContentFile<\n Attributes extends Record<string, any> = Record<string, any>,\n>(rawContentFile: string): { content: string; attributes: Attributes };\n\nexport function parseRawContentFile<TSchema extends StandardSchemaV1>(\n rawContentFile: string,\n schema: TSchema,\n filename?: string,\n): { content: string; attributes: StandardSchemaV1.InferOutput<TSchema> };\n\nexport function parseRawContentFile(\n rawContentFile: string,\n schema?: StandardSchemaV1,\n filename?: string,\n): { content: string; attributes: unknown } {\n const { body, attributes } = fm(rawContentFile);\n\n if (schema) {\n const result = schema['~standard'].validate(attributes);\n if (\n result != null &&\n typeof (result as PromiseLike<unknown>).then === 'function'\n ) {\n throw new Error(\n 'parseRawContentFile does not support async schema validation. ' +\n 'Use parseRawContentFileAsync() for async schemas.',\n );\n }\n const syncResult = result as StandardSchemaV1.Result<\n StandardSchemaV1.InferOutput<typeof schema>\n >;\n if (syncResult.issues) {\n throw new FrontmatterValidationError(syncResult.issues, filename);\n }\n return { content: body, attributes: syncResult.value };\n }\n\n return { content: body, attributes };\n}\n\nexport async function parseRawContentFileAsync<\n TSchema extends StandardSchemaV1,\n>(\n rawContentFile: string,\n schema: TSchema,\n filename?: string,\n): Promise<{\n content: string;\n attributes: StandardSchemaV1.InferOutput<TSchema>;\n}> {\n const { body, attributes } = fm(rawContentFile);\n const result = await schema['~standard'].validate(attributes);\n if (result.issues) {\n throw new FrontmatterValidationError(result.issues, filename);\n }\n return { content: body, attributes: result.value };\n}\n"],"mappings":";;AAGA,IAAa,6BAAb,cAAgD,MAAK;CACnD,YACkB,QACA,UAAiB;EAEjC,MAAM,gBAAgB,OACnB,KAAK,MAAK;GACT,MAAM,OAAO,EAAE,OACX,QAAQ,EAAE,KAAK,KAAK,MAAO,OAAO,MAAM,WAAW,EAAE,MAAM,EAAG,CAAC,KAAK,IAAI,CAAA,KACxE;AACJ,UAAO,OAAO,EAAE,UAAU;IAC1B,CACD,KAAK,KAAK;EACb,MAAM,SAAS,WAAW,IAAI,SAAQ,OAAQ;AAC9C,QAAM,GAAG,OAAM,iCAAkC,gBAAgB;AAZjD,OAAA,SAAA;AACA,OAAA,WAAA;AAYhB,OAAK,OAAO;;;AAchB,SAAgB,oBACd,gBACA,QACA,UAAiB;CAEjB,MAAM,EAAE,MAAM,eAAe,GAAG,eAAe;AAE/C,KAAI,QAAQ;EACV,MAAM,SAAS,OAAO,aAAa,SAAS,WAAW;AACvD,MACE,UAAU,QACV,OAAQ,OAAgC,SAAS,WAEjD,OAAM,IAAI,MACR,kHAED;EAEH,MAAM,aAAa;AAGnB,MAAI,WAAW,OACb,OAAM,IAAI,2BAA2B,WAAW,QAAQ,SAAS;AAEnE,SAAO;GAAE,SAAS;GAAM,YAAY,WAAW;GAAO;;AAGxD,QAAO;EAAE,SAAS;EAAM;EAAY;;AAGtC,eAAsB,yBAGpB,gBACA,QACA,UAAiB;CAKjB,MAAM,EAAE,MAAM,eAAe,GAAG,eAAe;CAC/C,MAAM,SAAS,MAAM,OAAO,aAAa,SAAS,WAAW;AAC7D,KAAI,OAAO,OACT,OAAM,IAAI,2BAA2B,OAAO,QAAQ,SAAS;AAE/D,QAAO;EAAE,SAAS;EAAM,YAAY,OAAO;EAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@analogjs/content",
3
- "version": "3.0.0-alpha.88",
3
+ "version": "3.0.0-alpha.89",
4
4
  "description": "Content Rendering for Analog",
5
5
  "type": "module",
6
6
  "author": "Brandon Roberts <robertsbt@gmail.com>",
@@ -108,8 +108,8 @@
108
108
  "tslib": "^2.3.0"
109
109
  },
110
110
  "devDependencies": {
111
- "@analogjs/vite-plugin-angular": "3.0.0-alpha.88",
112
- "@analogjs/vitest-angular": "3.0.0-alpha.88"
111
+ "@analogjs/vite-plugin-angular": "3.0.0-alpha.89",
112
+ "@analogjs/vitest-angular": "3.0.0-alpha.89"
113
113
  },
114
114
  "ng-update": {
115
115
  "packageGroup": [
@@ -0,0 +1,7 @@
1
+ export declare function escapeHtml(value: string): string;
2
+ /**
3
+ * Reduces a fenced code-block info string to a safe language identifier so it
4
+ * cannot break out of the `class="language-..."` attribute it is interpolated
5
+ * into.
6
+ */
7
+ export declare function sanitizeLanguage(lang: string | undefined): string;