@analogjs/content 3.0.0-beta.2 → 3.0.0-beta.20

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.
@@ -1,5 +1,5 @@
1
1
  import { a as injectContentFileLoader, f as filterByLocale, g as ContentRenderer, n as injectContentListLoader, p as injectContentLocale } from "./content-list-loader.mjs";
2
- import { n as parseRawContentFile, r as parseRawContentFileAsync, t as FrontmatterValidationError } from "./parse-raw-content-file.mjs";
2
+ import { t as FrontmatterValidationError } from "./frontmatter-validation-error.mjs";
3
3
  import { computed, inject, resource } from "@angular/core";
4
4
  import { ActivatedRoute } from "@angular/router";
5
5
  import { from } from "rxjs";
@@ -49,6 +49,7 @@ async function getContentFile(contentFiles, slug, fallback, locale, schema) {
49
49
  const validationFilename = getValidationFilename(matchKey);
50
50
  return contentFile().then(async (contentFile) => {
51
51
  if (typeof contentFile === "string") {
52
+ const { parseRawContentFile, parseRawContentFileAsync } = await import("./parse-raw-content-file.mjs").then((n) => n.r);
52
53
  const { content, attributes } = schema ? await parseRawContentFileAsync(contentFile, schema, validationFilename) : parseRawContentFile(contentFile);
53
54
  return {
54
55
  filename: resolvedBase,
@@ -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 { FrontmatterValidationError } from '../../src/lib/frontmatter-validation-error';\nimport type {\n ContentFileLoader,\n ContentFileModule,\n} from '../../src/lib/parse-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, ContentFileLoader>,\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, ContentFileLoader> = {};\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(async (contentFile: string | ContentFileModule) => {\n if (typeof contentFile === 'string') {\n const { parseRawContentFile, parseRawContentFileAsync } =\n await import('../../src/lib/parse-raw-content-file');\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 * 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,5 +1,6 @@
1
+ import { n as parseRawContentFileAsync, t as parseRawContentFile } from "./parse-raw-content-file.mjs";
1
2
  import { _ as NoopContentRenderer, a as injectContentFileLoader, c as injectContentFilesMap, d as CONTENT_LOCALE, f as filterByLocale, g as ContentRenderer, h as withLocaleCandidates, i as CONTENT_FILE_LOADER, l as RenderTaskService, m as withLocale, n as injectContentListLoader, o as withContentFileLoader, p as injectContentLocale, r as withContentListLoader, s as injectContentFiles, t as CONTENT_LIST_LOADER, u as CONTENT_FILES_TOKEN } from "./content-list-loader.mjs";
2
- import { n as parseRawContentFile, r as parseRawContentFileAsync, t as FrontmatterValidationError } from "./parse-raw-content-file.mjs";
3
+ import { t as FrontmatterValidationError } from "./frontmatter-validation-error.mjs";
3
4
  import * as i0 from "@angular/core";
4
5
  import { Component, Directive, HostListener, InjectionToken, Input, NgZone, PLATFORM_ID, ViewEncapsulation, computed, inject, input } from "@angular/core";
5
6
  import { DOCUMENT, Location, isPlatformBrowser } from "@angular/common";
@@ -76,6 +77,21 @@ function isInternalUrl(anchorElement, document) {
76
77
  return anchorElement.host === document.location.host && anchorElement.protocol === document.location.protocol;
77
78
  }
78
79
  //#endregion
80
+ //#region packages/content/src/lib/parse-content-file.ts
81
+ /**
82
+ * Reads a loaded content file. Build-time content modules carry their
83
+ * frontmatter as `metadata`; raw strings from custom loaders are parsed
84
+ * with a lazily loaded frontmatter parser.
85
+ */
86
+ async function parseContentFile(file) {
87
+ if (typeof file !== "string") return {
88
+ content: file.default,
89
+ attributes: file.metadata
90
+ };
91
+ const { parseRawContentFile } = await import("./parse-raw-content-file.mjs").then((n) => n.r);
92
+ return parseRawContentFile(file);
93
+ }
94
+ //#endregion
79
95
  //#region packages/content/src/lib/utils/zone-wait-for.ts
80
96
  async function waitFor(prom) {
81
97
  if (isObservable(prom)) prom = firstValueFrom(prom);
@@ -113,25 +129,13 @@ function getContentFile(contentFiles, prefix, slug, fallback, renderTaskService,
113
129
  observer.complete();
114
130
  setTimeout(() => renderTaskService.clearRenderTask(contentTask), 10);
115
131
  });
116
- }).pipe(switchMap((contentFile) => {
117
- if (typeof contentFile === "string") {
118
- const { content, attributes } = parseRawContentFile(contentFile);
119
- return from(contentRenderer.render(content)).pipe(map((rendered) => ({
120
- filename: resolvedBase,
121
- slug,
122
- attributes,
123
- content,
124
- toc: rendered.toc ?? []
125
- })));
126
- }
127
- return of({
128
- filename: resolvedBase,
129
- slug,
130
- attributes: contentFile.metadata,
131
- content: contentFile.default,
132
- toc: []
133
- });
134
- }), finalize(() => {
132
+ }).pipe(switchMap((contentFile) => parseContentFile(contentFile)), switchMap(({ content, attributes }) => from(contentRenderer.render(content)).pipe(map((rendered) => ({
133
+ filename: resolvedBase,
134
+ slug,
135
+ attributes,
136
+ content,
137
+ toc: rendered.toc ?? []
138
+ })))), finalize(() => {
135
139
  renderTaskService.clearRenderTask(contentTask);
136
140
  }));
137
141
  }
@@ -361,6 +365,6 @@ i0.ɵɵngDeclareClassMetadata({
361
365
  }
362
366
  });
363
367
  //#endregion
364
- 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 };
368
+ 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, parseContentFile, parseRawContentFile, parseRawContentFileAsync, provideContent, withContentFileLoader, withContentListLoader, withLocale, withLocaleCandidates, withMarkdownRenderer };
365
369
 
366
370
  //# sourceMappingURL=analogjs-content.mjs.map
