@analogjs/content 3.0.0-alpha.97 → 3.0.0-alpha.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/analogjs-content-md4x.mjs +0 -36
- package/fesm2022/analogjs-content-md4x.mjs.map +1 -1
- package/fesm2022/analogjs-content.mjs +1 -1
- package/fesm2022/content-list-loader.mjs +1 -7
- package/fesm2022/content-list-loader.mjs.map +1 -1
- package/package.json +4 -4
- package/types/md4x/src/lib/md4x-content-renderer.service.d.ts +1 -2
- package/types/md4x/src/lib/md4x-wasm-content-renderer.service.d.ts +1 -2
- package/types/src/index.d.ts +1 -1
- package/types/src/lib/content-renderer.d.ts +0 -2
|
@@ -47,24 +47,6 @@ var Md4xContentRendererService = class Md4xContentRendererService extends Conten
|
|
|
47
47
|
toc
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
-
getContentHeadings(content) {
|
|
51
|
-
const lines = content.split("\n");
|
|
52
|
-
const toc = [];
|
|
53
|
-
const slugCounts = /* @__PURE__ */ new Map();
|
|
54
|
-
for (const line of lines) {
|
|
55
|
-
const match = /^(#{1,6})\s+(.+?)\s*$/.exec(line);
|
|
56
|
-
if (!match) continue;
|
|
57
|
-
const level = match[1].length;
|
|
58
|
-
const text = match[2].trim();
|
|
59
|
-
if (!text) continue;
|
|
60
|
-
toc.push({
|
|
61
|
-
id: makeSlug$1(text, slugCounts),
|
|
62
|
-
level,
|
|
63
|
-
text
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
return toc;
|
|
67
|
-
}
|
|
68
50
|
static {
|
|
69
51
|
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
70
52
|
minVersion: "12.0.0",
|
|
@@ -140,24 +122,6 @@ var Md4xWasmContentRendererService = class Md4xWasmContentRendererService extend
|
|
|
140
122
|
toc
|
|
141
123
|
};
|
|
142
124
|
}
|
|
143
|
-
getContentHeadings(content) {
|
|
144
|
-
const lines = content.split("\n");
|
|
145
|
-
const toc = [];
|
|
146
|
-
const slugCounts = /* @__PURE__ */ new Map();
|
|
147
|
-
for (const line of lines) {
|
|
148
|
-
const match = /^(#{1,6})\s+(.+?)\s*$/.exec(line);
|
|
149
|
-
if (!match) continue;
|
|
150
|
-
const level = match[1].length;
|
|
151
|
-
const text = match[2].trim();
|
|
152
|
-
if (!text) continue;
|
|
153
|
-
toc.push({
|
|
154
|
-
id: makeSlug(text, slugCounts),
|
|
155
|
-
level,
|
|
156
|
-
text
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
return toc;
|
|
160
|
-
}
|
|
161
125
|
static {
|
|
162
126
|
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
163
127
|
minVersion: "12.0.0",
|
|
@@ -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","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","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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"}
|
|
@@ -360,6 +360,6 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
360
360
|
}
|
|
361
361
|
});
|
|
362
362
|
//#endregion
|
|
363
|
-
export { AnchorNavigationDirective, CONTENT_FILE_LOADER, CONTENT_LIST_LOADER, CONTENT_LOCALE, ContentRenderer, FrontmatterValidationError,
|
|
363
|
+
export { AnchorNavigationDirective, CONTENT_FILE_LOADER, CONTENT_LIST_LOADER, CONTENT_LOCALE, ContentRenderer, FrontmatterValidationError, AnalogMarkdownComponent as MarkdownComponent, AnalogMarkdownRouteComponent as MarkdownRouteComponent, NoopContentRenderer, filterByLocale, injectContent, injectContentFileLoader, injectContentFiles, injectContentFilesMap, injectContentListLoader, injectContentLocale, parseRawContentFile, parseRawContentFileAsync, provideContent, withContentFileLoader, withContentListLoader, withLocale, withLocaleCandidates, withMarkdownRenderer };
|
|
364
364
|
|
|
365
365
|
//# sourceMappingURL=analogjs-content.mjs.map
|
|
@@ -8,9 +8,6 @@ var ContentRenderer = class ContentRenderer {
|
|
|
8
8
|
toc: []
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
|
-
getContentHeadings(_content) {
|
|
12
|
-
return [];
|
|
13
|
-
}
|
|
14
11
|
enhance() {}
|
|
15
12
|
static {
|
|
16
13
|
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
@@ -58,7 +55,7 @@ var NoopContentRenderer = class {
|
|
|
58
55
|
}
|
|
59
56
|
async render(content) {
|
|
60
57
|
this.contentId = this.generateHash(content);
|
|
61
|
-
const toc = this.
|
|
58
|
+
const toc = this.extractHeadings(content);
|
|
62
59
|
const key = makeStateKey(`content-headings-${this.contentId}`);
|
|
63
60
|
return {
|
|
64
61
|
content,
|
|
@@ -66,9 +63,6 @@ var NoopContentRenderer = class {
|
|
|
66
63
|
};
|
|
67
64
|
}
|
|
68
65
|
enhance() {}
|
|
69
|
-
getContentHeadings(content) {
|
|
70
|
-
return this.extractHeadings(content);
|
|
71
|
-
}
|
|
72
66
|
extractHeadings(content) {
|
|
73
67
|
const markdownHeadings = this.extractHeadingsFromMarkdown(content);
|
|
74
68
|
if (markdownHeadings.length > 0) return markdownHeadings;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-list-loader.mjs","names":[],"sources":["../../src/lib/content-renderer.ts","../../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":["/// <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","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-renderer.ts","../../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":["/// <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 // 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.extractHeadings(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 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","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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@analogjs/content",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.99",
|
|
4
4
|
"description": "Content Rendering for Analog",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Brandon Roberts <robertsbt@gmail.com>",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"@angular/core": "^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
28
28
|
"@angular/platform-browser": "^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
29
29
|
"@angular/router": "^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
30
|
-
"@nx/devkit": "^
|
|
30
|
+
"@nx/devkit": "^23.2.0",
|
|
31
31
|
"front-matter": "^4.0.2",
|
|
32
32
|
"rxjs": "^6.5.0 || ^7.5.0",
|
|
33
33
|
"@standard-schema/spec": "^1.1.0",
|
|
@@ -90,8 +90,8 @@
|
|
|
90
90
|
"tslib": "^2.3.0"
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
93
|
-
"@analogjs/vite-plugin-angular": "3.0.0-alpha.
|
|
94
|
-
"@analogjs/vitest-angular": "3.0.0-alpha.
|
|
93
|
+
"@analogjs/vite-plugin-angular": "3.0.0-alpha.99",
|
|
94
|
+
"@analogjs/vitest-angular": "3.0.0-alpha.99"
|
|
95
95
|
},
|
|
96
96
|
"ng-update": {
|
|
97
97
|
"packageGroup": [
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { InjectionToken } from '@angular/core';
|
|
2
|
-
import { ContentRenderer, RenderedContent
|
|
2
|
+
import { ContentRenderer, RenderedContent } from '../../../src/lib/content-renderer';
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
/**
|
|
5
5
|
* Options for the experimental md4x-based content renderer.
|
|
@@ -27,7 +27,6 @@ export declare const MD4X_RENDERER_OPTIONS: InjectionToken<Md4xRendererOptions>;
|
|
|
27
27
|
export declare class Md4xContentRendererService extends ContentRenderer {
|
|
28
28
|
private options;
|
|
29
29
|
render(content: string): Promise<RenderedContent>;
|
|
30
|
-
getContentHeadings(content: string): TableOfContentItem[];
|
|
31
30
|
static ɵfac: i0.ɵɵFactoryDeclaration<Md4xContentRendererService, never>;
|
|
32
31
|
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
33
32
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ContentRenderer, RenderedContent
|
|
1
|
+
import { ContentRenderer, RenderedContent } from '../../../src/lib/content-renderer';
|
|
2
2
|
import * as i0 from "@angular/core";
|
|
3
3
|
/**
|
|
4
4
|
* Content renderer backed by md4x/wasm for client-side (browser) rendering.
|
|
@@ -10,7 +10,6 @@ export declare class Md4xWasmContentRendererService extends ContentRenderer {
|
|
|
10
10
|
private options;
|
|
11
11
|
private initPromise;
|
|
12
12
|
render(content: string): Promise<RenderedContent>;
|
|
13
|
-
getContentHeadings(content: string): TableOfContentItem[];
|
|
14
13
|
static ɵfac: i0.ɵɵFactoryDeclaration<Md4xWasmContentRendererService, never>;
|
|
15
14
|
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
16
15
|
}
|
package/types/src/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { ContentRenderer, NoopContentRenderer } from './lib/content-renderer';
|
|
|
5
5
|
export type { RenderedContent, TableOfContentItem, } from './lib/content-renderer';
|
|
6
6
|
export { injectContentFiles } from './lib/inject-content-files';
|
|
7
7
|
export type { InjectContentFilesFilterFunction } from './lib/inject-content-files';
|
|
8
|
-
export { provideContent, withMarkdownRenderer
|
|
8
|
+
export { provideContent, withMarkdownRenderer } from './lib/provide-content';
|
|
9
9
|
export { default as MarkdownRouteComponent } from './lib/markdown-route.component';
|
|
10
10
|
export { default as MarkdownComponent } from './lib/markdown.component';
|
|
11
11
|
export { parseRawContentFile, parseRawContentFileAsync, FrontmatterValidationError, } from './lib/parse-raw-content-file';
|
|
@@ -10,7 +10,6 @@ export type RenderedContent = {
|
|
|
10
10
|
};
|
|
11
11
|
export declare abstract class ContentRenderer {
|
|
12
12
|
render(content: string): Promise<RenderedContent>;
|
|
13
|
-
getContentHeadings(_content: string): TableOfContentItem[];
|
|
14
13
|
enhance(): void;
|
|
15
14
|
static ɵfac: i0.ɵɵFactoryDeclaration<ContentRenderer, never>;
|
|
16
15
|
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
@@ -25,7 +24,6 @@ export declare class NoopContentRenderer implements ContentRenderer {
|
|
|
25
24
|
private generateHash;
|
|
26
25
|
render(content: string): Promise<RenderedContent>;
|
|
27
26
|
enhance(): void;
|
|
28
|
-
getContentHeadings(content: string): TableOfContentItem[];
|
|
29
27
|
private extractHeadings;
|
|
30
28
|
private extractHeadingsFromMarkdown;
|
|
31
29
|
private extractHeadingsFromHtml;
|