@analogjs/content 2.8.0-beta.1 → 2.8.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/analogjs-content-og.mjs +31 -45
- package/fesm2022/analogjs-content-og.mjs.map +1 -1
- package/fesm2022/analogjs-content-prism-highlighter.mjs +75 -68
- package/fesm2022/analogjs-content-prism-highlighter.mjs.map +1 -1
- package/fesm2022/analogjs-content-resources.mjs +108 -145
- package/fesm2022/analogjs-content-resources.mjs.map +1 -1
- package/fesm2022/analogjs-content-shiki-highlighter.mjs +7 -14
- package/fesm2022/analogjs-content-shiki-highlighter.mjs.map +1 -1
- package/fesm2022/analogjs-content.mjs +747 -760
- package/fesm2022/analogjs-content.mjs.map +1 -1
- package/package.json +1 -1
- package/types/analogjs-content-og.d.ts +14 -18
- package/types/analogjs-content-prism-highlighter.d.ts +12 -13
- package/types/analogjs-content-resources.d.ts +5 -11
- package/types/analogjs-content-shiki-highlighter.d.ts +6 -10
- package/types/analogjs-content.d.ts +159 -152
|
@@ -1,807 +1,794 @@
|
|
|
1
|
-
import * as i0 from
|
|
2
|
-
import {
|
|
3
|
-
import { DOCUMENT, Location,
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import fm from
|
|
8
|
-
import {
|
|
9
|
-
import { marked } from
|
|
10
|
-
import { mangle } from
|
|
11
|
-
import { DomSanitizer } from
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import { Component, Directive, HostListener, Injectable, InjectionToken, Input, NgZone, PLATFORM_ID, TransferState, ViewEncapsulation, computed, inject, input, makeStateKey, signal, ɵPendingTasksInternal } from "@angular/core";
|
|
3
|
+
import { AsyncPipe, DOCUMENT, Location, isPlatformBrowser } from "@angular/common";
|
|
4
|
+
import { ActivatedRoute, Router } from "@angular/router";
|
|
5
|
+
import { Observable, firstValueFrom, from, isObservable, of } from "rxjs";
|
|
6
|
+
import { catchError, finalize, map, switchMap, tap } from "rxjs/operators";
|
|
7
|
+
import fm from "front-matter";
|
|
8
|
+
import { getHeadingList, gfmHeadingId } from "marked-gfm-heading-id";
|
|
9
|
+
import { marked } from "marked";
|
|
10
|
+
import { mangle } from "marked-mangle";
|
|
11
|
+
import { DomSanitizer } from "@angular/platform-browser";
|
|
12
|
+
import { takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop";
|
|
13
|
+
var AnchorNavigationDirective = class AnchorNavigationDirective {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.document = inject(DOCUMENT);
|
|
16
|
+
this.location = inject(Location);
|
|
17
|
+
this.router = inject(Router);
|
|
18
|
+
}
|
|
19
|
+
handleNavigation(element) {
|
|
20
|
+
if (element instanceof HTMLAnchorElement && isInternalUrl(element, this.document) && hasTargetSelf(element) && !hasDownloadAttribute(element)) {
|
|
21
|
+
const { pathname, search, hash } = element;
|
|
22
|
+
const isFragmentOnly = element.getAttribute("href")?.startsWith("#");
|
|
23
|
+
const resolvedPathname = isFragmentOnly ? this.document.location.pathname : pathname;
|
|
24
|
+
const resolvedSearch = isFragmentOnly ? this.document.location.search : search;
|
|
25
|
+
const url = this.location.normalize(`${resolvedPathname}${resolvedSearch}${hash}`);
|
|
26
|
+
this.router.navigateByUrl(url);
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
static {
|
|
32
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
33
|
+
minVersion: "12.0.0",
|
|
34
|
+
version: "22.2.0",
|
|
35
|
+
ngImport: i0,
|
|
36
|
+
type: AnchorNavigationDirective,
|
|
37
|
+
deps: [],
|
|
38
|
+
target: i0.ɵɵFactoryTarget.Directive
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
static {
|
|
42
|
+
this.ɵdir = i0.ɵɵngDeclareDirective({
|
|
43
|
+
minVersion: "14.0.0",
|
|
44
|
+
version: "22.2.0",
|
|
45
|
+
type: AnchorNavigationDirective,
|
|
46
|
+
isStandalone: true,
|
|
47
|
+
selector: "[analogAnchorNavigation]",
|
|
48
|
+
host: { listeners: { "click": "handleNavigation($event.target)" } },
|
|
49
|
+
ngImport: i0
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
54
|
+
minVersion: "12.0.0",
|
|
55
|
+
version: "22.2.0",
|
|
56
|
+
ngImport: i0,
|
|
57
|
+
type: AnchorNavigationDirective,
|
|
58
|
+
decorators: [{
|
|
59
|
+
type: Directive,
|
|
60
|
+
args: [{
|
|
61
|
+
selector: "[analogAnchorNavigation]",
|
|
62
|
+
standalone: true
|
|
63
|
+
}]
|
|
64
|
+
}],
|
|
65
|
+
propDecorators: { handleNavigation: [{
|
|
66
|
+
type: HostListener,
|
|
67
|
+
args: ["click", ["$event.target"]]
|
|
68
|
+
}] }
|
|
69
|
+
});
|
|
58
70
|
function hasDownloadAttribute(anchorElement) {
|
|
59
|
-
|
|
71
|
+
return anchorElement.getAttribute("download") !== null;
|
|
60
72
|
}
|
|
61
73
|
function hasTargetSelf(anchorElement) {
|
|
62
|
-
|
|
74
|
+
return !anchorElement.target || anchorElement.target === "_self";
|
|
63
75
|
}
|
|
64
76
|
function isInternalUrl(anchorElement, document) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
* When set, `injectContentFiles()` filters to content matching this locale,
|
|
193
|
-
* and `injectContent()` resolves locale-prefixed content paths first.
|
|
194
|
-
*/
|
|
195
|
-
const CONTENT_LOCALE = new InjectionToken('@analogjs/content Locale');
|
|
196
|
-
/**
|
|
197
|
-
* Injects the content locale, returning null if not configured.
|
|
198
|
-
*/
|
|
77
|
+
return anchorElement.host === document.location.host && anchorElement.protocol === document.location.protocol;
|
|
78
|
+
}
|
|
79
|
+
var ContentRenderer = class ContentRenderer {
|
|
80
|
+
async render(content) {
|
|
81
|
+
return {
|
|
82
|
+
content,
|
|
83
|
+
toc: []
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
getContentHeadings(_content) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
enhance() {}
|
|
90
|
+
static {
|
|
91
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
92
|
+
minVersion: "12.0.0",
|
|
93
|
+
version: "22.2.0",
|
|
94
|
+
ngImport: i0,
|
|
95
|
+
type: ContentRenderer,
|
|
96
|
+
deps: [],
|
|
97
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
static {
|
|
101
|
+
this.ɵprov = i0.ɵɵngDeclareInjectable({
|
|
102
|
+
minVersion: "12.0.0",
|
|
103
|
+
version: "22.2.0",
|
|
104
|
+
ngImport: i0,
|
|
105
|
+
type: ContentRenderer
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
110
|
+
minVersion: "12.0.0",
|
|
111
|
+
version: "22.2.0",
|
|
112
|
+
ngImport: i0,
|
|
113
|
+
type: ContentRenderer,
|
|
114
|
+
decorators: [{ type: Injectable }]
|
|
115
|
+
});
|
|
116
|
+
var NoopContentRenderer = class {
|
|
117
|
+
constructor() {
|
|
118
|
+
this.transferState = inject(TransferState);
|
|
119
|
+
this.contentId = 0;
|
|
120
|
+
}
|
|
121
|
+
generateHash(str) {
|
|
122
|
+
let hash = 0;
|
|
123
|
+
for (let i = 0, len = str.length; i < len; i++) {
|
|
124
|
+
let chr = str.charCodeAt(i);
|
|
125
|
+
hash = (hash << 5) - hash + chr;
|
|
126
|
+
hash |= 0;
|
|
127
|
+
}
|
|
128
|
+
return hash;
|
|
129
|
+
}
|
|
130
|
+
async render(content) {
|
|
131
|
+
this.contentId = this.generateHash(content);
|
|
132
|
+
const toc = this.getContentHeadings(content);
|
|
133
|
+
const key = makeStateKey(`content-headings-${this.contentId}`);
|
|
134
|
+
if (import.meta.env.SSR === true) {
|
|
135
|
+
this.transferState.set(key, toc);
|
|
136
|
+
return {
|
|
137
|
+
content,
|
|
138
|
+
toc
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
content,
|
|
143
|
+
toc: this.transferState.get(key, toc)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
enhance() {}
|
|
147
|
+
getContentHeadings(content) {
|
|
148
|
+
return this.extractHeadings(content);
|
|
149
|
+
}
|
|
150
|
+
extractHeadings(content) {
|
|
151
|
+
const markdownHeadings = this.extractHeadingsFromMarkdown(content);
|
|
152
|
+
if (markdownHeadings.length > 0) return markdownHeadings;
|
|
153
|
+
return this.extractHeadingsFromHtml(content);
|
|
154
|
+
}
|
|
155
|
+
extractHeadingsFromMarkdown(content) {
|
|
156
|
+
const lines = content.split("\n");
|
|
157
|
+
const toc = [];
|
|
158
|
+
const slugCounts = /* @__PURE__ */ new Map();
|
|
159
|
+
for (const line of lines) {
|
|
160
|
+
const match = /^(#{1,6})\s+(.+?)\s*$/.exec(line);
|
|
161
|
+
if (!match) continue;
|
|
162
|
+
const level = match[1].length;
|
|
163
|
+
const text = match[2].trim();
|
|
164
|
+
if (!text) continue;
|
|
165
|
+
const baseSlug = text.toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-");
|
|
166
|
+
const count = slugCounts.get(baseSlug) ?? 0;
|
|
167
|
+
slugCounts.set(baseSlug, count + 1);
|
|
168
|
+
const id = count === 0 ? baseSlug : `${baseSlug}-${count}`;
|
|
169
|
+
toc.push({
|
|
170
|
+
id,
|
|
171
|
+
level,
|
|
172
|
+
text
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return toc;
|
|
176
|
+
}
|
|
177
|
+
extractHeadingsFromHtml(content) {
|
|
178
|
+
const toc = [];
|
|
179
|
+
const slugCounts = /* @__PURE__ */ new Map();
|
|
180
|
+
for (const match of content.matchAll(/<h([1-6])([^>]*)>([\s\S]*?)<\/h\1>/gi)) {
|
|
181
|
+
const level = Number(match[1]);
|
|
182
|
+
const attrs = match[2] ?? "";
|
|
183
|
+
const text = (match[3] ?? "").replace(/<[^>]+>/g, "").trim();
|
|
184
|
+
if (!text) continue;
|
|
185
|
+
const idMatch = /\sid=(['"])(.*?)\1/i.exec(attrs) ?? /\sid=([^\s>]+)/i.exec(attrs);
|
|
186
|
+
let id = idMatch?.[2] ?? idMatch?.[1] ?? "";
|
|
187
|
+
if (!id) id = this.makeSlug(text, slugCounts);
|
|
188
|
+
toc.push({
|
|
189
|
+
id,
|
|
190
|
+
level,
|
|
191
|
+
text
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return toc;
|
|
195
|
+
}
|
|
196
|
+
makeSlug(text, slugCounts) {
|
|
197
|
+
const baseSlug = text.toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-");
|
|
198
|
+
const count = slugCounts.get(baseSlug) ?? 0;
|
|
199
|
+
slugCounts.set(baseSlug, count + 1);
|
|
200
|
+
return count === 0 ? baseSlug : `${baseSlug}-${count}`;
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
const CONTENT_LOCALE = new InjectionToken("@analogjs/content Locale");
|
|
199
204
|
function injectContentLocale() {
|
|
200
|
-
|
|
205
|
+
return inject(CONTENT_LOCALE, { optional: true });
|
|
201
206
|
}
|
|
202
|
-
/**
|
|
203
|
-
* Content feature that sets the active locale for content resolution.
|
|
204
|
-
*
|
|
205
|
-
* When provided, content APIs become locale-aware:
|
|
206
|
-
* - `injectContentFiles()` filters to files in the locale subdirectory
|
|
207
|
-
* or with a matching `locale` frontmatter attribute.
|
|
208
|
-
* - `injectContent()` tries locale-prefixed paths first
|
|
209
|
-
* (e.g., `content/fr/blog/post.md` before `content/blog/post.md`).
|
|
210
|
-
*
|
|
211
|
-
* Usage:
|
|
212
|
-
* ```typescript
|
|
213
|
-
* // With loader — runs in injection context
|
|
214
|
-
* provideContent(
|
|
215
|
-
* withMarkdownRenderer(),
|
|
216
|
-
* withLocale({ loadLocale: injectLocale }),
|
|
217
|
-
* )
|
|
218
|
-
*
|
|
219
|
-
* // Static locale
|
|
220
|
-
* provideContent(
|
|
221
|
-
* withMarkdownRenderer(),
|
|
222
|
-
* withLocale('fr'),
|
|
223
|
-
* )
|
|
224
|
-
* ```
|
|
225
|
-
*/
|
|
226
207
|
function withLocale(locale) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
208
|
+
if (typeof locale === "string") return {
|
|
209
|
+
provide: CONTENT_LOCALE,
|
|
210
|
+
useValue: locale
|
|
211
|
+
};
|
|
212
|
+
return {
|
|
213
|
+
provide: CONTENT_LOCALE,
|
|
214
|
+
useFactory: locale.loadLocale
|
|
215
|
+
};
|
|
231
216
|
}
|
|
232
|
-
/**
|
|
233
|
-
* Filters content files by locale using map-based key lookup.
|
|
234
|
-
*
|
|
235
|
-
* Matching rules:
|
|
236
|
-
* 1. Frontmatter `locale` attribute matches the active locale.
|
|
237
|
-
* 2. File is in the active locale subdirectory (e.g., `/content/fr/blog/post`).
|
|
238
|
-
* 3. File has no locale marker and no localized variant exists — included as universal content.
|
|
239
|
-
*
|
|
240
|
-
* Files in a different locale's subdirectory are always excluded.
|
|
241
|
-
*/
|
|
242
217
|
function filterByLocale(files, locale) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return files.filter((file) => {
|
|
260
|
-
// Frontmatter locale attribute takes priority
|
|
261
|
-
if (file.attributes['locale']) {
|
|
262
|
-
return file.attributes['locale'] === locale;
|
|
263
|
-
}
|
|
264
|
-
// File is in the active locale subdirectory — include
|
|
265
|
-
if (file.filename.includes(localePrefix)) {
|
|
266
|
-
return true;
|
|
267
|
-
}
|
|
268
|
-
// File is in a different locale's subdirectory — exclude
|
|
269
|
-
for (const prefix of allLocalePrefixes) {
|
|
270
|
-
if (prefix !== localePrefix && file.filename.includes(prefix)) {
|
|
271
|
-
return false;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
// Universal content — include only if no localized variant exists
|
|
275
|
-
return !localizedBasePaths.has(file.filename);
|
|
276
|
-
});
|
|
218
|
+
const localePrefix = `/content/${locale}/`;
|
|
219
|
+
const allLocalePrefixes = /* @__PURE__ */ new Set();
|
|
220
|
+
for (const file of files) {
|
|
221
|
+
const match = file.filename.match(/\/content\/([a-z]{2}(?:-[a-zA-Z]+)?)\//);
|
|
222
|
+
if (match) allLocalePrefixes.add(`/content/${match[1]}/`);
|
|
223
|
+
}
|
|
224
|
+
const localizedBasePaths = /* @__PURE__ */ new Set();
|
|
225
|
+
for (const file of files) if (file.filename.includes(localePrefix)) localizedBasePaths.add(file.filename.replace(localePrefix, "/content/"));
|
|
226
|
+
return files.filter((file) => {
|
|
227
|
+
if (file.attributes["locale"]) return file.attributes["locale"] === locale;
|
|
228
|
+
if (file.filename.includes(localePrefix)) return true;
|
|
229
|
+
for (const prefix of allLocalePrefixes) if (prefix !== localePrefix && file.filename.includes(prefix)) return false;
|
|
230
|
+
return !localizedBasePaths.has(file.filename);
|
|
231
|
+
});
|
|
277
232
|
}
|
|
278
|
-
/**
|
|
279
|
-
* Prepends locale-prefixed candidates before the standard candidates
|
|
280
|
-
* for content file map key lookup.
|
|
281
|
-
*/
|
|
282
233
|
function withLocaleCandidates(candidates, locale) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
const localeCandidates = candidates.map((c) => c.replace('/src/content/', `/src/content/${locale}/`));
|
|
287
|
-
return [...localeCandidates, ...candidates];
|
|
234
|
+
if (!locale) return candidates;
|
|
235
|
+
return [...candidates.map((c) => c.replace("/src/content/", `/src/content/${locale}/`)), ...candidates];
|
|
288
236
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
* Returns the list of content files by filename with ?analog-content-list=true.
|
|
292
|
-
* We use the query param to transform the return into an array of
|
|
293
|
-
* just front matter attributes.
|
|
294
|
-
*
|
|
295
|
-
* @returns
|
|
296
|
-
*/
|
|
237
|
+
let ANALOG_CONTENT_FILE_LIST = {};
|
|
238
|
+
let ANALOG_CONTENT_ROUTE_FILES = {};
|
|
297
239
|
const getContentFilesList = () => {
|
|
298
|
-
|
|
299
|
-
return ANALOG_CONTENT_FILE_LIST;
|
|
240
|
+
return ANALOG_CONTENT_FILE_LIST;
|
|
300
241
|
};
|
|
301
|
-
/**
|
|
302
|
-
* Returns the lazy loaded content files for lookups.
|
|
303
|
-
*
|
|
304
|
-
* @returns
|
|
305
|
-
*/
|
|
306
242
|
const getContentFiles = () => {
|
|
307
|
-
|
|
308
|
-
return ANALOG_CONTENT_ROUTE_FILES;
|
|
243
|
+
return ANALOG_CONTENT_ROUTE_FILES;
|
|
309
244
|
};
|
|
310
|
-
|
|
311
245
|
function getSlug(filename) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
slug: slug ? encodeURI(slug) : encodeURI(getSlug(filename)),
|
|
330
|
-
};
|
|
331
|
-
});
|
|
332
|
-
},
|
|
246
|
+
const base = (filename.split(/[/\\]/).pop() || "").trim().replace(/\.[^./\\]+$/, "");
|
|
247
|
+
return base === "index" ? "" : base;
|
|
248
|
+
}
|
|
249
|
+
const CONTENT_FILES_LIST_TOKEN = new InjectionToken("@analogjs/content Content Files List", {
|
|
250
|
+
providedIn: "root",
|
|
251
|
+
factory() {
|
|
252
|
+
const contentFiles = getContentFilesList();
|
|
253
|
+
return Object.keys(contentFiles).map((filename) => {
|
|
254
|
+
const attributes = contentFiles[filename];
|
|
255
|
+
const slug = attributes["slug"];
|
|
256
|
+
return {
|
|
257
|
+
filename,
|
|
258
|
+
attributes,
|
|
259
|
+
slug: slug ? encodeURI(slug) : encodeURI(getSlug(filename))
|
|
260
|
+
};
|
|
261
|
+
});
|
|
262
|
+
}
|
|
333
263
|
});
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
Object.entries(allFiles).forEach((entry) => {
|
|
366
|
-
const filename = entry[0];
|
|
367
|
-
const value = entry[1];
|
|
368
|
-
const strippedFilename = filename.replace(/^\/(.*?)\/content/, '/src/content');
|
|
369
|
-
const newFilename = lookup[strippedFilename];
|
|
370
|
-
if (newFilename !== undefined) {
|
|
371
|
-
const objectFilename = newFilename.replace(/^\/(.*?)\/content/, '/src/content');
|
|
372
|
-
objectUsingSlugAttribute[objectFilename] =
|
|
373
|
-
value;
|
|
374
|
-
}
|
|
375
|
-
});
|
|
376
|
-
return objectUsingSlugAttribute;
|
|
377
|
-
},
|
|
264
|
+
const CONTENT_FILES_TOKEN = new InjectionToken("@analogjs/content Content Files", {
|
|
265
|
+
providedIn: "root",
|
|
266
|
+
factory() {
|
|
267
|
+
const allFiles = { ...getContentFiles() };
|
|
268
|
+
const contentFilesList = inject(CONTENT_FILES_LIST_TOKEN);
|
|
269
|
+
const lookup = {};
|
|
270
|
+
contentFilesList.forEach((item) => {
|
|
271
|
+
const contentFilename = item.filename.replace(/(.*?)\/content/, "/src/content");
|
|
272
|
+
const fileParts = contentFilename.split("/");
|
|
273
|
+
const filePath = fileParts.slice(0, fileParts.length - 1).join("/");
|
|
274
|
+
const fileNameParts = fileParts[fileParts.length - 1].split(".");
|
|
275
|
+
const ext = fileNameParts[fileNameParts.length - 1];
|
|
276
|
+
let slug = item.slug ?? "";
|
|
277
|
+
if (slug === "") slug = "index";
|
|
278
|
+
const subdirRoot = fileParts.length > 4 ? fileParts.slice(0, 4).join("/") : "/src/content";
|
|
279
|
+
const newBase = slug.includes("/") ? `${subdirRoot}/${slug}` : `${filePath}/${slug}`;
|
|
280
|
+
lookup[contentFilename] = `${newBase}.${ext}`.replace(/\/{2,}/g, "/");
|
|
281
|
+
});
|
|
282
|
+
const objectUsingSlugAttribute = {};
|
|
283
|
+
Object.entries(allFiles).forEach((entry) => {
|
|
284
|
+
const filename = entry[0];
|
|
285
|
+
const value = entry[1];
|
|
286
|
+
const strippedFilename = filename.replace(/^\/(.*?)\/content/, "/src/content");
|
|
287
|
+
const newFilename = lookup[strippedFilename];
|
|
288
|
+
if (newFilename !== void 0) {
|
|
289
|
+
const objectFilename = newFilename.replace(/^\/(.*?)\/content/, "/src/content");
|
|
290
|
+
objectUsingSlugAttribute[objectFilename] = value;
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
return objectUsingSlugAttribute;
|
|
294
|
+
}
|
|
378
295
|
});
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
296
|
+
new InjectionToken("@analogjs/content Content Files", {
|
|
297
|
+
providedIn: "root",
|
|
298
|
+
factory() {
|
|
299
|
+
return signal(inject(CONTENT_FILES_TOKEN));
|
|
300
|
+
}
|
|
384
301
|
});
|
|
385
|
-
|
|
386
302
|
function parseRawContentFile(rawContentFile) {
|
|
387
|
-
|
|
388
|
-
|
|
303
|
+
const { body, attributes } = fm(rawContentFile);
|
|
304
|
+
return {
|
|
305
|
+
content: body,
|
|
306
|
+
attributes
|
|
307
|
+
};
|
|
389
308
|
}
|
|
390
|
-
|
|
391
309
|
async function waitFor(prom) {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
310
|
+
if (isObservable(prom)) prom = firstValueFrom(prom);
|
|
311
|
+
if (typeof Zone === "undefined") return prom;
|
|
312
|
+
const macroTask = Zone.current.scheduleMacroTask(`AnalogContentResolve-${Math.random()}`, () => {}, {}, () => {});
|
|
313
|
+
return prom.then((p) => {
|
|
314
|
+
macroTask.invoke();
|
|
315
|
+
return p;
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
var RenderTaskService = class RenderTaskService {
|
|
319
|
+
#pendingTasks = inject(ɵPendingTasksInternal);
|
|
320
|
+
addRenderTask() {
|
|
321
|
+
return this.#pendingTasks.add();
|
|
322
|
+
}
|
|
323
|
+
clearRenderTask(clear) {
|
|
324
|
+
if (typeof clear === "function") clear();
|
|
325
|
+
else if (typeof this.#pendingTasks.remove === "function") this.#pendingTasks.remove(clear);
|
|
326
|
+
}
|
|
327
|
+
static {
|
|
328
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
329
|
+
minVersion: "12.0.0",
|
|
330
|
+
version: "22.2.0",
|
|
331
|
+
ngImport: i0,
|
|
332
|
+
type: RenderTaskService,
|
|
333
|
+
deps: [],
|
|
334
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
static {
|
|
338
|
+
this.ɵprov = i0.ɵɵngDeclareInjectable({
|
|
339
|
+
minVersion: "12.0.0",
|
|
340
|
+
version: "22.2.0",
|
|
341
|
+
ngImport: i0,
|
|
342
|
+
type: RenderTaskService
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
347
|
+
minVersion: "12.0.0",
|
|
348
|
+
version: "22.2.0",
|
|
349
|
+
ngImport: i0,
|
|
350
|
+
type: RenderTaskService,
|
|
351
|
+
decorators: [{ type: Injectable }]
|
|
352
|
+
});
|
|
426
353
|
function getContentFile(contentFiles, prefix, slug, fallback, renderTaskService, contentRenderer, locale) {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
354
|
+
const normalizedFiles = {};
|
|
355
|
+
for (const [key, resolver] of Object.entries(contentFiles)) {
|
|
356
|
+
const normalizedKey = key.replace(/^(?:.*?)\/content(?=\/)/, "/src/content").replace(/\/{2,}/g, "/");
|
|
357
|
+
normalizedFiles[normalizedKey] = resolver;
|
|
358
|
+
}
|
|
359
|
+
const base = `/src/content/${prefix}${slug}`.replace(/\/{2,}/g, "/");
|
|
360
|
+
const matchKey = withLocaleCandidates([
|
|
361
|
+
`${base}.md`,
|
|
362
|
+
`${base}.agx`,
|
|
363
|
+
`${base}/index.md`,
|
|
364
|
+
`${base}/index.agx`
|
|
365
|
+
], locale).find((k) => k in normalizedFiles);
|
|
366
|
+
const contentFile = matchKey ? normalizedFiles[matchKey] : void 0;
|
|
367
|
+
const resolvedBase = (matchKey || `${base}.md`).replace(/\.(md|agx)$/, "");
|
|
368
|
+
if (!contentFile) return of({
|
|
369
|
+
filename: resolvedBase,
|
|
370
|
+
attributes: {},
|
|
371
|
+
slug: "",
|
|
372
|
+
content: fallback,
|
|
373
|
+
toc: []
|
|
374
|
+
});
|
|
375
|
+
const contentTask = renderTaskService.addRenderTask();
|
|
376
|
+
return new Observable((observer) => {
|
|
377
|
+
const contentResolver = contentFile();
|
|
378
|
+
if (import.meta.env.SSR === true) waitFor(contentResolver).then((content) => {
|
|
379
|
+
observer.next(content);
|
|
380
|
+
observer.complete();
|
|
381
|
+
setTimeout(() => renderTaskService.clearRenderTask(contentTask), 10);
|
|
382
|
+
});
|
|
383
|
+
else contentResolver.then((content) => {
|
|
384
|
+
observer.next(content);
|
|
385
|
+
observer.complete();
|
|
386
|
+
});
|
|
387
|
+
}).pipe(switchMap((contentFile) => {
|
|
388
|
+
if (typeof contentFile === "string") {
|
|
389
|
+
const { content, attributes } = parseRawContentFile(contentFile);
|
|
390
|
+
return from(contentRenderer.render(content)).pipe(map((rendered) => ({
|
|
391
|
+
filename: resolvedBase,
|
|
392
|
+
slug,
|
|
393
|
+
attributes,
|
|
394
|
+
content,
|
|
395
|
+
toc: rendered.toc ?? []
|
|
396
|
+
})));
|
|
397
|
+
}
|
|
398
|
+
return of({
|
|
399
|
+
filename: resolvedBase,
|
|
400
|
+
slug,
|
|
401
|
+
attributes: contentFile.metadata,
|
|
402
|
+
content: contentFile.default,
|
|
403
|
+
toc: []
|
|
404
|
+
});
|
|
405
|
+
}), finalize(() => {
|
|
406
|
+
if (import.meta.env.SSR !== true) renderTaskService.clearRenderTask(contentTask);
|
|
407
|
+
}));
|
|
408
|
+
}
|
|
409
|
+
function injectContent(param = "slug", fallback = "No Content Found") {
|
|
410
|
+
const contentFiles = inject(CONTENT_FILES_TOKEN);
|
|
411
|
+
const contentRenderer = inject(ContentRenderer);
|
|
412
|
+
const renderTaskService = inject(RenderTaskService);
|
|
413
|
+
const locale = inject(CONTENT_LOCALE, { optional: true });
|
|
414
|
+
const task = renderTaskService.addRenderTask();
|
|
415
|
+
if (typeof param === "string" || "param" in param) {
|
|
416
|
+
const prefix = typeof param === "string" ? "" : `${param.subdirectory}/`;
|
|
417
|
+
const route = inject(ActivatedRoute);
|
|
418
|
+
const paramKey = typeof param === "string" ? param : param.param;
|
|
419
|
+
return route.paramMap.pipe(map((params) => params.get(paramKey)), switchMap((slug) => {
|
|
420
|
+
if (slug) return getContentFile(contentFiles, prefix, slug, fallback, renderTaskService, contentRenderer, locale);
|
|
421
|
+
return of({
|
|
422
|
+
filename: "",
|
|
423
|
+
slug: "",
|
|
424
|
+
attributes: {},
|
|
425
|
+
content: fallback,
|
|
426
|
+
toc: []
|
|
427
|
+
});
|
|
428
|
+
}), tap(() => renderTaskService.clearRenderTask(task)));
|
|
429
|
+
} else return getContentFile(contentFiles, "", param.customFilename, fallback, renderTaskService, contentRenderer, locale).pipe(tap(() => renderTaskService.clearRenderTask(task)));
|
|
490
430
|
}
|
|
491
|
-
/**
|
|
492
|
-
* Retrieves the static content using the provided param and/or prefix.
|
|
493
|
-
*
|
|
494
|
-
* @param param route parameter (default: 'slug')
|
|
495
|
-
* @param fallback fallback text if content file is not found (default: 'No Content Found')
|
|
496
|
-
*/
|
|
497
|
-
function injectContent(param = 'slug', fallback = 'No Content Found') {
|
|
498
|
-
const contentFiles = inject(CONTENT_FILES_TOKEN);
|
|
499
|
-
const contentRenderer = inject(ContentRenderer);
|
|
500
|
-
const renderTaskService = inject(RenderTaskService);
|
|
501
|
-
const locale = inject(CONTENT_LOCALE, { optional: true });
|
|
502
|
-
const task = renderTaskService.addRenderTask();
|
|
503
|
-
if (typeof param === 'string' || 'param' in param) {
|
|
504
|
-
const prefix = typeof param === 'string' ? '' : `${param.subdirectory}/`;
|
|
505
|
-
const route = inject(ActivatedRoute);
|
|
506
|
-
const paramKey = typeof param === 'string' ? param : param.param;
|
|
507
|
-
return route.paramMap.pipe(map((params) => params.get(paramKey)), switchMap((slug) => {
|
|
508
|
-
if (slug) {
|
|
509
|
-
return getContentFile(contentFiles, prefix, slug, fallback, renderTaskService, contentRenderer, locale);
|
|
510
|
-
}
|
|
511
|
-
return of({
|
|
512
|
-
filename: '',
|
|
513
|
-
slug: '',
|
|
514
|
-
attributes: {},
|
|
515
|
-
content: fallback,
|
|
516
|
-
toc: [],
|
|
517
|
-
});
|
|
518
|
-
}), tap(() => renderTaskService.clearRenderTask(task)));
|
|
519
|
-
}
|
|
520
|
-
else {
|
|
521
|
-
return getContentFile(contentFiles, '', param.customFilename, fallback, renderTaskService, contentRenderer, locale).pipe(tap(() => renderTaskService.clearRenderTask(task)));
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
431
|
function injectContentFiles(filterFn) {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
if (filterFn) {
|
|
536
|
-
results = results.filter(filterFn);
|
|
537
|
-
}
|
|
538
|
-
return results;
|
|
432
|
+
const renderTaskService = inject(RenderTaskService);
|
|
433
|
+
const task = renderTaskService.addRenderTask();
|
|
434
|
+
const allContentFiles = inject(CONTENT_FILES_LIST_TOKEN);
|
|
435
|
+
const locale = inject(CONTENT_LOCALE, { optional: true });
|
|
436
|
+
renderTaskService.clearRenderTask(task);
|
|
437
|
+
let results = allContentFiles;
|
|
438
|
+
if (locale) results = filterByLocale(results, locale);
|
|
439
|
+
if (filterFn) results = results.filter(filterFn);
|
|
440
|
+
return results;
|
|
539
441
|
}
|
|
540
442
|
function injectContentFilesMap() {
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
443
|
+
return inject(CONTENT_FILES_TOKEN);
|
|
444
|
+
}
|
|
445
|
+
var MarkedContentHighlighter = class MarkedContentHighlighter {
|
|
446
|
+
static {
|
|
447
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
448
|
+
minVersion: "12.0.0",
|
|
449
|
+
version: "22.2.0",
|
|
450
|
+
ngImport: i0,
|
|
451
|
+
type: MarkedContentHighlighter,
|
|
452
|
+
deps: [],
|
|
453
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
static {
|
|
457
|
+
this.ɵprov = i0.ɵɵngDeclareInjectable({
|
|
458
|
+
minVersion: "12.0.0",
|
|
459
|
+
version: "22.2.0",
|
|
460
|
+
ngImport: i0,
|
|
461
|
+
type: MarkedContentHighlighter
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
466
|
+
minVersion: "12.0.0",
|
|
467
|
+
version: "22.2.0",
|
|
468
|
+
ngImport: i0,
|
|
469
|
+
type: MarkedContentHighlighter,
|
|
470
|
+
decorators: [{ type: Injectable }]
|
|
471
|
+
});
|
|
551
472
|
function withHighlighter(provider) {
|
|
552
|
-
|
|
473
|
+
return {
|
|
474
|
+
provide: MarkedContentHighlighter,
|
|
475
|
+
...provider
|
|
476
|
+
};
|
|
553
477
|
}
|
|
554
|
-
|
|
555
478
|
function escapeHtml(value) {
|
|
556
|
-
|
|
557
|
-
.replaceAll('&', '&')
|
|
558
|
-
.replaceAll('<', '<')
|
|
559
|
-
.replaceAll('>', '>')
|
|
560
|
-
.replaceAll('"', '"')
|
|
561
|
-
.replaceAll("'", ''');
|
|
479
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
562
480
|
}
|
|
563
|
-
/**
|
|
564
|
-
* Reduces a fenced code-block info string to a safe language identifier so it
|
|
565
|
-
* cannot break out of the `class="language-..."` attribute it is interpolated
|
|
566
|
-
* into.
|
|
567
|
-
*/
|
|
568
481
|
function sanitizeLanguage(lang) {
|
|
569
|
-
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
482
|
+
return (lang ?? "").match(/^[a-zA-Z0-9-+#.]*/)?.[0] ?? "";
|
|
483
|
+
}
|
|
484
|
+
var MarkedSetupService = class MarkedSetupService {
|
|
485
|
+
constructor() {
|
|
486
|
+
this.highlighter = inject(MarkedContentHighlighter, { optional: true });
|
|
487
|
+
const renderer = new marked.Renderer();
|
|
488
|
+
renderer.code = ({ text, lang }) => {
|
|
489
|
+
const language = sanitizeLanguage(lang);
|
|
490
|
+
if (language === "mermaid") return "<pre class=\"mermaid\">" + escapeHtml(text) + "</pre>";
|
|
491
|
+
if (!language) return "<pre><code>" + escapeHtml(text) + "</code></pre>";
|
|
492
|
+
if (this.highlighter?.augmentCodeBlock) return this.highlighter?.augmentCodeBlock(text, language);
|
|
493
|
+
return `<pre class="language-${language}"><code class="language-${language}">${escapeHtml(text)}</code></pre>`;
|
|
494
|
+
};
|
|
495
|
+
const extensions = [gfmHeadingId(), mangle()];
|
|
496
|
+
if (this.highlighter) extensions.push(this.highlighter.getHighlightExtension());
|
|
497
|
+
marked.use(...extensions, {
|
|
498
|
+
renderer,
|
|
499
|
+
pedantic: false,
|
|
500
|
+
gfm: true,
|
|
501
|
+
breaks: false
|
|
502
|
+
});
|
|
503
|
+
this.marked = marked;
|
|
504
|
+
}
|
|
505
|
+
getMarkedInstance() {
|
|
506
|
+
return this.marked;
|
|
507
|
+
}
|
|
508
|
+
static {
|
|
509
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
510
|
+
minVersion: "12.0.0",
|
|
511
|
+
version: "22.2.0",
|
|
512
|
+
ngImport: i0,
|
|
513
|
+
type: MarkedSetupService,
|
|
514
|
+
deps: [],
|
|
515
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
static {
|
|
519
|
+
this.ɵprov = i0.ɵɵngDeclareInjectable({
|
|
520
|
+
minVersion: "12.0.0",
|
|
521
|
+
version: "22.2.0",
|
|
522
|
+
ngImport: i0,
|
|
523
|
+
type: MarkedSetupService
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
528
|
+
minVersion: "12.0.0",
|
|
529
|
+
version: "22.2.0",
|
|
530
|
+
ngImport: i0,
|
|
531
|
+
type: MarkedSetupService,
|
|
532
|
+
decorators: [{ type: Injectable }],
|
|
533
|
+
ctorParameters: () => []
|
|
534
|
+
});
|
|
535
|
+
var MarkdownContentRendererService = class MarkdownContentRendererService {
|
|
536
|
+
#marked = inject(MarkedSetupService, { self: true });
|
|
537
|
+
async render(content) {
|
|
538
|
+
return {
|
|
539
|
+
content: await this.#marked.getMarkedInstance().parse(content),
|
|
540
|
+
toc: getHeadingList()
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
getContentHeadings(content) {
|
|
544
|
+
return [...content.matchAll(/^(#{1,6})\s+(.+?)\s*$/gm)].map((match) => ({
|
|
545
|
+
id: match[2].trim().toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-"),
|
|
546
|
+
level: match[1].length,
|
|
547
|
+
text: match[2].trim()
|
|
548
|
+
}));
|
|
549
|
+
}
|
|
550
|
+
enhance() {}
|
|
551
|
+
static {
|
|
552
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
553
|
+
minVersion: "12.0.0",
|
|
554
|
+
version: "22.2.0",
|
|
555
|
+
ngImport: i0,
|
|
556
|
+
type: MarkdownContentRendererService,
|
|
557
|
+
deps: [],
|
|
558
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
static {
|
|
562
|
+
this.ɵprov = i0.ɵɵngDeclareInjectable({
|
|
563
|
+
minVersion: "12.0.0",
|
|
564
|
+
version: "22.2.0",
|
|
565
|
+
ngImport: i0,
|
|
566
|
+
type: MarkdownContentRendererService
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
571
|
+
minVersion: "12.0.0",
|
|
572
|
+
version: "22.2.0",
|
|
573
|
+
ngImport: i0,
|
|
574
|
+
type: MarkdownContentRendererService,
|
|
575
|
+
decorators: [{ type: Injectable }]
|
|
576
|
+
});
|
|
577
|
+
const CONTENT_FILE_LOADER = new InjectionToken("@analogjs/content/resource File Loader");
|
|
651
578
|
function injectContentFileLoader() {
|
|
652
|
-
|
|
579
|
+
return inject(CONTENT_FILE_LOADER);
|
|
653
580
|
}
|
|
654
581
|
function withContentFileLoader() {
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
const CONTENT_LIST_LOADER = new InjectionToken('@analogjs/content/resource List Loader');
|
|
582
|
+
return {
|
|
583
|
+
provide: CONTENT_FILE_LOADER,
|
|
584
|
+
useFactory() {
|
|
585
|
+
return async () => injectContentFilesMap();
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
const CONTENT_LIST_LOADER = new InjectionToken("@analogjs/content/resource List Loader");
|
|
664
590
|
function injectContentListLoader() {
|
|
665
|
-
|
|
591
|
+
return inject(CONTENT_LIST_LOADER);
|
|
666
592
|
}
|
|
667
593
|
function withContentListLoader() {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
594
|
+
return {
|
|
595
|
+
provide: CONTENT_LIST_LOADER,
|
|
596
|
+
useFactory() {
|
|
597
|
+
return async () => injectContentFiles();
|
|
598
|
+
}
|
|
599
|
+
};
|
|
674
600
|
}
|
|
675
|
-
|
|
676
601
|
const CONTENT_RENDERER_PROVIDERS = [
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
602
|
+
{
|
|
603
|
+
provide: ContentRenderer,
|
|
604
|
+
useClass: NoopContentRenderer
|
|
605
|
+
},
|
|
606
|
+
withContentFileLoader(),
|
|
607
|
+
withContentListLoader()
|
|
683
608
|
];
|
|
684
609
|
function withMarkdownRenderer(options) {
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
{
|
|
690
|
-
provide: MERMAID_IMPORT_TOKEN,
|
|
691
|
-
useFactory: options.loadMermaid,
|
|
692
|
-
},
|
|
693
|
-
]
|
|
694
|
-
: [],
|
|
695
|
-
];
|
|
610
|
+
return [CONTENT_RENDERER_PROVIDERS, options?.loadMermaid ? [{
|
|
611
|
+
provide: MERMAID_IMPORT_TOKEN,
|
|
612
|
+
useFactory: options.loadMermaid
|
|
613
|
+
}] : []];
|
|
696
614
|
}
|
|
697
615
|
function provideContent(...features) {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
}
|
|
703
|
-
const MERMAID_IMPORT_TOKEN = new InjectionToken(
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
616
|
+
return [{
|
|
617
|
+
provide: RenderTaskService,
|
|
618
|
+
useClass: RenderTaskService
|
|
619
|
+
}, ...features];
|
|
620
|
+
}
|
|
621
|
+
const MERMAID_IMPORT_TOKEN = new InjectionToken("mermaid_import");
|
|
622
|
+
var AnalogMarkdownRouteComponent = class AnalogMarkdownRouteComponent {
|
|
623
|
+
constructor() {
|
|
624
|
+
this.sanitizer = inject(DomSanitizer);
|
|
625
|
+
this.route = inject(ActivatedRoute);
|
|
626
|
+
this.contentRenderer = inject(ContentRenderer);
|
|
627
|
+
this.content = this.sanitizer.bypassSecurityTrustHtml(this.route.snapshot.data["renderedAnalogContent"]);
|
|
628
|
+
this.classes = "analog-markdown-route";
|
|
629
|
+
}
|
|
630
|
+
ngAfterViewChecked() {
|
|
631
|
+
this.contentRenderer.enhance();
|
|
632
|
+
}
|
|
633
|
+
static {
|
|
634
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
635
|
+
minVersion: "12.0.0",
|
|
636
|
+
version: "22.2.0",
|
|
637
|
+
ngImport: i0,
|
|
638
|
+
type: AnalogMarkdownRouteComponent,
|
|
639
|
+
deps: [],
|
|
640
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
static {
|
|
644
|
+
this.ɵcmp = i0.ɵɵngDeclareComponent({
|
|
645
|
+
minVersion: "14.0.0",
|
|
646
|
+
version: "22.2.0",
|
|
647
|
+
type: AnalogMarkdownRouteComponent,
|
|
648
|
+
isStandalone: true,
|
|
649
|
+
selector: "analog-markdown-route",
|
|
650
|
+
inputs: { classes: "classes" },
|
|
651
|
+
hostDirectives: [{ directive: AnchorNavigationDirective }],
|
|
652
|
+
ngImport: i0,
|
|
653
|
+
template: `<div [innerHTML]="content" [class]="classes"></div>`,
|
|
654
|
+
isInline: true,
|
|
655
|
+
encapsulation: i0.ViewEncapsulation.None,
|
|
656
|
+
preserveWhitespaces: true
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
661
|
+
minVersion: "12.0.0",
|
|
662
|
+
version: "22.2.0",
|
|
663
|
+
ngImport: i0,
|
|
664
|
+
type: AnalogMarkdownRouteComponent,
|
|
665
|
+
decorators: [{
|
|
666
|
+
type: Component,
|
|
667
|
+
args: [{
|
|
668
|
+
selector: "analog-markdown-route",
|
|
669
|
+
standalone: true,
|
|
670
|
+
imports: [AsyncPipe],
|
|
671
|
+
hostDirectives: [AnchorNavigationDirective],
|
|
672
|
+
preserveWhitespaces: true,
|
|
673
|
+
encapsulation: ViewEncapsulation.None,
|
|
674
|
+
template: `<div [innerHTML]="content" [class]="classes"></div>`
|
|
675
|
+
}]
|
|
676
|
+
}],
|
|
677
|
+
propDecorators: { classes: [{ type: Input }] }
|
|
678
|
+
});
|
|
679
|
+
var AnalogMarkdownComponent = class AnalogMarkdownComponent {
|
|
680
|
+
constructor() {
|
|
681
|
+
this.sanitizer = inject(DomSanitizer);
|
|
682
|
+
this.route = inject(ActivatedRoute);
|
|
683
|
+
this.zone = inject(NgZone);
|
|
684
|
+
this.platformId = inject(PLATFORM_ID);
|
|
685
|
+
this.mermaidImport = inject(MERMAID_IMPORT_TOKEN, { optional: true });
|
|
686
|
+
this.contentSource = toSignal(this.getContentSource());
|
|
687
|
+
this.htmlContent = computed(() => {
|
|
688
|
+
const inputContent = this.content();
|
|
689
|
+
if (inputContent) return this.sanitizer.bypassSecurityTrustHtml(inputContent);
|
|
690
|
+
return this.contentSource();
|
|
691
|
+
}, ...ngDevMode ? [{ debugName: "htmlContent" }] : /* istanbul ignore next */ []);
|
|
692
|
+
this.content = input(...ngDevMode ? [void 0, { debugName: "content" }] : /* istanbul ignore next */ []);
|
|
693
|
+
this.classes = input("analog-markdown", ...ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []);
|
|
694
|
+
this.contentRenderer = inject(ContentRenderer);
|
|
695
|
+
if (isPlatformBrowser(this.platformId) && this.mermaidImport) this.loadMermaid(this.mermaidImport);
|
|
696
|
+
}
|
|
697
|
+
getContentSource() {
|
|
698
|
+
return this.route.data.pipe(map((data) => data["_analogContent"] ?? ""), switchMap((contentString) => this.renderContent(contentString)), map((content) => this.sanitizer.bypassSecurityTrustHtml(content)), catchError((e) => of(`There was an error ${e}`)));
|
|
699
|
+
}
|
|
700
|
+
async renderContent(content) {
|
|
701
|
+
return (await this.contentRenderer.render(content)).content;
|
|
702
|
+
}
|
|
703
|
+
ngAfterViewChecked() {
|
|
704
|
+
this.contentRenderer.enhance();
|
|
705
|
+
this.zone.runOutsideAngular(() => this.mermaid?.default.run());
|
|
706
|
+
}
|
|
707
|
+
loadMermaid(mermaidImport) {
|
|
708
|
+
this.zone.runOutsideAngular(() => from(mermaidImport).pipe(takeUntilDestroyed()).subscribe((mermaid) => {
|
|
709
|
+
this.mermaid = mermaid;
|
|
710
|
+
this.mermaid.default.initialize({ startOnLoad: false });
|
|
711
|
+
this.mermaid?.default.run();
|
|
712
|
+
}));
|
|
713
|
+
}
|
|
714
|
+
static {
|
|
715
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
716
|
+
minVersion: "12.0.0",
|
|
717
|
+
version: "22.2.0",
|
|
718
|
+
ngImport: i0,
|
|
719
|
+
type: AnalogMarkdownComponent,
|
|
720
|
+
deps: [],
|
|
721
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
static {
|
|
725
|
+
this.ɵcmp = i0.ɵɵngDeclareComponent({
|
|
726
|
+
minVersion: "17.1.0",
|
|
727
|
+
version: "22.2.0",
|
|
728
|
+
type: AnalogMarkdownComponent,
|
|
729
|
+
isStandalone: true,
|
|
730
|
+
selector: "analog-markdown",
|
|
731
|
+
inputs: {
|
|
732
|
+
content: {
|
|
733
|
+
classPropertyName: "content",
|
|
734
|
+
publicName: "content",
|
|
735
|
+
isSignal: true,
|
|
736
|
+
isRequired: false,
|
|
737
|
+
transformFunction: null
|
|
738
|
+
},
|
|
739
|
+
classes: {
|
|
740
|
+
classPropertyName: "classes",
|
|
741
|
+
publicName: "classes",
|
|
742
|
+
isSignal: true,
|
|
743
|
+
isRequired: false,
|
|
744
|
+
transformFunction: null
|
|
745
|
+
}
|
|
746
|
+
},
|
|
747
|
+
hostDirectives: [{ directive: AnchorNavigationDirective }],
|
|
748
|
+
ngImport: i0,
|
|
749
|
+
template: ` <div [innerHTML]="htmlContent()" [class]="classes()"></div> `,
|
|
750
|
+
isInline: true,
|
|
751
|
+
encapsulation: i0.ViewEncapsulation.None,
|
|
752
|
+
preserveWhitespaces: true
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
757
|
+
minVersion: "12.0.0",
|
|
758
|
+
version: "22.2.0",
|
|
759
|
+
ngImport: i0,
|
|
760
|
+
type: AnalogMarkdownComponent,
|
|
761
|
+
decorators: [{
|
|
762
|
+
type: Component,
|
|
763
|
+
args: [{
|
|
764
|
+
selector: "analog-markdown",
|
|
765
|
+
standalone: true,
|
|
766
|
+
hostDirectives: [AnchorNavigationDirective],
|
|
767
|
+
preserveWhitespaces: true,
|
|
768
|
+
encapsulation: ViewEncapsulation.None,
|
|
769
|
+
template: ` <div [innerHTML]="htmlContent()" [class]="classes()"></div> `
|
|
770
|
+
}]
|
|
771
|
+
}],
|
|
772
|
+
ctorParameters: () => [],
|
|
773
|
+
propDecorators: {
|
|
774
|
+
content: [{
|
|
775
|
+
type: i0.Input,
|
|
776
|
+
args: [{
|
|
777
|
+
isSignal: true,
|
|
778
|
+
alias: "content",
|
|
779
|
+
required: false
|
|
780
|
+
}]
|
|
781
|
+
}],
|
|
782
|
+
classes: [{
|
|
783
|
+
type: i0.Input,
|
|
784
|
+
args: [{
|
|
785
|
+
isSignal: true,
|
|
786
|
+
alias: "classes",
|
|
787
|
+
required: false
|
|
788
|
+
}]
|
|
789
|
+
}]
|
|
790
|
+
}
|
|
791
|
+
});
|
|
806
792
|
export { AnchorNavigationDirective, CONTENT_FILE_LOADER, CONTENT_LIST_LOADER, CONTENT_LOCALE, ContentRenderer, MERMAID_IMPORT_TOKEN, AnalogMarkdownComponent as MarkdownComponent, MarkdownContentRendererService, AnalogMarkdownRouteComponent as MarkdownRouteComponent, MarkedContentHighlighter, MarkedSetupService, NoopContentRenderer, filterByLocale, injectContent, injectContentFileLoader, injectContentFiles, injectContentFilesMap, injectContentListLoader, injectContentLocale, parseRawContentFile, provideContent, withContentFileLoader, withContentListLoader, withHighlighter, withLocale, withLocaleCandidates, withMarkdownRenderer };
|
|
807
|
-
|
|
793
|
+
|
|
794
|
+
//# sourceMappingURL=analogjs-content.mjs.map
|