@@ -1 +1 @@
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/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 { finalize, map, switchMap, tap } from 'rxjs/operators';\n\nimport { ContentFile } from './content-file';\nimport { ContentRenderer } from './content-renderer';\nimport { CONTENT_LOCALE, withLocaleCandidates } from './content-locale';\nimport { CONTENT_FILES_TOKEN } from './content-files-token';\nimport { 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 finalize(() => {\n if (import.meta.env.SSR !== true) {\n renderTaskService.clearRenderTask(contentTask);\n }\n }),\n );\n}\n\n/**\n * Retrieves the static content using the provided param and/or prefix.\n *\n * @param param route parameter (default: 'slug')\n * @param fallback fallback text if content file is not found (default: 'No Content Found')\n */\nexport function injectContent<\n Attributes extends Record<string, any> = Record<string, any>,\n>(\n param:\n | string\n | {\n param: string;\n subdirectory: string;\n }\n | {\n customFilename: string;\n } = 'slug',\n fallback = 'No Content Found',\n): Observable<ContentFile<Attributes | Record<string, never>>> {\n const contentFiles = inject(CONTENT_FILES_TOKEN);\n const contentRenderer = inject(ContentRenderer);\n const renderTaskService = inject(RenderTaskService);\n const locale = inject(CONTENT_LOCALE, { optional: true });\n const task = renderTaskService.addRenderTask();\n\n if (typeof param === 'string' || 'param' in param) {\n const prefix = typeof param === 'string' ? '' : `${param.subdirectory}/`;\n const route = inject(ActivatedRoute);\n const paramKey = typeof param === 'string' ? param : param.param;\n return route.paramMap.pipe(\n map((params) => params.get(paramKey)),\n switchMap((slug) => {\n if (slug) {\n return getContentFile<Attributes>(\n contentFiles,\n prefix,\n slug,\n fallback,\n renderTaskService,\n contentRenderer,\n locale,\n );\n }\n return of({\n filename: '',\n slug: '',\n attributes: {},\n content: fallback,\n toc: [],\n });\n }),\n tap(() => renderTaskService.clearRenderTask(task)),\n );\n } else {\n return getContentFile<Attributes>(\n contentFiles,\n '',\n param.customFilename,\n fallback,\n renderTaskService,\n contentRenderer,\n locale,\n ).pipe(tap(() => renderTaskService.clearRenderTask(task)));\n }\n}\n","import { Provider, InjectionToken } from '@angular/core';\nimport { ContentRenderer, NoopContentRenderer } from './content-renderer';\nimport { RenderTaskService } from './render-task.service';\nimport { withContentFileLoader } from './content-file-loader';\nimport { withContentListLoader } from './content-list-loader';\n\nexport interface MarkdownRendererOptions {\n loadMermaid?: () => Promise<typeof import('mermaid')>;\n}\n\nconst CONTENT_RENDERER_PROVIDERS: Provider[] = [\n {\n provide: ContentRenderer,\n useClass: NoopContentRenderer,\n },\n withContentFileLoader(),\n withContentListLoader(),\n];\n\nexport function withMarkdownRenderer(\n options?: MarkdownRendererOptions,\n): Provider {\n return [\n CONTENT_RENDERER_PROVIDERS,\n options?.loadMermaid\n ? [\n {\n provide: MERMAID_IMPORT_TOKEN,\n useFactory: options.loadMermaid,\n },\n ]\n : [],\n ];\n}\n\nexport function provideContent(...features: Provider[]): Provider[] {\n return [\n { provide: RenderTaskService, useClass: RenderTaskService },\n ...features,\n ];\n}\n\nexport const MERMAID_IMPORT_TOKEN: InjectionToken<\n Promise<typeof import('mermaid')>\n> = new InjectionToken<Promise<typeof import('mermaid')>>('mermaid_import');\n","import {\n AfterViewChecked,\n Component,\n inject,\n Input,\n ViewEncapsulation,\n} from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { ActivatedRoute } from '@angular/router';\n\nimport { ContentRenderer } from './content-renderer';\nimport { AnchorNavigationDirective } from './anchor-navigation.directive';\n\n@Component({\n selector: 'analog-markdown-route',\n standalone: true,\n imports: [],\n hostDirectives: [AnchorNavigationDirective],\n preserveWhitespaces: true,\n encapsulation: ViewEncapsulation.None,\n template: `<div [innerHTML]=\"content\" [class]=\"classes\"></div>`,\n})\nexport default class AnalogMarkdownRouteComponent implements AfterViewChecked {\n private sanitizer = inject(DomSanitizer);\n private route = inject(ActivatedRoute);\n contentRenderer: ContentRenderer = inject(ContentRenderer);\n\n protected content: SafeHtml = this.sanitizer.bypassSecurityTrustHtml(\n this.route.snapshot.data['renderedAnalogContent'],\n );\n\n @Input() classes = 'analog-markdown-route';\n\n ngAfterViewChecked(): void {\n this.contentRenderer.enhance();\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n AfterViewChecked,\n Component,\n InputSignal,\n NgZone,\n PLATFORM_ID,\n Signal,\n ViewEncapsulation,\n computed,\n inject,\n input,\n} from '@angular/core';\nimport { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { ActivatedRoute, Data } from '@angular/router';\nimport { from, Observable, of } from 'rxjs';\nimport { catchError, map, switchMap } from 'rxjs/operators';\n\nimport { AnchorNavigationDirective } from './anchor-navigation.directive';\nimport { ContentRenderer } from './content-renderer';\nimport { MERMAID_IMPORT_TOKEN } from './provide-content';\n\n@Component({\n selector: 'analog-markdown',\n standalone: true,\n hostDirectives: [AnchorNavigationDirective],\n preserveWhitespaces: true,\n encapsulation: ViewEncapsulation.None,\n template: ` <div [innerHTML]=\"htmlContent()\" [class]=\"classes()\"></div> `,\n})\nexport default class AnalogMarkdownComponent implements AfterViewChecked {\n private sanitizer = inject(DomSanitizer);\n private route = inject(ActivatedRoute);\n private zone = inject(NgZone);\n private readonly platformId = inject(PLATFORM_ID);\n private readonly mermaidImport = inject(MERMAID_IMPORT_TOKEN, {\n optional: true,\n });\n private mermaid: typeof import('mermaid') | undefined;\n\n private contentSource: Signal<SafeHtml | string | undefined> = toSignal(\n this.getContentSource(),\n );\n readonly htmlContent: Signal<SafeHtml | string | undefined> = computed(() => {\n const inputContent = this.content();\n\n if (inputContent) {\n return this.sanitizer.bypassSecurityTrustHtml(inputContent as string);\n }\n\n return this.contentSource();\n });\n readonly content: InputSignal<string | object | null | undefined> = input<\n string | object | null\n >();\n readonly classes: InputSignal<string> = input('analog-markdown');\n\n contentRenderer: ContentRenderer = inject(ContentRenderer);\n\n constructor() {\n if (isPlatformBrowser(this.platformId) && this.mermaidImport) {\n // Mermaid can only be loaded on client side\n this.loadMermaid(this.mermaidImport);\n }\n }\n\n getContentSource(): Observable<SafeHtml | string> {\n return this.route.data.pipe(\n map<Data, string>((data) => data['_analogContent'] ?? ''),\n switchMap((contentString) => this.renderContent(contentString)),\n map((content) => this.sanitizer.bypassSecurityTrustHtml(content)),\n catchError((e) => of(`There was an error ${e}`)),\n );\n }\n\n async renderContent(content: string): Promise<string> {\n const rendered = await this.contentRenderer.render(content);\n return rendered.content;\n }\n\n ngAfterViewChecked(): void {\n this.contentRenderer.enhance();\n this.zone.runOutsideAngular(() => this.mermaid?.default.run());\n }\n\n private loadMermaid(mermaidImport: Promise<typeof import('mermaid')>) {\n this.zone.runOutsideAngular(() =>\n // Wrap into an observable to avoid redundant initialization once\n // the markdown component is destroyed before the promise is resolved.\n from(mermaidImport)\n .pipe(takeUntilDestroyed())\n .subscribe((mermaid) => {\n this.mermaid = mermaid;\n this.mermaid.default.initialize({ startOnLoad: false });\n // Explicitly running mermaid as ngAfterViewChecked\n // has probably already been called\n this.mermaid?.default.run();\n }),\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"analogjs-content.mjs","names":[],"sources":["../../src/lib/anchor-navigation.directive.ts","../../src/lib/parse-content-file.ts","../../src/lib/utils/zone-wait-for.ts","../../src/lib/content.ts","../../src/lib/provide-content.ts","../../src/lib/markdown-route.component.ts","../../src/lib/markdown.component.ts"],"sourcesContent":["import { Directive, HostListener, inject } from '@angular/core';\nimport { DOCUMENT, Location } from '@angular/common';\nimport { Router } from '@angular/router';\n\n@Directive({\n selector: '[analogAnchorNavigation]',\n standalone: true,\n})\nexport class AnchorNavigationDirective {\n private readonly document = inject(DOCUMENT);\n private readonly location = inject(Location);\n private readonly router = inject(Router);\n\n @HostListener('click', ['$event.target'])\n handleNavigation(element: EventTarget | null): boolean {\n if (\n element instanceof HTMLAnchorElement &&\n isInternalUrl(element, this.document) &&\n hasTargetSelf(element) &&\n !hasDownloadAttribute(element)\n ) {\n const { pathname, search, hash } = element;\n // A `<base>` element makes the browser resolve fragment-only hrefs\n // (e.g. `href=\"#some-id\"`) against the base URL rather than the\n // current document, so `pathname`/`search` come back as the base's\n // rather than the current page's. Fall back to the document's own\n // location in that case, or an in-page anchor link would navigate\n // to the base route and lose the fragment there.\n const isFragmentOnly = element.getAttribute('href')?.startsWith('#');\n const resolvedPathname = isFragmentOnly\n ? this.document.location.pathname\n : pathname;\n const resolvedSearch = isFragmentOnly\n ? this.document.location.search\n : search;\n const url = this.location.normalize(\n `${resolvedPathname}${resolvedSearch}${hash}`,\n );\n this.router.navigateByUrl(url);\n\n return false;\n }\n\n return true;\n }\n}\n\nfunction hasDownloadAttribute(anchorElement: HTMLAnchorElement): boolean {\n return anchorElement.getAttribute('download') !== null;\n}\n\nfunction hasTargetSelf(anchorElement: HTMLAnchorElement): boolean {\n return !anchorElement.target || anchorElement.target === '_self';\n}\n\nfunction isInternalUrl(\n anchorElement: HTMLAnchorElement,\n document: Document,\n): boolean {\n return (\n anchorElement.host === document.location.host &&\n anchorElement.protocol === document.location.protocol\n );\n}\n","export type ContentFileModule<\n Attributes extends Record<string, any> = Record<string, any>,\n> = {\n default: string;\n metadata: Attributes;\n};\n\nexport type ContentFileLoader = () => Promise<string | ContentFileModule>;\n\n/**\n * Reads a loaded content file. Build-time content modules carry their\n * frontmatter as `metadata`; raw strings from custom loaders are parsed\n * with a lazily loaded frontmatter parser.\n */\nexport async function parseContentFile<\n Attributes extends Record<string, any> = Record<string, any>,\n>(\n file: string | ContentFileModule,\n): Promise<{ content: string; attributes: Attributes }> {\n if (typeof file !== 'string') {\n return { content: file.default, attributes: file.metadata as Attributes };\n }\n\n const { parseRawContentFile } = await import('./parse-raw-content-file');\n return parseRawContentFile<Attributes>(file);\n}\n","import { firstValueFrom, isObservable, Observable } from 'rxjs';\n\ndeclare const Zone: any;\n\nexport async function waitFor<T>(prom: Promise<T> | Observable<T>): Promise<T> {\n if (isObservable(prom)) {\n prom = firstValueFrom(prom);\n }\n\n if (typeof Zone === 'undefined') {\n return prom;\n }\n\n const macroTask = Zone.current.scheduleMacroTask(\n `AnalogContentResolve-${Math.random()}`,\n () => {\n /* noop */\n },\n {},\n () => {\n /* noop */\n },\n );\n return prom.then((p: T) => {\n macroTask.invoke();\n return p;\n });\n}\n","/// <reference types=\"vite/client\" />\n\nimport { inject } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { from, Observable, of } from 'rxjs';\nimport { finalize, map, switchMap, tap } from 'rxjs/operators';\n\nimport { ContentFile } from './content-file';\nimport { ContentRenderer } from './content-renderer';\nimport { CONTENT_LOCALE, withLocaleCandidates } from './content-locale';\nimport { CONTENT_FILES_TOKEN } from './content-files-token';\nimport {\n type ContentFileLoader,\n type ContentFileModule,\n parseContentFile,\n} from './parse-content-file';\nimport { waitFor } from './utils/zone-wait-for';\nimport { RenderTaskService } from './render-task.service';\n\nfunction getContentFile<\n Attributes extends Record<string, any> = Record<string, any>,\n>(\n contentFiles: Record<string, ContentFileLoader>,\n prefix: string,\n slug: string,\n fallback: string,\n renderTaskService: RenderTaskService,\n contentRenderer: ContentRenderer,\n locale?: string | null,\n): Observable<ContentFile<Attributes | Record<string, never>>> {\n // Normalize file keys so both \"/src/content/...\" and \"/<project>/src/content/...\" resolve.\n const normalizedFiles: Record<string, ContentFileLoader> = {};\n for (const [key, resolver] of Object.entries(contentFiles)) {\n const normalizedKey = key\n .replace(/^(?:.*?)\\/content(?=\\/)/, '/src/content')\n .replace(/\\/{2,}/g, '/');\n normalizedFiles[normalizedKey] = resolver;\n }\n\n const base = `/src/content/${prefix}${slug}`.replace(/\\/{2,}/g, '/');\n const candidates = [`${base}.md`, `${base}/index.md`];\n\n const allCandidates = withLocaleCandidates(candidates, locale);\n const matchKey = allCandidates.find((k) => k in normalizedFiles);\n const contentFile = matchKey ? normalizedFiles[matchKey] : undefined;\n const resolvedBase = (matchKey || `${base}.md`).replace(/\\.md$/, '');\n\n if (!contentFile) {\n return of({\n filename: resolvedBase,\n attributes: {},\n slug: '',\n content: fallback,\n toc: [],\n });\n }\n\n const contentTask = renderTaskService.addRenderTask();\n return new Observable<string | ContentFileModule>((observer) => {\n // `waitFor` is a no-op in non-Zone environments (browser without zone.js),\n // so this branch works for both SSR and client. Keeping a single path also\n // avoids Rolldown evaluating `import.meta.env.SSR` at library-build time\n // and eliminating the task-clearing branch, which caused SSR to hang once\n // the content map resolved a real lazy import.\n waitFor(contentFile()).then((content) => {\n observer.next(content);\n observer.complete();\n\n setTimeout(() => renderTaskService.clearRenderTask(contentTask), 10);\n });\n }).pipe(\n switchMap((contentFile) => parseContentFile<Attributes>(contentFile)),\n switchMap(({ content, attributes }) =>\n from(contentRenderer.render(content)).pipe(\n map((rendered) => ({\n filename: resolvedBase,\n slug,\n attributes,\n content,\n toc: rendered.toc ?? [],\n })),\n ),\n ),\n finalize(() => {\n if (import.meta.env.SSR !== true) {\n renderTaskService.clearRenderTask(contentTask);\n }\n }),\n );\n}\n\n/**\n * Retrieves the static content using the provided param and/or prefix.\n *\n * @param param route parameter (default: 'slug')\n * @param fallback fallback text if content file is not found (default: 'No Content Found')\n */\nexport function injectContent<\n Attributes extends Record<string, any> = Record<string, any>,\n>(\n param:\n | string\n | {\n param: string;\n subdirectory: string;\n }\n | {\n customFilename: string;\n } = 'slug',\n fallback = 'No Content Found',\n): Observable<ContentFile<Attributes | Record<string, never>>> {\n const contentFiles = inject(CONTENT_FILES_TOKEN);\n const contentRenderer = inject(ContentRenderer);\n const renderTaskService = inject(RenderTaskService);\n const locale = inject(CONTENT_LOCALE, { optional: true });\n const task = renderTaskService.addRenderTask();\n\n if (typeof param === 'string' || 'param' in param) {\n const prefix = typeof param === 'string' ? '' : `${param.subdirectory}/`;\n const route = inject(ActivatedRoute);\n const paramKey = typeof param === 'string' ? param : param.param;\n return route.paramMap.pipe(\n map((params) => params.get(paramKey)),\n switchMap((slug) => {\n if (slug) {\n return getContentFile<Attributes>(\n contentFiles,\n prefix,\n slug,\n fallback,\n renderTaskService,\n contentRenderer,\n locale,\n );\n }\n return of({\n filename: '',\n slug: '',\n attributes: {},\n content: fallback,\n toc: [],\n });\n }),\n tap(() => renderTaskService.clearRenderTask(task)),\n );\n } else {\n return getContentFile<Attributes>(\n contentFiles,\n '',\n param.customFilename,\n fallback,\n renderTaskService,\n contentRenderer,\n locale,\n ).pipe(tap(() => renderTaskService.clearRenderTask(task)));\n }\n}\n","import { Provider, InjectionToken } from '@angular/core';\nimport { ContentRenderer, NoopContentRenderer } from './content-renderer';\nimport { RenderTaskService } from './render-task.service';\nimport { withContentFileLoader } from './content-file-loader';\nimport { withContentListLoader } from './content-list-loader';\n\nexport interface MarkdownRendererOptions {\n loadMermaid?: () => Promise<typeof import('mermaid')>;\n}\n\nconst CONTENT_RENDERER_PROVIDERS: Provider[] = [\n {\n provide: ContentRenderer,\n useClass: NoopContentRenderer,\n },\n withContentFileLoader(),\n withContentListLoader(),\n];\n\nexport function withMarkdownRenderer(\n options?: MarkdownRendererOptions,\n): Provider {\n return [\n CONTENT_RENDERER_PROVIDERS,\n options?.loadMermaid\n ? [\n {\n provide: MERMAID_IMPORT_TOKEN,\n useFactory: options.loadMermaid,\n },\n ]\n : [],\n ];\n}\n\nexport function provideContent(...features: Provider[]): Provider[] {\n return [\n { provide: RenderTaskService, useClass: RenderTaskService },\n ...features,\n ];\n}\n\nexport const MERMAID_IMPORT_TOKEN: InjectionToken<\n Promise<typeof import('mermaid')>\n> = new InjectionToken<Promise<typeof import('mermaid')>>('mermaid_import');\n","import {\n AfterViewChecked,\n Component,\n inject,\n Input,\n ViewEncapsulation,\n} from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { ActivatedRoute } from '@angular/router';\n\nimport { ContentRenderer } from './content-renderer';\nimport { AnchorNavigationDirective } from './anchor-navigation.directive';\n\n@Component({\n selector: 'analog-markdown-route',\n standalone: true,\n imports: [],\n hostDirectives: [AnchorNavigationDirective],\n preserveWhitespaces: true,\n encapsulation: ViewEncapsulation.None,\n template: `<div [innerHTML]=\"content\" [class]=\"classes\"></div>`,\n})\nexport default class AnalogMarkdownRouteComponent implements AfterViewChecked {\n private sanitizer = inject(DomSanitizer);\n private route = inject(ActivatedRoute);\n contentRenderer: ContentRenderer = inject(ContentRenderer);\n\n protected content: SafeHtml = this.sanitizer.bypassSecurityTrustHtml(\n this.route.snapshot.data['renderedAnalogContent'],\n );\n\n @Input() classes = 'analog-markdown-route';\n\n ngAfterViewChecked(): void {\n this.contentRenderer.enhance();\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n AfterViewChecked,\n Component,\n InputSignal,\n NgZone,\n PLATFORM_ID,\n Signal,\n ViewEncapsulation,\n computed,\n inject,\n input,\n} from '@angular/core';\nimport { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { ActivatedRoute, Data } from '@angular/router';\nimport { from, Observable, of } from 'rxjs';\nimport { catchError, map, switchMap } from 'rxjs/operators';\n\nimport { AnchorNavigationDirective } from './anchor-navigation.directive';\nimport { ContentRenderer } from './content-renderer';\nimport { MERMAID_IMPORT_TOKEN } from './provide-content';\n\n@Component({\n selector: 'analog-markdown',\n standalone: true,\n hostDirectives: [AnchorNavigationDirective],\n preserveWhitespaces: true,\n encapsulation: ViewEncapsulation.None,\n template: ` <div [innerHTML]=\"htmlContent()\" [class]=\"classes()\"></div> `,\n})\nexport default class AnalogMarkdownComponent implements AfterViewChecked {\n private sanitizer = inject(DomSanitizer);\n private route = inject(ActivatedRoute);\n private zone = inject(NgZone);\n private readonly platformId = inject(PLATFORM_ID);\n private readonly mermaidImport = inject(MERMAID_IMPORT_TOKEN, {\n optional: true,\n });\n private mermaid: typeof import('mermaid') | undefined;\n\n private contentSource: Signal<SafeHtml | string | undefined> = toSignal(\n this.getContentSource(),\n );\n readonly htmlContent: Signal<SafeHtml | string | undefined> = computed(() => {\n const inputContent = this.content();\n\n if (inputContent) {\n return this.sanitizer.bypassSecurityTrustHtml(inputContent as string);\n }\n\n return this.contentSource();\n });\n readonly content: InputSignal<string | object | null | undefined> = input<\n string | object | null\n >();\n readonly classes: InputSignal<string> = input('analog-markdown');\n\n contentRenderer: ContentRenderer = inject(ContentRenderer);\n\n constructor() {\n if (isPlatformBrowser(this.platformId) && this.mermaidImport) {\n // Mermaid can only be loaded on client side\n this.loadMermaid(this.mermaidImport);\n }\n }\n\n getContentSource(): Observable<SafeHtml | string> {\n return this.route.data.pipe(\n map<Data, string>((data) => data['_analogContent'] ?? ''),\n switchMap((contentString) => this.renderContent(contentString)),\n map((content) => this.sanitizer.bypassSecurityTrustHtml(content)),\n catchError((e) => of(`There was an error ${e}`)),\n );\n }\n\n async renderContent(content: string): Promise<string> {\n const rendered = await this.contentRenderer.render(content);\n return rendered.content;\n }\n\n ngAfterViewChecked(): void {\n this.contentRenderer.enhance();\n this.zone.runOutsideAngular(() => this.mermaid?.default.run());\n }\n\n private loadMermaid(mermaidImport: Promise<typeof import('mermaid')>) {\n this.zone.runOutsideAngular(() =>\n // Wrap into an observable to avoid redundant initialization once\n // the markdown component is destroyed before the promise is resolved.\n from(mermaidImport)\n .pipe(takeUntilDestroyed())\n .subscribe((mermaid) => {\n this.mermaid = mermaid;\n this.mermaid.default.initialize({ startOnLoad: false });\n // Explicitly running mermaid as ngAfterViewChecked\n // has probably already been called\n this.mermaid?.default.run();\n }),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;AAQA,IAAa,4BAAb,MAAa,0BAAyB;CAJtC,cAAA;EAKmB,KAAA,WAAW,OAAO,QAAQ;EAC1B,KAAA,WAAW,OAAO,QAAQ;EAC1B,KAAA,SAAS,OAAO,MAAM;CAkCxC;CA/BC,iBAAiB,SAA2B;EAC1C,IACE,mBAAmB,qBACnB,cAAc,SAAS,KAAK,QAAQ,KACpC,cAAc,OAAO,KACrB,CAAC,qBAAqB,OAAO,GAC7B;GACA,MAAM,EAAE,UAAU,QAAQ,SAAS;GAOnC,MAAM,iBAAiB,QAAQ,aAAa,MAAM,CAAC,EAAE,WAAW,GAAG;GACnE,MAAM,mBAAmB,iBACrB,KAAK,SAAS,SAAS,WACvB;GACJ,MAAM,iBAAiB,iBACnB,KAAK,SAAS,SAAS,SACvB;GACJ,MAAM,MAAM,KAAK,SAAS,UACxB,GAAG,mBAAmB,iBAAiB,MAAM;GAE/C,KAAK,OAAO,cAAc,GAAG;GAE7B,OAAO;EACT;EAEA,OAAO;CACT;CApCW;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAyB,MAAA,CAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAAzB;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAAyB,cAAA;GAAA,UAAA;GAAA,MAAA,EAAA,WAAA,EAAA,SAAA,kCAAA,EAAA;GAAA,UAAA;EAAA,CAAA;CAAA;;AAAzB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAyB,YAAA,CAAA;EAJrC,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,YAAY;EACb,CAAA;;;EAME,MAAA;EAAa,MAAA,CAAA,SAAS,CAAC,eAAe,CAAC;;;AAkC1C,SAAS,qBAAqB,eAAgC;CAC5D,OAAO,cAAc,aAAa,UAAU,MAAM;AACpD;AAEA,SAAS,cAAc,eAAgC;CACrD,OAAO,CAAC,cAAc,UAAU,cAAc,WAAW;AAC3D;AAEA,SAAS,cACP,eACA,UAAkB;CAElB,OACE,cAAc,SAAS,SAAS,SAAS,QACzC,cAAc,aAAa,SAAS,SAAS;AAEjD;;;;;;;;ACjDA,eAAsB,iBAGpB,MAAgC;CAEhC,IAAI,OAAO,SAAS,UAClB,OAAO;EAAE,SAAS,KAAK;EAAS,YAAY,KAAK;CAAsB;CAGzE,MAAM,EAAE,wBAAwB,MAAM,OAAO,+BAA2B,CAAA,MAAA,MAAA,EAAA,CAAA;CACxE,OAAO,oBAAgC,IAAI;AAC7C;;;ACrBA,eAAsB,QAAW,MAAgC;CAC/D,IAAI,aAAa,IAAI,GACnB,OAAO,eAAe,IAAI;CAG5B,IAAI,OAAO,SAAS,aAClB,OAAO;CAGT,MAAM,YAAY,KAAK,QAAQ,kBAC7B,wBAAwB,KAAK,OAAM,WAC9B,CAEL,GACA,CAAA,SACK,CAEL,CAAC;CAEH,OAAO,KAAK,MAAM,MAAQ;EACxB,UAAU,OAAM;EAChB,OAAO;CACT,CAAC;AACH;;;ACRA,SAAS,eAGP,cACA,QACA,MACA,UACA,mBACA,iBACA,QAAsB;CAGtB,MAAM,kBAAqD,CAAA;CAC3D,KAAK,MAAM,CAAC,KAAK,aAAa,OAAO,QAAQ,YAAY,GAAG;EAC1D,MAAM,gBAAgB,IACnB,QAAQ,2BAA2B,cAAc,CAAC,CAClD,QAAQ,WAAW,GAAG;EACzB,gBAAgB,iBAAiB;CACnC;CAEA,MAAM,OAAO,gBAAgB,SAAS,OAAO,QAAQ,WAAW,GAAG;CACnE,MAAM,aAAa,CAAC,GAAG,KAAI,MAAO,GAAG,KAAI,UAAW;CAGpD,MAAM,WADgB,qBAAqB,YAAY,MACtC,CAAa,CAAC,MAAM,MAAM,KAAK,eAAe;CAC/D,MAAM,cAAc,WAAW,gBAAgB,YAAY,KAAA;CAC3D,MAAM,gBAAgB,YAAY,GAAG,KAAI,KAAA,CAAO,QAAQ,SAAS,EAAE;CAEnE,IAAI,CAAC,aACH,OAAO,GAAG;EACR,UAAU;EACV,YAAY,CAAA;EACZ,MAAM;EACN,SAAS;EACT,KAAK,CAAA;CACN,CAAA;CAGH,MAAM,cAAc,kBAAkB,cAAa;CACnD,OAAO,IAAI,YAAwC,aAAY;EAM7D,QAAQ,YAAW,CAAE,CAAC,CAAC,MAAM,YAAW;GACtC,SAAS,KAAK,OAAO;GACrB,SAAS,SAAQ;GAEjB,iBAAiB,kBAAkB,gBAAgB,WAAW,GAAG,EAAE;EACrE,CAAC;CACH,CAAC,CAAC,CAAC,KACD,WAAW,gBAAgB,iBAA6B,WAAW,CAAC,GACpE,WAAW,EAAE,SAAS,iBACpB,KAAK,gBAAgB,OAAO,OAAO,CAAC,CAAC,CAAC,KACpC,KAAK,cAAc;EACjB,UAAU;EACV;EACA;EACA;EACA,KAAK,SAAS,OAAO,CAAA;CACtB,EAAC,CAAC,CACJ,GAEH,eAAc;EAEV,kBAAkB,gBAAgB,WAAW;CAEjD,CAAC,CAAC;AAEN;;;;;;;AAQA,SAAgB,cAGd,QAQQ,QACR,WAAW,oBAAkB;CAE7B,MAAM,eAAe,OAAO,mBAAmB;CAC/C,MAAM,kBAAkB,OAAO,eAAe;CAC9C,MAAM,oBAAoB,OAAO,iBAAiB;CAClD,MAAM,SAAS,OAAO,gBAAgB,EAAE,UAAU,KAAI,CAAE;CACxD,MAAM,OAAO,kBAAkB,cAAa;CAE5C,IAAI,OAAO,UAAU,YAAY,WAAW,OAAO;EACjD,MAAM,SAAS,OAAO,UAAU,WAAW,KAAK,GAAG,MAAM,aAAY;EACrE,MAAM,QAAQ,OAAO,cAAc;EACnC,MAAM,WAAW,OAAO,UAAU,WAAW,QAAQ,MAAM;EAC3D,OAAO,MAAM,SAAS,KACpB,KAAK,WAAW,OAAO,IAAI,QAAQ,CAAC,GACpC,WAAW,SAAQ;GACjB,IAAI,MACF,OAAO,eACL,cACA,QACA,MACA,UACA,mBACA,iBACA,MAAM;GAGV,OAAO,GAAG;IACR,UAAU;IACV,MAAM;IACN,YAAY,CAAA;IACZ,SAAS;IACT,KAAK,CAAA;GACN,CAAA;EACH,CAAC,GACD,UAAU,kBAAkB,gBAAgB,IAAI,CAAC,CAAC;CAEtD,OACE,OAAO,eACL,cACA,IACA,MAAM,gBACN,UACA,mBACA,iBACA,MAAM,CACP,CAAC,KAAK,UAAU,kBAAkB,gBAAgB,IAAI,CAAC,CAAC;AAE7D;;;AClJA,IAAM,6BAAyC;CAC7C;EACE,SAAS;EACT,UAAU;CACX;CACD,sBAAqB;CACrB,sBAAqB;AACtB;AAED,SAAgB,qBACd,SAAiC;CAEjC,OAAO,CACL,4BACA,SAAS,cACL,CACE;EACE,SAAS;EACT,YAAY,QAAQ;CACrB,CACF,IACD,CAAA,CACL;AACH;AAEA,SAAgB,eAAe,GAAG,UAAoB;CACpD,OAAO,CACL;EAAE,SAAS;EAAmB,UAAU;CAAiB,GACzD,GAAG,QACJ;AACH;AAEA,IAAa,uBAET,IAAI,eAAkD,gBAAgB;;;ACtB1E,IAAqB,+BAArB,MAAqB,6BAA4B;CATjD,cAAA;EAUU,KAAA,YAAY,OAAO,YAAY;EAC/B,KAAA,QAAQ,OAAO,cAAc;EACrC,KAAA,kBAAmC,OAAO,eAAe;EAE/C,KAAA,UAAoB,KAAK,UAAU,wBAC3C,KAAK,MAAM,SAAS,KAAK,wBAAwB;EAG1C,KAAA,UAAU;CAKpB;CAHC,qBAAkB;EAChB,KAAK,gBAAgB,QAAO;CAC9B;CAbmB;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAA4B,MAAA,CAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAA5B;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAA4B,cAAA;GAAA,UAAA;GAAA,QAAA,EAAA,SAAA,UAAA;GAAA,gBAAA,CAAA,EAAA,WAAA,0BAAA,CAAA;GAAA,UAAA;GAAA,UAFrC;GAAqD,UAAA;GAAA,eAAA,GAAA,kBAAA;GAAA,qBAAA;EAAA,CAAA;CAAA;;AAE5C,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAA4B,YAAA,CAAA;EAThD,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,YAAY;GACZ,SAAS,CAAA;GACT,gBAAgB,CAAC,yBAAyB;GAC1C,qBAAqB;GACrB,eAAe,kBAAkB;GACjC,UAAU;EACX,CAAA;;CAUE,gBAAA,EAAA,SAAA,CAAA,EAAA,MAAA,MAAA,CAAA,EAAA;;;;ACAH,IAAqB,0BAArB,MAAqB,wBAAuB;CA6B1C,cAAA;EA5BQ,KAAA,YAAY,OAAO,YAAY;EAC/B,KAAA,QAAQ,OAAO,cAAc;EAC7B,KAAA,OAAO,OAAO,MAAM;EACX,KAAA,aAAa,OAAO,WAAW;EAC/B,KAAA,gBAAgB,OAAO,sBAAsB,EAC5D,UAAU,KACX,CAAA;EAGO,KAAA,gBAAuD,SAC7D,KAAK,iBAAgB,CAAE;EAEhB,KAAA,cAAqD,eAAc;GAC1E,MAAM,eAAe,KAAK,QAAO;GAEjC,IAAI,cACF,OAAO,KAAK,UAAU,wBAAwB,YAAsB;GAGtE,OAAO,KAAK,cAAa;EAC3B,GAAE,GAAA,CAAA,CAAA;EACO,KAAA,UAA2D,MAEjE,GAAA,CAAA,CAAA;EACM,KAAA,UAA+B,MAAM,mBAAkB,GAAA,CAAA,CAAA;EAEhE,KAAA,kBAAmC,OAAO,eAAe;EAGvD,IAAI,kBAAkB,KAAK,UAAU,KAAK,KAAK,eAE7C,KAAK,YAAY,KAAK,aAAa;CAEvC;CAEA,mBAAgB;EACd,OAAO,KAAK,MAAM,KAAK,KACrB,KAAmB,SAAS,KAAK,qBAAqB,EAAE,GACxD,WAAW,kBAAkB,KAAK,cAAc,aAAa,CAAC,GAC9D,KAAK,YAAY,KAAK,UAAU,wBAAwB,OAAO,CAAC,GAChE,YAAY,MAAM,GAAG,sBAAsB,GAAG,CAAC,CAAC;CAEpD;CAEA,MAAM,cAAc,SAAe;EAEjC,QAAO,MADgB,KAAK,gBAAgB,OAAO,OAAO,EAAA,CAC1C;CAClB;CAEA,qBAAkB;EAChB,KAAK,gBAAgB,QAAO;EAC5B,KAAK,KAAK,wBAAwB,KAAK,SAAS,QAAQ,IAAG,CAAE;CAC/D;CAEQ,YAAY,eAAgD;EAClE,KAAK,KAAK,wBAGR,KAAK,aAAa,CAAC,CAChB,KAAK,mBAAkB,CAAE,CAAC,CAC1B,WAAW,YAAW;GACrB,KAAK,UAAU;GACf,KAAK,QAAQ,QAAQ,WAAW,EAAE,aAAa,MAAK,CAAE;GAGtD,KAAK,SAAS,QAAQ,IAAG;EAC3B,CAAC,CAAC;CAER;CArEmB;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAuB,MAAA,CAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAAvB;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAAuB,cAAA;GAAA,UAAA;GAAA,QAAA;IAAA,SAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,SAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;GAAA;GAAA,gBAAA,CAAA,EAAA,WAAA,0BAAA,CAAA;GAAA,UAAA;GAAA,UAFhC;GAA+D,UAAA;GAAA,eAAA,GAAA,kBAAA;GAAA,qBAAA;EAAA,CAAA;CAAA;;AAEtD,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAuB,YAAA,CAAA;EAR3C,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,YAAY;GACZ,gBAAgB,CAAC,yBAAyB;GAC1C,qBAAqB;GACrB,eAAe,kBAAkB;GACjC,UAAU;EACX,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import * as i0 from "@angular/core";
2
- import { Injectable, InjectionToken, TransferState, inject, makeStateKey, signal, ɵPendingTasksInternal } from "@angular/core";
2
+ import { Injectable, InjectionToken, PendingTasks, TransferState, inject, makeStateKey, signal } from "@angular/core";
3
3
  //#region packages/content/src/lib/content-renderer.ts
4
4
  var ContentRenderer = class ContentRenderer {
5
5
  async render(content) {
@@ -295,13 +295,12 @@ new InjectionToken("@analogjs/content Content Files", {
295
295
  //#endregion
296
296
  //#region packages/content/src/lib/render-task.service.ts
297
297
  var RenderTaskService = class RenderTaskService {
298
- #pendingTasks = inject(ɵPendingTasksInternal);
298
+ #pendingTasks = inject(PendingTasks);
299
299
  addRenderTask() {
300
300
  return this.#pendingTasks.add();
301
301
  }
302
302
  clearRenderTask(clear) {
303
- if (typeof clear === "function") clear();
304
- else if (typeof this.#pendingTasks.remove === "function") this.#pendingTasks.remove(clear);
303
+ clear();
305
304
  }
306
305
  static {
307
306
  this.ɵfac = i0.ɵɵngDeclareFactory({
@@ -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 // 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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"}
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","import type { ContentFileLoader } from './parse-content-file';\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, ContentFileLoader>;\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';\nimport type { ContentFileLoader } from './parse-content-file';\n\nexport const CONTENT_FILES_TOKEN: InjectionToken<\n Record<string, ContentFileLoader>\n> = new InjectionToken<Record<string, ContentFileLoader>>(\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, ContentFileLoader> = {};\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] = value as ContentFileLoader;\n }\n });\n\n return objectUsingSlugAttribute;\n },\n },\n);\n\nexport const CONTENT_FILES_MAP_TOKEN: InjectionToken<\n Signal<Record<string, ContentFileLoader>>\n> = new InjectionToken<Signal<Record<string, ContentFileLoader>>>(\n '@analogjs/content Content Files',\n {\n providedIn: 'root',\n factory() {\n return signal(inject(CONTENT_FILES_TOKEN));\n },\n },\n);\n","import { Injectable, PendingTasks, inject } from '@angular/core';\n\n@Injectable()\nexport class RenderTaskService {\n #pendingTasks = inject(PendingTasks);\n\n addRenderTask(): () => void {\n return this.#pendingTasks.add();\n }\n\n clearRenderTask(clear: () => void): void {\n clear();\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 type { ContentFileLoader } from './parse-content-file';\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, ContentFileLoader> {\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';\nimport type { ContentFileLoader } from './parse-content-file';\n\ntype ContentFileLoaderFunction = () => Promise<\n Record<string, ContentFileLoader>\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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"}
@@ -0,0 +1,18 @@
1
+ //#region packages/content/src/lib/frontmatter-validation-error.ts
2
+ var FrontmatterValidationError = class extends Error {
3
+ constructor(issues, filename) {
4
+ const issueMessages = issues.map((i) => {
5
+ const path = i.path ? ` at "${i.path.map((p) => typeof p === "object" ? p.key : p).join(".")}"` : "";
6
+ return ` - ${i.message}${path}`;
7
+ }).join("\n");
8
+ const prefix = filename ? `"${filename}" f` : "F";
9
+ super(`${prefix}rontmatter validation failed:\n${issueMessages}`);
10
+ this.issues = issues;
11
+ this.filename = filename;
12
+ this.name = "FrontmatterValidationError";
13
+ }
14
+ };
15
+ //#endregion
16
+ export { FrontmatterValidationError as t };
17
+
18
+ //# sourceMappingURL=frontmatter-validation-error.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter-validation-error.mjs","names":[],"sources":["../../src/lib/frontmatter-validation-error.ts"],"sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec';\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"],"mappings":";AAEA,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,CAAE,CAAC,CAAC,KAAK,GAAG,EAAC,KACxE;GACJ,OAAO,OAAO,EAAE,UAAU;EAC5B,CAAC,CAAC,CACD,KAAK,IAAI;EACZ,MAAM,SAAS,WAAW,IAAI,SAAQ,OAAQ;EAC9C,MAAM,GAAG,OAAM,iCAAkC,eAAe;EAZhD,KAAA,SAAA;EACA,KAAA,WAAA;EAYhB,KAAK,OAAO;CACd;AACD"}
@@ -1,18 +1,23 @@
1
+ import { t as FrontmatterValidationError } from "./frontmatter-validation-error.mjs";
1
2
  import fm from "front-matter";
2
- //#region packages/content/src/lib/parse-raw-content-file.ts
3
- var FrontmatterValidationError = class extends Error {
4
- constructor(issues, filename) {
5
- const issueMessages = issues.map((i) => {
6
- const path = i.path ? ` at "${i.path.map((p) => typeof p === "object" ? p.key : p).join(".")}"` : "";
7
- return ` - ${i.message}${path}`;
8
- }).join("\n");
9
- const prefix = filename ? `"${filename}" f` : "F";
10
- super(`${prefix}rontmatter validation failed:\n${issueMessages}`);
11
- this.issues = issues;
12
- this.filename = filename;
13
- this.name = "FrontmatterValidationError";
14
- }
3
+ //#region \0rolldown/runtime.js
4
+ var __defProp = Object.defineProperty;
5
+ var __exportAll = (all, no_symbols) => {
6
+ let target = {};
7
+ for (var name in all) __defProp(target, name, {
8
+ get: all[name],
9
+ enumerable: true
10
+ });
11
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
12
+ return target;
15
13
  };
14
+ //#endregion
15
+ //#region packages/content/src/lib/parse-raw-content-file.ts
16
+ var parse_raw_content_file_exports = /* @__PURE__ */ __exportAll({
17
+ FrontmatterValidationError: () => FrontmatterValidationError,
18
+ parseRawContentFile: () => parseRawContentFile,
19
+ parseRawContentFileAsync: () => parseRawContentFileAsync
20
+ });
16
21
  function parseRawContentFile(rawContentFile, schema, filename) {
17
22
  const { body, attributes } = fm(rawContentFile);
18
23
  if (schema) {
@@ -40,6 +45,6 @@ async function parseRawContentFileAsync(rawContentFile, schema, filename) {
40
45
  };
41
46
  }
42
47
  //#endregion
43
- export { parseRawContentFile as n, parseRawContentFileAsync as r, FrontmatterValidationError as t };
48
+ export { parseRawContentFileAsync as n, parse_raw_content_file_exports as r, parseRawContentFile as t };
44
49
 
45
50
  //# sourceMappingURL=parse-raw-content-file.mjs.map
@@ -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,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,CAAE,CAAC,CAAC,KAAK,GAAG,EAAC,KACxE;GACJ,OAAO,OAAO,EAAE,UAAU;EAC5B,CAAC,CAAC,CACD,KAAK,IAAI;EACZ,MAAM,SAAS,WAAW,IAAI,SAAQ,OAAQ;EAC9C,MAAM,GAAG,OAAM,iCAAkC,eAAe;EAZhD,KAAA,SAAA;EACA,KAAA,WAAA;EAYhB,KAAK,OAAO;CACd;AACD;AAYD,SAAgB,oBACd,gBACA,QACA,UAAiB;CAEjB,MAAM,EAAE,MAAM,eAAe,GAAG,cAAc;CAE9C,IAAI,QAAQ;EACV,MAAM,SAAS,OAAO,YAAY,CAAC,SAAS,UAAU;EACtD,IACE,UAAU,QACV,OAAQ,OAAgC,SAAS,YAEjD,MAAM,IAAI,MACR,iHACqD;EAGzD,MAAM,aAAa;EAGnB,IAAI,WAAW,QACb,MAAM,IAAI,2BAA2B,WAAW,QAAQ,QAAQ;EAElE,OAAO;GAAE,SAAS;GAAM,YAAY,WAAW;EAAK;CACtD;CAEA,OAAO;EAAE,SAAS;EAAM;CAAU;AACpC;AAEA,eAAsB,yBAGpB,gBACA,QACA,UAAiB;CAKjB,MAAM,EAAE,MAAM,eAAe,GAAG,cAAc;CAC9C,MAAM,SAAS,MAAM,OAAO,YAAY,CAAC,SAAS,UAAU;CAC5D,IAAI,OAAO,QACT,MAAM,IAAI,2BAA2B,OAAO,QAAQ,QAAQ;CAE9D,OAAO;EAAE,SAAS;EAAM,YAAY,OAAO;CAAK;AAClD"}
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\nimport { FrontmatterValidationError } from './frontmatter-validation-error';\n\nexport { FrontmatterValidationError };\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":";;;;;;;;;;;;;;;;;;;;AAiBA,SAAgB,oBACd,gBACA,QACA,UAAiB;CAEjB,MAAM,EAAE,MAAM,eAAe,GAAG,cAAc;CAE9C,IAAI,QAAQ;EACV,MAAM,SAAS,OAAO,YAAY,CAAC,SAAS,UAAU;EACtD,IACE,UAAU,QACV,OAAQ,OAAgC,SAAS,YAEjD,MAAM,IAAI,MACR,iHACqD;EAGzD,MAAM,aAAa;EAGnB,IAAI,WAAW,QACb,MAAM,IAAI,2BAA2B,WAAW,QAAQ,QAAQ;EAElE,OAAO;GAAE,SAAS;GAAM,YAAY,WAAW;EAAK;CACtD;CAEA,OAAO;EAAE,SAAS;EAAM;CAAU;AACpC;AAEA,eAAsB,yBAGpB,gBACA,QACA,UAAiB;CAKjB,MAAM,EAAE,MAAM,eAAe,GAAG,cAAc;CAC9C,MAAM,SAAS,MAAM,OAAO,YAAY,CAAC,SAAS,UAAU;CAC5D,IAAI,OAAO,QACT,MAAM,IAAI,2BAA2B,OAAO,QAAQ,QAAQ;CAE9D,OAAO;EAAE,SAAS;EAAM,YAAY,OAAO;CAAK;AAClD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@analogjs/content",
3
- "version": "3.0.0-beta.2",
3
+ "version": "3.0.0-beta.20",
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": "^23.2.0",
30
+ "@nx/devkit": "^21.2.0 || ^22.0.0 || ^23.0.0 || ^24.0.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,9 @@
90
90
  "tslib": "^2.3.0"
91
91
  },
92
92
  "devDependencies": {
93
- "@analogjs/vite-plugin-angular": "3.0.0-beta.2",
94
- "@analogjs/vitest-angular": "3.0.0-beta.2"
93
+ "@analogjs/vite-plugin-angular": "3.0.0-beta.20",
94
+ "@analogjs/vitest-angular": "3.0.0-beta.20",
95
+ "@nx/devkit": "24.0.0"
95
96
  },
96
97
  "ng-update": {
97
98
  "packageGroup": [
@@ -8,7 +8,10 @@ export type { InjectContentFilesFilterFunction } from './lib/inject-content-file
8
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
- export { parseRawContentFile, parseRawContentFileAsync, FrontmatterValidationError, } from './lib/parse-raw-content-file';
11
+ export { parseRawContentFile, parseRawContentFileAsync, } from './lib/parse-raw-content-file';
12
+ export { FrontmatterValidationError } from './lib/frontmatter-validation-error';
13
+ export { parseContentFile } from './lib/parse-content-file';
14
+ export type { ContentFileLoader, ContentFileModule, } from './lib/parse-content-file';
12
15
  export { injectContentFilesMap } from './lib/inject-content-files';
13
16
  export { injectContentListLoader, withContentListLoader, CONTENT_LIST_LOADER, } from './lib/content-list-loader';
14
17
  export { injectContentFileLoader, withContentFileLoader, CONTENT_FILE_LOADER, } from './lib/content-file-loader';
@@ -1,5 +1,6 @@
1
1
  import { InjectionToken, Provider } from '@angular/core';
2
- type ContentFileLoaderFunction = () => Promise<Record<string, () => Promise<string>>>;
2
+ import type { ContentFileLoader } from './parse-content-file';
3
+ type ContentFileLoaderFunction = () => Promise<Record<string, ContentFileLoader>>;
3
4
  export declare const CONTENT_FILE_LOADER: InjectionToken<ContentFileLoaderFunction>;
4
5
  export declare function injectContentFileLoader(): ContentFileLoaderFunction;
5
6
  export declare function withContentFileLoader(): Provider;
@@ -1,3 +1,4 @@
1
1
  import { InjectionToken, Signal } from '@angular/core';
2
- export declare const CONTENT_FILES_TOKEN: InjectionToken<Record<string, () => Promise<string>>>;
3
- export declare const CONTENT_FILES_MAP_TOKEN: InjectionToken<Signal<Record<string, () => Promise<string>>>>;
2
+ import type { ContentFileLoader } from './parse-content-file';
3
+ export declare const CONTENT_FILES_TOKEN: InjectionToken<Record<string, ContentFileLoader>>;
4
+ export declare const CONTENT_FILES_MAP_TOKEN: InjectionToken<Signal<Record<string, ContentFileLoader>>>;
@@ -0,0 +1,6 @@
1
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
2
+ export declare class FrontmatterValidationError extends Error {
3
+ readonly issues: ReadonlyArray<StandardSchemaV1.Issue>;
4
+ readonly filename?: string | undefined;
5
+ constructor(issues: ReadonlyArray<StandardSchemaV1.Issue>, filename?: string | undefined);
6
+ }
@@ -1,3 +1,4 @@
1
+ import type { ContentFileLoader } from './parse-content-file';
1
2
  /**
2
3
  * These bindings are rewritten at build time:
3
4
  * - `ANALOG_CONTENT_FILE_LIST` by `@analogjs/platform`'s content plugin,
@@ -26,4 +27,4 @@ export declare const getContentFilesList: () => Record<string, Record<string, an
26
27
  /**
27
28
  * Returns the lazy loaded content files for lookups.
28
29
  */
29
- export declare const getContentFiles: () => Record<string, () => Promise<string>>;
30
+ export declare const getContentFiles: () => Record<string, ContentFileLoader>;
@@ -1,4 +1,5 @@
1
1
  import { ContentFile } from './content-file';
2
+ import type { ContentFileLoader } from './parse-content-file';
2
3
  export declare function injectContentFiles<Attributes extends Record<string, any>>(filterFn?: InjectContentFilesFilterFunction<Attributes>): ContentFile<Attributes>[];
3
4
  export type InjectContentFilesFilterFunction<T extends Record<string, any>> = (value: ContentFile<T>, index: number, array: ContentFile<T>[]) => boolean;
4
- export declare function injectContentFilesMap(): Record<string, () => Promise<string>>;
5
+ export declare function injectContentFilesMap(): Record<string, ContentFileLoader>;
@@ -0,0 +1,14 @@
1
+ export type ContentFileModule<Attributes extends Record<string, any> = Record<string, any>> = {
2
+ default: string;
3
+ metadata: Attributes;
4
+ };
5
+ export type ContentFileLoader = () => Promise<string | ContentFileModule>;
6
+ /**
7
+ * Reads a loaded content file. Build-time content modules carry their
8
+ * frontmatter as `metadata`; raw strings from custom loaders are parsed
9
+ * with a lazily loaded frontmatter parser.
10
+ */
11
+ export declare function parseContentFile<Attributes extends Record<string, any> = Record<string, any>>(file: string | ContentFileModule): Promise<{
12
+ content: string;
13
+ attributes: Attributes;
14
+ }>;
@@ -1,9 +1,6 @@
1
1
  import type { StandardSchemaV1 } from '@standard-schema/spec';
2
- export declare class FrontmatterValidationError extends Error {
3
- readonly issues: ReadonlyArray<StandardSchemaV1.Issue>;
4
- readonly filename?: string | undefined;
5
- constructor(issues: ReadonlyArray<StandardSchemaV1.Issue>, filename?: string | undefined);
6
- }
2
+ import { FrontmatterValidationError } from './frontmatter-validation-error';
3
+ export { FrontmatterValidationError };
7
4
  export declare function parseRawContentFile<Attributes extends Record<string, any> = Record<string, any>>(rawContentFile: string): {
8
5
  content: string;
9
6
  attributes: Attributes;
@@ -1,8 +1,8 @@
1
1
  import * as i0 from "@angular/core";
2
2
  export declare class RenderTaskService {
3
3
  #private;
4
- addRenderTask(): number;
5
- clearRenderTask(clear: number | (() => void)): void;
4
+ addRenderTask(): () => void;
5
+ clearRenderTask(clear: () => void): void;
6
6
  static ɵfac: i0.ɵɵFactoryDeclaration<RenderTaskService, never>;
7
7
  static ɵprov: i0.ɵɵInjectableDeclaration<any>;
8
8
  }