@analogjs/content 3.0.0-alpha.2 → 3.0.0-alpha.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.
- package/LICENSE +21 -0
- package/fesm2022/analogjs-content-md4x.mjs +290 -0
- package/fesm2022/analogjs-content-md4x.mjs.map +1 -0
- package/fesm2022/analogjs-content-mdc.mjs +170 -0
- package/fesm2022/analogjs-content-mdc.mjs.map +1 -0
- package/fesm2022/analogjs-content-og.mjs +34 -46
- package/fesm2022/analogjs-content-og.mjs.map +1 -1
- package/fesm2022/analogjs-content-prism-highlighter.mjs +81 -68
- package/fesm2022/analogjs-content-prism-highlighter.mjs.map +1 -1
- package/fesm2022/analogjs-content-resources.mjs +113 -124
- package/fesm2022/analogjs-content-resources.mjs.map +1 -1
- package/fesm2022/analogjs-content-shiki-highlighter.mjs +9 -14
- package/fesm2022/analogjs-content-shiki-highlighter.mjs.map +1 -1
- package/fesm2022/analogjs-content.mjs +658 -636
- package/fesm2022/analogjs-content.mjs.map +1 -1
- package/fesm2022/content-list-loader.mjs +284 -0
- package/fesm2022/content-list-loader.mjs.map +1 -0
- package/md4x/package.json +4 -0
- package/mdc/package.json +4 -0
- package/og/package.json +2 -2
- package/package.json +56 -30
- package/plugin/package.json +2 -22
- package/plugin/src/index.d.ts +4 -2
- package/plugin/src/index.js +6 -3
- package/plugin/src/index.js.map +1 -1
- package/plugin/src/migrations/update-markdown-version/compat.d.ts +6 -2
- package/plugin/src/migrations/update-markdown-version/compat.js +8 -5
- package/plugin/src/migrations/update-markdown-version/compat.js.map +1 -1
- package/plugin/src/migrations/update-markdown-version/update-markdown-version.d.ts +6 -2
- package/plugin/src/migrations/update-markdown-version/update-markdown-version.js +18 -20
- package/plugin/src/migrations/update-markdown-version/update-markdown-version.js.map +1 -1
- package/prism-highlighter/package.json +2 -2
- package/resources/package.json +2 -2
- package/shiki-highlighter/package.json +2 -2
- package/src/lib/devtools/content-devtools-client.ts +215 -0
- package/src/lib/devtools/content-devtools.styles.css +194 -0
- package/types/md4x/src/index.d.ts +5 -0
- package/types/md4x/src/lib/md4x-content-renderer.service.d.ts +33 -0
- package/types/md4x/src/lib/md4x-wasm-content-renderer.service.d.ts +16 -0
- package/types/md4x/src/lib/provide-md4x.d.ts +26 -0
- package/types/md4x/src/lib/streaming-markdown-renderer.d.ts +21 -0
- package/types/mdc/src/index.d.ts +2 -0
- package/types/mdc/src/lib/mdc-component-registry.d.ts +25 -0
- package/types/mdc/src/lib/mdc-renderer.directive.d.ts +33 -0
- package/types/og/src/index.d.ts +2 -0
- package/types/og/src/lib/og.d.ts +5 -0
- package/types/og/src/lib/options.d.ts +11 -0
- package/types/prism-highlighter/src/index.d.ts +8 -0
- package/types/prism-highlighter/src/lib/prism/angular.d.ts +1 -0
- package/types/prism-highlighter/src/lib/prism-highlighter.d.ts +8 -0
- package/types/resources/src/content-file-resource.d.ts +37 -0
- package/types/resources/src/content-files-resource.d.ts +3 -0
- package/types/resources/src/index.d.ts +2 -0
- package/types/shiki-highlighter/src/index.d.ts +7 -0
- package/types/src/index.d.ts +18 -0
- package/types/src/lib/anchor-navigation.directive.d.ts +9 -0
- package/types/src/lib/content-file-loader.d.ts +6 -0
- package/types/src/lib/content-file.d.ts +8 -0
- package/types/src/lib/content-files-list-token.d.ts +3 -0
- package/types/src/lib/content-files-token.d.ts +3 -0
- package/types/src/lib/content-list-loader.d.ts +7 -0
- package/types/src/lib/content-renderer.d.ts +33 -0
- package/types/src/lib/content.d.ts +14 -0
- package/types/src/lib/devtools/content-devtools-plugin.d.ts +23 -0
- package/types/src/lib/devtools/content-devtools-renderer.d.ts +23 -0
- package/types/src/lib/devtools/index.d.ts +23 -0
- package/types/src/lib/get-content-files.d.ts +14 -0
- package/types/src/lib/inject-content-files.d.ts +4 -0
- package/types/src/lib/markdown-content-renderer.service.d.ts +10 -0
- package/types/src/lib/markdown-route.component.d.ts +15 -0
- package/types/src/lib/markdown.component.d.ts +26 -0
- package/types/src/lib/marked-content-highlighter.d.ts +17 -0
- package/types/src/lib/marked-setup.service.d.ts +10 -0
- package/types/src/lib/parse-raw-content-file.d.ts +18 -0
- package/types/src/lib/provide-content.d.ts +7 -0
- package/types/src/lib/render-task.service.d.ts +8 -0
- package/types/src/lib/utils/zone-wait-for.d.ts +2 -0
- package/og/README.md +0 -3
- package/plugin/README.md +0 -11
- package/plugin/src/migrations/update-markdown-renderer-feature/compat.d.ts +0 -2
- package/plugin/src/migrations/update-markdown-renderer-feature/compat.js +0 -6
- package/plugin/src/migrations/update-markdown-renderer-feature/compat.js.map +0 -1
- package/plugin/src/migrations/update-markdown-renderer-feature/update-markdown-renderer-feature.d.ts +0 -2
- package/plugin/src/migrations/update-markdown-renderer-feature/update-markdown-renderer-feature.js +0 -48
- package/plugin/src/migrations/update-markdown-renderer-feature/update-markdown-renderer-feature.js.map +0 -1
- package/prism-highlighter/README.md +0 -3
- package/resources/README.md +0 -3
- package/shiki-highlighter/README.md +0 -3
- package/types/analogjs-content-og.d.ts +0 -19
- package/types/analogjs-content-prism-highlighter.d.ts +0 -15
- package/types/analogjs-content-resources.d.ts +0 -20
- package/types/analogjs-content-shiki-highlighter.d.ts +0 -11
- package/types/analogjs-content.d.ts +0 -168
|
@@ -1,660 +1,682 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
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
|
-
i0.ɵɵ
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
1
|
+
import { a as injectContentFileLoader, c as injectContentFilesMap, d as ContentRenderer, f as NoopContentRenderer, i as CONTENT_FILE_LOADER, l as RenderTaskService, n as injectContentListLoader, o as withContentFileLoader, r as withContentListLoader, s as injectContentFiles, t as CONTENT_LIST_LOADER, u as CONTENT_FILES_TOKEN } from "./content-list-loader.mjs";
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
import { Component, Directive, HostListener, Injectable, InjectionToken, Input, NgZone, PLATFORM_ID, ViewEncapsulation, computed, inject, input } from "@angular/core";
|
|
4
|
+
import { DOCUMENT, Location, isPlatformBrowser } from "@angular/common";
|
|
5
|
+
import { ActivatedRoute, Router } from "@angular/router";
|
|
6
|
+
import { Observable, from, of } from "rxjs";
|
|
7
|
+
import { catchError, map, switchMap, tap } from "rxjs/operators";
|
|
8
|
+
import fm from "front-matter";
|
|
9
|
+
import { getHeadingList, gfmHeadingId } from "marked-gfm-heading-id";
|
|
10
|
+
import { marked } from "marked";
|
|
11
|
+
import { mangle } from "marked-mangle";
|
|
12
|
+
import { DomSanitizer } from "@angular/platform-browser";
|
|
13
|
+
import { takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop";
|
|
14
|
+
import { transformWithOxc } from "vite";
|
|
15
|
+
//#region \0rolldown/runtime.js
|
|
16
|
+
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region packages/content/src/lib/anchor-navigation.directive.ts
|
|
19
|
+
var AnchorNavigationDirective = class AnchorNavigationDirective {
|
|
20
|
+
constructor() {
|
|
21
|
+
this.document = inject(DOCUMENT);
|
|
22
|
+
this.location = inject(Location);
|
|
23
|
+
this.router = inject(Router);
|
|
24
|
+
}
|
|
25
|
+
handleNavigation(element) {
|
|
26
|
+
if (element instanceof HTMLAnchorElement && isInternalUrl(element, this.document) && hasTargetSelf(element) && !hasDownloadAttribute(element)) {
|
|
27
|
+
const { pathname, search, hash } = element;
|
|
28
|
+
const url = this.location.normalize(`${pathname}${search}${hash}`);
|
|
29
|
+
this.router.navigateByUrl(url);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
static {
|
|
35
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
36
|
+
minVersion: "12.0.0",
|
|
37
|
+
version: "21.2.5",
|
|
38
|
+
ngImport: i0,
|
|
39
|
+
type: AnchorNavigationDirective,
|
|
40
|
+
deps: [],
|
|
41
|
+
target: i0.ɵɵFactoryTarget.Directive
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
static {
|
|
45
|
+
this.ɵdir = i0.ɵɵngDeclareDirective({
|
|
46
|
+
minVersion: "14.0.0",
|
|
47
|
+
version: "21.2.5",
|
|
48
|
+
type: AnchorNavigationDirective,
|
|
49
|
+
isStandalone: true,
|
|
50
|
+
selector: "[analogAnchorNavigation]",
|
|
51
|
+
host: { listeners: { "click": "handleNavigation($event.target)" } },
|
|
52
|
+
ngImport: i0
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
57
|
+
minVersion: "12.0.0",
|
|
58
|
+
version: "21.2.5",
|
|
59
|
+
ngImport: i0,
|
|
60
|
+
type: AnchorNavigationDirective,
|
|
61
|
+
decorators: [{
|
|
62
|
+
type: Directive,
|
|
63
|
+
args: [{
|
|
64
|
+
selector: "[analogAnchorNavigation]",
|
|
65
|
+
standalone: true
|
|
66
|
+
}]
|
|
67
|
+
}],
|
|
68
|
+
propDecorators: { handleNavigation: [{
|
|
69
|
+
type: HostListener,
|
|
70
|
+
args: ["click", ["$event.target"]]
|
|
71
|
+
}] }
|
|
72
|
+
});
|
|
45
73
|
function hasDownloadAttribute(anchorElement) {
|
|
46
|
-
|
|
74
|
+
return anchorElement.getAttribute("download") !== null;
|
|
47
75
|
}
|
|
48
76
|
function hasTargetSelf(anchorElement) {
|
|
49
|
-
|
|
77
|
+
return !anchorElement.target || anchorElement.target === "_self";
|
|
50
78
|
}
|
|
51
79
|
function isInternalUrl(anchorElement, document) {
|
|
52
|
-
|
|
53
|
-
anchorElement.protocol === document.location.protocol);
|
|
80
|
+
return anchorElement.host === document.location.host && anchorElement.protocol === document.location.protocol;
|
|
54
81
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
class
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: ContentRenderer, decorators: [{
|
|
71
|
-
type: Injectable
|
|
72
|
-
}] });
|
|
73
|
-
class NoopContentRenderer {
|
|
74
|
-
constructor() {
|
|
75
|
-
this.transferState = inject(TransferState);
|
|
76
|
-
this.contentId = 0;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Generates a hash from the content string
|
|
80
|
-
* to be used with the transfer state
|
|
81
|
-
*/
|
|
82
|
-
generateHash(str) {
|
|
83
|
-
let hash = 0;
|
|
84
|
-
for (let i = 0, len = str.length; i < len; i++) {
|
|
85
|
-
let chr = str.charCodeAt(i);
|
|
86
|
-
hash = (hash << 5) - hash + chr;
|
|
87
|
-
hash |= 0; // Convert to 32bit integer
|
|
88
|
-
}
|
|
89
|
-
return hash;
|
|
90
|
-
}
|
|
91
|
-
async render(content) {
|
|
92
|
-
this.contentId = this.generateHash(content);
|
|
93
|
-
const toc = this.getContentHeadings(content);
|
|
94
|
-
const key = makeStateKey(`content-headings-${this.contentId}`);
|
|
95
|
-
if (import.meta.env.SSR === true) {
|
|
96
|
-
this.transferState.set(key, toc);
|
|
97
|
-
return { content, toc };
|
|
98
|
-
}
|
|
99
|
-
return {
|
|
100
|
-
content,
|
|
101
|
-
toc: this.transferState.get(key, toc),
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
enhance() { }
|
|
105
|
-
getContentHeadings(content) {
|
|
106
|
-
return this.extractHeadings(content);
|
|
107
|
-
}
|
|
108
|
-
extractHeadings(content) {
|
|
109
|
-
const markdownHeadings = this.extractHeadingsFromMarkdown(content);
|
|
110
|
-
if (markdownHeadings.length > 0) {
|
|
111
|
-
return markdownHeadings;
|
|
112
|
-
}
|
|
113
|
-
const htmlHeadings = this.extractHeadingsFromHtml(content);
|
|
114
|
-
return htmlHeadings;
|
|
115
|
-
}
|
|
116
|
-
extractHeadingsFromMarkdown(content) {
|
|
117
|
-
const lines = content.split('\n');
|
|
118
|
-
const toc = [];
|
|
119
|
-
const slugCounts = new Map();
|
|
120
|
-
for (const line of lines) {
|
|
121
|
-
const match = /^(#{1,6})\s+(.+?)\s*$/.exec(line);
|
|
122
|
-
if (!match) {
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
const level = match[1].length;
|
|
126
|
-
const text = match[2].trim();
|
|
127
|
-
if (!text) {
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
const baseSlug = text
|
|
131
|
-
.toLowerCase()
|
|
132
|
-
.replace(/[^\w\s-]/g, '')
|
|
133
|
-
.trim()
|
|
134
|
-
.replace(/\s+/g, '-');
|
|
135
|
-
const count = slugCounts.get(baseSlug) ?? 0;
|
|
136
|
-
slugCounts.set(baseSlug, count + 1);
|
|
137
|
-
const id = count === 0 ? baseSlug : `${baseSlug}-${count}`;
|
|
138
|
-
toc.push({ id, level, text });
|
|
139
|
-
}
|
|
140
|
-
return toc;
|
|
141
|
-
}
|
|
142
|
-
extractHeadingsFromHtml(content) {
|
|
143
|
-
const toc = [];
|
|
144
|
-
const slugCounts = new Map();
|
|
145
|
-
const headingRegex = /<h([1-6])([^>]*)>([\s\S]*?)<\/h\1>/gi;
|
|
146
|
-
for (const match of content.matchAll(headingRegex)) {
|
|
147
|
-
const level = Number(match[1]);
|
|
148
|
-
const attrs = match[2] ?? '';
|
|
149
|
-
const rawInner = match[3] ?? '';
|
|
150
|
-
const text = rawInner.replace(/<[^>]+>/g, '').trim();
|
|
151
|
-
if (!text) {
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
const idMatch = /\sid=(['"])(.*?)\1/i.exec(attrs) ?? /\sid=([^\s>]+)/i.exec(attrs);
|
|
155
|
-
let id = idMatch?.[2] ?? idMatch?.[1] ?? '';
|
|
156
|
-
if (!id) {
|
|
157
|
-
id = this.makeSlug(text, slugCounts);
|
|
158
|
-
}
|
|
159
|
-
toc.push({ id, level, text });
|
|
160
|
-
}
|
|
161
|
-
return toc;
|
|
162
|
-
}
|
|
163
|
-
makeSlug(text, slugCounts) {
|
|
164
|
-
const baseSlug = text
|
|
165
|
-
.toLowerCase()
|
|
166
|
-
.replace(/[^\w\s-]/g, '')
|
|
167
|
-
.trim()
|
|
168
|
-
.replace(/\s+/g, '-');
|
|
169
|
-
const count = slugCounts.get(baseSlug) ?? 0;
|
|
170
|
-
slugCounts.set(baseSlug, count + 1);
|
|
171
|
-
return count === 0 ? baseSlug : `${baseSlug}-${count}`;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Returns the list of content files by filename with ?analog-content-list=true.
|
|
177
|
-
* We use the query param to transform the return into an array of
|
|
178
|
-
* just front matter attributes.
|
|
179
|
-
*
|
|
180
|
-
* @returns
|
|
181
|
-
*/
|
|
182
|
-
const getContentFilesList = () => {
|
|
183
|
-
let ANALOG_CONTENT_FILE_LIST = {};
|
|
184
|
-
return ANALOG_CONTENT_FILE_LIST;
|
|
185
|
-
};
|
|
186
|
-
/**
|
|
187
|
-
* Returns the lazy loaded content files for lookups.
|
|
188
|
-
*
|
|
189
|
-
* @returns
|
|
190
|
-
*/
|
|
191
|
-
const getContentFiles = () => {
|
|
192
|
-
let ANALOG_CONTENT_ROUTE_FILES = {};
|
|
193
|
-
return ANALOG_CONTENT_ROUTE_FILES;
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region packages/content/src/lib/parse-raw-content-file.ts
|
|
84
|
+
var FrontmatterValidationError = class extends Error {
|
|
85
|
+
constructor(issues, filename) {
|
|
86
|
+
const issueMessages = issues.map((i) => {
|
|
87
|
+
const path = i.path ? ` at "${i.path.map((p) => typeof p === "object" ? p.key : p).join(".")}"` : "";
|
|
88
|
+
return ` - ${i.message}${path}`;
|
|
89
|
+
}).join("\n");
|
|
90
|
+
const prefix = filename ? `"${filename}" f` : "F";
|
|
91
|
+
super(`${prefix}rontmatter validation failed:\n${issueMessages}`);
|
|
92
|
+
this.issues = issues;
|
|
93
|
+
this.filename = filename;
|
|
94
|
+
this.name = "FrontmatterValidationError";
|
|
95
|
+
}
|
|
194
96
|
};
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
97
|
+
function parseRawContentFile(rawContentFile, schema, filename) {
|
|
98
|
+
const { body, attributes } = fm(rawContentFile);
|
|
99
|
+
if (schema) {
|
|
100
|
+
const result = schema["~standard"].validate(attributes);
|
|
101
|
+
if (result != null && typeof result.then === "function") throw new Error("parseRawContentFile does not support async schema validation. Use parseRawContentFileAsync() for async schemas.");
|
|
102
|
+
const syncResult = result;
|
|
103
|
+
if (syncResult.issues) throw new FrontmatterValidationError(syncResult.issues, filename);
|
|
104
|
+
return {
|
|
105
|
+
content: body,
|
|
106
|
+
attributes: syncResult.value
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
content: body,
|
|
111
|
+
attributes
|
|
112
|
+
};
|
|
203
113
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
filename,
|
|
213
|
-
attributes,
|
|
214
|
-
slug: slug ? encodeURI(slug) : encodeURI(getSlug(filename)),
|
|
215
|
-
};
|
|
216
|
-
});
|
|
217
|
-
},
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
const CONTENT_FILES_TOKEN = new InjectionToken('@analogjs/content Content Files', {
|
|
221
|
-
providedIn: 'root',
|
|
222
|
-
factory() {
|
|
223
|
-
const contentFiles = getContentFiles();
|
|
224
|
-
const allFiles = { ...contentFiles };
|
|
225
|
-
const contentFilesList = inject(CONTENT_FILES_LIST_TOKEN);
|
|
226
|
-
const lookup = {};
|
|
227
|
-
contentFilesList.forEach((item) => {
|
|
228
|
-
const contentFilename = item.filename.replace(/(.*?)\/content/, '/src/content');
|
|
229
|
-
const fileParts = contentFilename.split('/');
|
|
230
|
-
const filePath = fileParts.slice(0, fileParts.length - 1).join('/');
|
|
231
|
-
const fileNameParts = fileParts[fileParts.length - 1].split('.');
|
|
232
|
-
const ext = fileNameParts[fileNameParts.length - 1];
|
|
233
|
-
let slug = (item.slug ?? '');
|
|
234
|
-
// Default empty slug to 'index'
|
|
235
|
-
if (slug === '') {
|
|
236
|
-
slug = 'index';
|
|
237
|
-
}
|
|
238
|
-
// If slug contains path separators, treat it as root-relative to /src/content
|
|
239
|
-
const newBase = slug.includes('/')
|
|
240
|
-
? `/src/content/${slug}`
|
|
241
|
-
: `${filePath}/${slug}`;
|
|
242
|
-
lookup[contentFilename] = `${newBase}.${ext}`.replace(/\/{2,}/g, '/');
|
|
243
|
-
});
|
|
244
|
-
const objectUsingSlugAttribute = {};
|
|
245
|
-
Object.entries(allFiles).forEach((entry) => {
|
|
246
|
-
const filename = entry[0];
|
|
247
|
-
const value = entry[1];
|
|
248
|
-
const strippedFilename = filename.replace(/^\/(.*?)\/content/, '/src/content');
|
|
249
|
-
const newFilename = lookup[strippedFilename];
|
|
250
|
-
if (newFilename !== undefined) {
|
|
251
|
-
const objectFilename = newFilename.replace(/^\/(.*?)\/content/, '/src/content');
|
|
252
|
-
objectUsingSlugAttribute[objectFilename] =
|
|
253
|
-
value;
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
return objectUsingSlugAttribute;
|
|
257
|
-
},
|
|
258
|
-
});
|
|
259
|
-
const CONTENT_FILES_MAP_TOKEN = new InjectionToken('@analogjs/content Content Files', {
|
|
260
|
-
providedIn: 'root',
|
|
261
|
-
factory() {
|
|
262
|
-
return signal(inject(CONTENT_FILES_TOKEN));
|
|
263
|
-
},
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
function parseRawContentFile(rawContentFile) {
|
|
267
|
-
const { body, attributes } = fm(rawContentFile);
|
|
268
|
-
return { content: body, attributes };
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
async function waitFor(prom) {
|
|
272
|
-
if (isObservable(prom)) {
|
|
273
|
-
prom = firstValueFrom(prom);
|
|
274
|
-
}
|
|
275
|
-
if (typeof Zone === 'undefined') {
|
|
276
|
-
return prom;
|
|
277
|
-
}
|
|
278
|
-
const macroTask = Zone.current.scheduleMacroTask(`AnalogContentResolve-${Math.random()}`, () => { }, {}, () => { });
|
|
279
|
-
return prom.then((p) => {
|
|
280
|
-
macroTask.invoke();
|
|
281
|
-
return p;
|
|
282
|
-
});
|
|
114
|
+
async function parseRawContentFileAsync(rawContentFile, schema, filename) {
|
|
115
|
+
const { body, attributes } = fm(rawContentFile);
|
|
116
|
+
const result = await schema["~standard"].validate(attributes);
|
|
117
|
+
if (result.issues) throw new FrontmatterValidationError(result.issues, filename);
|
|
118
|
+
return {
|
|
119
|
+
content: body,
|
|
120
|
+
attributes: result.value
|
|
121
|
+
};
|
|
283
122
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
#pendingTasks = inject(_PendingTasksInternal);
|
|
287
|
-
addRenderTask() {
|
|
288
|
-
return this.#pendingTasks.add();
|
|
289
|
-
}
|
|
290
|
-
clearRenderTask(clear) {
|
|
291
|
-
if (typeof clear === 'function') {
|
|
292
|
-
clear();
|
|
293
|
-
}
|
|
294
|
-
else if (typeof this.#pendingTasks.remove === 'function') {
|
|
295
|
-
this.#pendingTasks.remove(clear);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: RenderTaskService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
299
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: RenderTaskService }); }
|
|
300
|
-
}
|
|
301
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: RenderTaskService, decorators: [{
|
|
302
|
-
type: Injectable
|
|
303
|
-
}] });
|
|
304
|
-
|
|
305
|
-
/// <reference types="vite/client" />
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region packages/content/src/lib/content.ts
|
|
306
125
|
function getContentFile(contentFiles, prefix, slug, fallback, renderTaskService, contentRenderer) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
}).pipe(switchMap((contentFile) => {
|
|
351
|
-
if (typeof contentFile === 'string') {
|
|
352
|
-
const { content, attributes } = parseRawContentFile(contentFile);
|
|
353
|
-
return from(contentRenderer.render(content)).pipe(map((rendered) => ({
|
|
354
|
-
filename: resolvedBase,
|
|
355
|
-
slug,
|
|
356
|
-
attributes,
|
|
357
|
-
content,
|
|
358
|
-
toc: rendered.toc ?? [],
|
|
359
|
-
})));
|
|
360
|
-
}
|
|
361
|
-
return of({
|
|
362
|
-
filename: resolvedBase,
|
|
363
|
-
slug,
|
|
364
|
-
attributes: contentFile.metadata,
|
|
365
|
-
content: contentFile.default,
|
|
366
|
-
toc: [],
|
|
367
|
-
});
|
|
368
|
-
}));
|
|
126
|
+
const normalizedFiles = {};
|
|
127
|
+
for (const [key, resolver] of Object.entries(contentFiles)) {
|
|
128
|
+
const normalizedKey = key.replace(/^(?:.*)\/content/, "/src/content").replace(/\/{2,}/g, "/");
|
|
129
|
+
normalizedFiles[normalizedKey] = resolver;
|
|
130
|
+
}
|
|
131
|
+
const base = `/src/content/${prefix}${slug}`.replace(/\/{2,}/g, "/");
|
|
132
|
+
const matchKey = [`${base}.md`, `${base}/index.md`].find((k) => k in normalizedFiles);
|
|
133
|
+
const contentFile = matchKey ? normalizedFiles[matchKey] : void 0;
|
|
134
|
+
const resolvedBase = (matchKey || `${base}.md`).replace(/\.md$/, "");
|
|
135
|
+
if (!contentFile) return of({
|
|
136
|
+
filename: resolvedBase,
|
|
137
|
+
attributes: {},
|
|
138
|
+
slug: "",
|
|
139
|
+
content: fallback,
|
|
140
|
+
toc: []
|
|
141
|
+
});
|
|
142
|
+
renderTaskService.addRenderTask();
|
|
143
|
+
return new Observable((observer) => {
|
|
144
|
+
contentFile().then((content) => {
|
|
145
|
+
observer.next(content);
|
|
146
|
+
observer.complete();
|
|
147
|
+
});
|
|
148
|
+
}).pipe(switchMap((contentFile) => {
|
|
149
|
+
if (typeof contentFile === "string") {
|
|
150
|
+
const { content, attributes } = parseRawContentFile(contentFile);
|
|
151
|
+
return from(contentRenderer.render(content)).pipe(map((rendered) => ({
|
|
152
|
+
filename: resolvedBase,
|
|
153
|
+
slug,
|
|
154
|
+
attributes,
|
|
155
|
+
content,
|
|
156
|
+
toc: rendered.toc ?? []
|
|
157
|
+
})));
|
|
158
|
+
}
|
|
159
|
+
return of({
|
|
160
|
+
filename: resolvedBase,
|
|
161
|
+
slug,
|
|
162
|
+
attributes: contentFile.metadata,
|
|
163
|
+
content: contentFile.default,
|
|
164
|
+
toc: []
|
|
165
|
+
});
|
|
166
|
+
}));
|
|
369
167
|
}
|
|
370
168
|
/**
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
function injectContent(param =
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}), tap(() => renderTaskService.clearRenderTask(task)));
|
|
397
|
-
}
|
|
398
|
-
else {
|
|
399
|
-
return getContentFile(contentFiles, '', param.customFilename, fallback, renderTaskService, contentRenderer).pipe(tap(() => renderTaskService.clearRenderTask(task)));
|
|
400
|
-
}
|
|
169
|
+
* Retrieves the static content using the provided param and/or prefix.
|
|
170
|
+
*
|
|
171
|
+
* @param param route parameter (default: 'slug')
|
|
172
|
+
* @param fallback fallback text if content file is not found (default: 'No Content Found')
|
|
173
|
+
*/
|
|
174
|
+
function injectContent(param = "slug", fallback = "No Content Found") {
|
|
175
|
+
const contentFiles = inject(CONTENT_FILES_TOKEN);
|
|
176
|
+
const contentRenderer = inject(ContentRenderer);
|
|
177
|
+
const renderTaskService = inject(RenderTaskService);
|
|
178
|
+
const task = renderTaskService.addRenderTask();
|
|
179
|
+
if (typeof param === "string" || "param" in param) {
|
|
180
|
+
const prefix = typeof param === "string" ? "" : `${param.subdirectory}/`;
|
|
181
|
+
const route = inject(ActivatedRoute);
|
|
182
|
+
const paramKey = typeof param === "string" ? param : param.param;
|
|
183
|
+
return route.paramMap.pipe(map((params) => params.get(paramKey)), switchMap((slug) => {
|
|
184
|
+
if (slug) return getContentFile(contentFiles, prefix, slug, fallback, renderTaskService, contentRenderer);
|
|
185
|
+
return of({
|
|
186
|
+
filename: "",
|
|
187
|
+
slug: "",
|
|
188
|
+
attributes: {},
|
|
189
|
+
content: fallback,
|
|
190
|
+
toc: []
|
|
191
|
+
});
|
|
192
|
+
}), tap(() => renderTaskService.clearRenderTask(task)));
|
|
193
|
+
} else return getContentFile(contentFiles, "", param.customFilename, fallback, renderTaskService, contentRenderer).pipe(tap(() => renderTaskService.clearRenderTask(task)));
|
|
401
194
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
195
|
+
//#endregion
|
|
196
|
+
//#region packages/content/src/lib/marked-content-highlighter.ts
|
|
197
|
+
var MarkedContentHighlighter = class MarkedContentHighlighter {
|
|
198
|
+
static {
|
|
199
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
200
|
+
minVersion: "12.0.0",
|
|
201
|
+
version: "21.2.5",
|
|
202
|
+
ngImport: i0,
|
|
203
|
+
type: MarkedContentHighlighter,
|
|
204
|
+
deps: [],
|
|
205
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
static {
|
|
209
|
+
this.ɵprov = i0.ɵɵngDeclareInjectable({
|
|
210
|
+
minVersion: "12.0.0",
|
|
211
|
+
version: "21.2.5",
|
|
212
|
+
ngImport: i0,
|
|
213
|
+
type: MarkedContentHighlighter
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
218
|
+
minVersion: "12.0.0",
|
|
219
|
+
version: "21.2.5",
|
|
220
|
+
ngImport: i0,
|
|
221
|
+
type: MarkedContentHighlighter,
|
|
222
|
+
decorators: [{ type: Injectable }]
|
|
223
|
+
});
|
|
425
224
|
function withHighlighter(provider) {
|
|
426
|
-
|
|
225
|
+
return {
|
|
226
|
+
provide: MarkedContentHighlighter,
|
|
227
|
+
...provider
|
|
228
|
+
};
|
|
427
229
|
}
|
|
428
|
-
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region packages/content/src/lib/marked-setup.service.ts
|
|
429
232
|
/**
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
class MarkedSetupService {
|
|
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
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
i0.ɵɵ
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
},
|
|
537
|
-
withContentFileLoader(),
|
|
538
|
-
withContentListLoader(),
|
|
233
|
+
* Credit goes to Scully for original implementation
|
|
234
|
+
* https://github.com/scullyio/scully/blob/main/libs/scully/src/lib/fileHanderPlugins/markdown.ts
|
|
235
|
+
*/
|
|
236
|
+
var MarkedSetupService = class MarkedSetupService {
|
|
237
|
+
constructor() {
|
|
238
|
+
this.highlighter = inject(MarkedContentHighlighter, { optional: true });
|
|
239
|
+
const renderer = new marked.Renderer();
|
|
240
|
+
renderer.code = ({ text, lang }) => {
|
|
241
|
+
if (lang === "mermaid") return "<pre class=\"mermaid\">" + text + "</pre>";
|
|
242
|
+
if (!lang) return "<pre><code>" + text + "</code></pre>";
|
|
243
|
+
if (this.highlighter?.augmentCodeBlock) return this.highlighter?.augmentCodeBlock(text, lang);
|
|
244
|
+
return `<pre class="language-${lang}"><code class="language-${lang}">${text}</code></pre>`;
|
|
245
|
+
};
|
|
246
|
+
const extensions = [gfmHeadingId(), mangle()];
|
|
247
|
+
if (this.highlighter) extensions.push(this.highlighter.getHighlightExtension());
|
|
248
|
+
marked.use(...extensions, {
|
|
249
|
+
renderer,
|
|
250
|
+
pedantic: false,
|
|
251
|
+
gfm: true,
|
|
252
|
+
breaks: false
|
|
253
|
+
});
|
|
254
|
+
this.marked = marked;
|
|
255
|
+
}
|
|
256
|
+
getMarkedInstance() {
|
|
257
|
+
return this.marked;
|
|
258
|
+
}
|
|
259
|
+
static {
|
|
260
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
261
|
+
minVersion: "12.0.0",
|
|
262
|
+
version: "21.2.5",
|
|
263
|
+
ngImport: i0,
|
|
264
|
+
type: MarkedSetupService,
|
|
265
|
+
deps: [],
|
|
266
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
static {
|
|
270
|
+
this.ɵprov = i0.ɵɵngDeclareInjectable({
|
|
271
|
+
minVersion: "12.0.0",
|
|
272
|
+
version: "21.2.5",
|
|
273
|
+
ngImport: i0,
|
|
274
|
+
type: MarkedSetupService
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
279
|
+
minVersion: "12.0.0",
|
|
280
|
+
version: "21.2.5",
|
|
281
|
+
ngImport: i0,
|
|
282
|
+
type: MarkedSetupService,
|
|
283
|
+
decorators: [{ type: Injectable }],
|
|
284
|
+
ctorParameters: () => []
|
|
285
|
+
});
|
|
286
|
+
//#endregion
|
|
287
|
+
//#region packages/content/src/lib/markdown-content-renderer.service.ts
|
|
288
|
+
var MarkdownContentRendererService = class MarkdownContentRendererService {
|
|
289
|
+
#marked = inject(MarkedSetupService, { self: true });
|
|
290
|
+
async render(content) {
|
|
291
|
+
return {
|
|
292
|
+
content: await this.#marked.getMarkedInstance().parse(content),
|
|
293
|
+
toc: getHeadingList()
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
getContentHeadings(content) {
|
|
297
|
+
return [...content.matchAll(/^(#{1,6})\s+(.+?)\s*$/gm)].map((match) => ({
|
|
298
|
+
id: match[2].trim().toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-"),
|
|
299
|
+
level: match[1].length,
|
|
300
|
+
text: match[2].trim()
|
|
301
|
+
}));
|
|
302
|
+
}
|
|
303
|
+
enhance() {}
|
|
304
|
+
static {
|
|
305
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
306
|
+
minVersion: "12.0.0",
|
|
307
|
+
version: "21.2.5",
|
|
308
|
+
ngImport: i0,
|
|
309
|
+
type: MarkdownContentRendererService,
|
|
310
|
+
deps: [],
|
|
311
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
static {
|
|
315
|
+
this.ɵprov = i0.ɵɵngDeclareInjectable({
|
|
316
|
+
minVersion: "12.0.0",
|
|
317
|
+
version: "21.2.5",
|
|
318
|
+
ngImport: i0,
|
|
319
|
+
type: MarkdownContentRendererService
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
324
|
+
minVersion: "12.0.0",
|
|
325
|
+
version: "21.2.5",
|
|
326
|
+
ngImport: i0,
|
|
327
|
+
type: MarkdownContentRendererService,
|
|
328
|
+
decorators: [{ type: Injectable }]
|
|
329
|
+
});
|
|
330
|
+
//#endregion
|
|
331
|
+
//#region packages/content/src/lib/provide-content.ts
|
|
332
|
+
var CONTENT_RENDERER_PROVIDERS = [
|
|
333
|
+
{
|
|
334
|
+
provide: ContentRenderer,
|
|
335
|
+
useClass: NoopContentRenderer
|
|
336
|
+
},
|
|
337
|
+
withContentFileLoader(),
|
|
338
|
+
withContentListLoader()
|
|
539
339
|
];
|
|
540
340
|
function withMarkdownRenderer(options) {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
{
|
|
546
|
-
provide: MERMAID_IMPORT_TOKEN,
|
|
547
|
-
useFactory: options.loadMermaid,
|
|
548
|
-
},
|
|
549
|
-
]
|
|
550
|
-
: [],
|
|
551
|
-
];
|
|
341
|
+
return [CONTENT_RENDERER_PROVIDERS, options?.loadMermaid ? [{
|
|
342
|
+
provide: MERMAID_IMPORT_TOKEN,
|
|
343
|
+
useFactory: options.loadMermaid
|
|
344
|
+
}] : []];
|
|
552
345
|
}
|
|
553
346
|
function provideContent(...features) {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
347
|
+
return [{
|
|
348
|
+
provide: RenderTaskService,
|
|
349
|
+
useClass: RenderTaskService
|
|
350
|
+
}, ...features];
|
|
558
351
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
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
|
-
|
|
352
|
+
var MERMAID_IMPORT_TOKEN = new InjectionToken("mermaid_import");
|
|
353
|
+
//#endregion
|
|
354
|
+
//#region packages/content/src/lib/markdown-route.component.ts
|
|
355
|
+
var AnalogMarkdownRouteComponent = class AnalogMarkdownRouteComponent {
|
|
356
|
+
constructor() {
|
|
357
|
+
this.sanitizer = inject(DomSanitizer);
|
|
358
|
+
this.route = inject(ActivatedRoute);
|
|
359
|
+
this.contentRenderer = inject(ContentRenderer);
|
|
360
|
+
this.content = this.sanitizer.bypassSecurityTrustHtml(this.route.snapshot.data["renderedAnalogContent"]);
|
|
361
|
+
this.classes = "analog-markdown-route";
|
|
362
|
+
}
|
|
363
|
+
ngAfterViewChecked() {
|
|
364
|
+
this.contentRenderer.enhance();
|
|
365
|
+
}
|
|
366
|
+
static {
|
|
367
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
368
|
+
minVersion: "12.0.0",
|
|
369
|
+
version: "21.2.5",
|
|
370
|
+
ngImport: i0,
|
|
371
|
+
type: AnalogMarkdownRouteComponent,
|
|
372
|
+
deps: [],
|
|
373
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
static {
|
|
377
|
+
this.ɵcmp = i0.ɵɵngDeclareComponent({
|
|
378
|
+
minVersion: "14.0.0",
|
|
379
|
+
version: "21.2.5",
|
|
380
|
+
type: AnalogMarkdownRouteComponent,
|
|
381
|
+
isStandalone: true,
|
|
382
|
+
selector: "analog-markdown-route",
|
|
383
|
+
inputs: { classes: "classes" },
|
|
384
|
+
hostDirectives: [{ directive: AnchorNavigationDirective }],
|
|
385
|
+
ngImport: i0,
|
|
386
|
+
template: `<div [innerHTML]="content" [class]="classes"></div>`,
|
|
387
|
+
isInline: true,
|
|
388
|
+
encapsulation: i0.ViewEncapsulation.None,
|
|
389
|
+
preserveWhitespaces: true
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
394
|
+
minVersion: "12.0.0",
|
|
395
|
+
version: "21.2.5",
|
|
396
|
+
ngImport: i0,
|
|
397
|
+
type: AnalogMarkdownRouteComponent,
|
|
398
|
+
decorators: [{
|
|
399
|
+
type: Component,
|
|
400
|
+
args: [{
|
|
401
|
+
selector: "analog-markdown-route",
|
|
402
|
+
standalone: true,
|
|
403
|
+
imports: [],
|
|
404
|
+
hostDirectives: [AnchorNavigationDirective],
|
|
405
|
+
preserveWhitespaces: true,
|
|
406
|
+
encapsulation: ViewEncapsulation.None,
|
|
407
|
+
template: `<div [innerHTML]="content" [class]="classes"></div>`
|
|
408
|
+
}]
|
|
409
|
+
}],
|
|
410
|
+
propDecorators: { classes: [{ type: Input }] }
|
|
411
|
+
});
|
|
412
|
+
//#endregion
|
|
413
|
+
//#region packages/content/src/lib/markdown.component.ts
|
|
414
|
+
var AnalogMarkdownComponent = class AnalogMarkdownComponent {
|
|
415
|
+
constructor() {
|
|
416
|
+
this.sanitizer = inject(DomSanitizer);
|
|
417
|
+
this.route = inject(ActivatedRoute);
|
|
418
|
+
this.zone = inject(NgZone);
|
|
419
|
+
this.platformId = inject(PLATFORM_ID);
|
|
420
|
+
this.mermaidImport = inject(MERMAID_IMPORT_TOKEN, { optional: true });
|
|
421
|
+
this.contentSource = toSignal(this.getContentSource());
|
|
422
|
+
this.htmlContent = computed(() => {
|
|
423
|
+
const inputContent = this.content();
|
|
424
|
+
if (inputContent) return this.sanitizer.bypassSecurityTrustHtml(inputContent);
|
|
425
|
+
return this.contentSource();
|
|
426
|
+
}, ...[]);
|
|
427
|
+
this.content = input(...[]);
|
|
428
|
+
this.classes = input("analog-markdown", ...[]);
|
|
429
|
+
this.contentRenderer = inject(ContentRenderer);
|
|
430
|
+
if (isPlatformBrowser(this.platformId) && this.mermaidImport) this.loadMermaid(this.mermaidImport);
|
|
431
|
+
}
|
|
432
|
+
getContentSource() {
|
|
433
|
+
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}`)));
|
|
434
|
+
}
|
|
435
|
+
async renderContent(content) {
|
|
436
|
+
return (await this.contentRenderer.render(content)).content;
|
|
437
|
+
}
|
|
438
|
+
ngAfterViewChecked() {
|
|
439
|
+
this.contentRenderer.enhance();
|
|
440
|
+
this.zone.runOutsideAngular(() => this.mermaid?.default.run());
|
|
441
|
+
}
|
|
442
|
+
loadMermaid(mermaidImport) {
|
|
443
|
+
this.zone.runOutsideAngular(() => from(mermaidImport).pipe(takeUntilDestroyed()).subscribe((mermaid) => {
|
|
444
|
+
this.mermaid = mermaid;
|
|
445
|
+
this.mermaid.default.initialize({ startOnLoad: false });
|
|
446
|
+
this.mermaid?.default.run();
|
|
447
|
+
}));
|
|
448
|
+
}
|
|
449
|
+
static {
|
|
450
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
451
|
+
minVersion: "12.0.0",
|
|
452
|
+
version: "21.2.5",
|
|
453
|
+
ngImport: i0,
|
|
454
|
+
type: AnalogMarkdownComponent,
|
|
455
|
+
deps: [],
|
|
456
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
static {
|
|
460
|
+
this.ɵcmp = i0.ɵɵngDeclareComponent({
|
|
461
|
+
minVersion: "17.1.0",
|
|
462
|
+
version: "21.2.5",
|
|
463
|
+
type: AnalogMarkdownComponent,
|
|
464
|
+
isStandalone: true,
|
|
465
|
+
selector: "analog-markdown",
|
|
466
|
+
inputs: {
|
|
467
|
+
content: {
|
|
468
|
+
classPropertyName: "content",
|
|
469
|
+
publicName: "content",
|
|
470
|
+
isSignal: true,
|
|
471
|
+
isRequired: false,
|
|
472
|
+
transformFunction: null
|
|
473
|
+
},
|
|
474
|
+
classes: {
|
|
475
|
+
classPropertyName: "classes",
|
|
476
|
+
publicName: "classes",
|
|
477
|
+
isSignal: true,
|
|
478
|
+
isRequired: false,
|
|
479
|
+
transformFunction: null
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
hostDirectives: [{ directive: AnchorNavigationDirective }],
|
|
483
|
+
ngImport: i0,
|
|
484
|
+
template: ` <div [innerHTML]="htmlContent()" [class]="classes()"></div> `,
|
|
485
|
+
isInline: true,
|
|
486
|
+
encapsulation: i0.ViewEncapsulation.None,
|
|
487
|
+
preserveWhitespaces: true
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
492
|
+
minVersion: "12.0.0",
|
|
493
|
+
version: "21.2.5",
|
|
494
|
+
ngImport: i0,
|
|
495
|
+
type: AnalogMarkdownComponent,
|
|
496
|
+
decorators: [{
|
|
497
|
+
type: Component,
|
|
498
|
+
args: [{
|
|
499
|
+
selector: "analog-markdown",
|
|
500
|
+
standalone: true,
|
|
501
|
+
hostDirectives: [AnchorNavigationDirective],
|
|
502
|
+
preserveWhitespaces: true,
|
|
503
|
+
encapsulation: ViewEncapsulation.None,
|
|
504
|
+
template: ` <div [innerHTML]="htmlContent()" [class]="classes()"></div> `
|
|
505
|
+
}]
|
|
506
|
+
}],
|
|
507
|
+
ctorParameters: () => [],
|
|
508
|
+
propDecorators: {
|
|
509
|
+
content: [{
|
|
510
|
+
type: i0.Input,
|
|
511
|
+
args: [{
|
|
512
|
+
isSignal: true,
|
|
513
|
+
alias: "content",
|
|
514
|
+
required: false
|
|
515
|
+
}]
|
|
516
|
+
}],
|
|
517
|
+
classes: [{
|
|
518
|
+
type: i0.Input,
|
|
519
|
+
args: [{
|
|
520
|
+
isSignal: true,
|
|
521
|
+
alias: "classes",
|
|
522
|
+
required: false
|
|
523
|
+
}]
|
|
524
|
+
}]
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
//#endregion
|
|
528
|
+
//#region packages/content/src/lib/devtools/content-devtools-renderer.ts
|
|
529
|
+
/**
|
|
530
|
+
* Token for the wrapped renderer that DevTools delegates to.
|
|
531
|
+
* @internal
|
|
532
|
+
*/
|
|
533
|
+
var DEVTOOLS_INNER_RENDERER = new InjectionToken("devtools_inner_renderer");
|
|
534
|
+
/**
|
|
535
|
+
* Wraps an existing ContentRenderer to collect timing and metadata for the
|
|
536
|
+
* Content DevTools panel. Dispatches a custom event on the window after
|
|
537
|
+
* each render so the devtools client can update.
|
|
538
|
+
*
|
|
539
|
+
* @experimental Content DevTools is experimental and may change in future releases.
|
|
540
|
+
*/
|
|
541
|
+
var DevToolsContentRenderer = class DevToolsContentRenderer extends ContentRenderer {
|
|
542
|
+
constructor() {
|
|
543
|
+
super(...arguments);
|
|
544
|
+
this.inner = inject(DEVTOOLS_INNER_RENDERER);
|
|
545
|
+
}
|
|
546
|
+
async render(content) {
|
|
547
|
+
const start = performance.now();
|
|
548
|
+
const result = await this.inner.render(content);
|
|
549
|
+
const elapsed = performance.now() - start;
|
|
550
|
+
if (typeof window !== "undefined") window.dispatchEvent(new CustomEvent("analog-content-devtools-data", { detail: {
|
|
551
|
+
renderer: this.inner.constructor.name,
|
|
552
|
+
renderTimeMs: elapsed,
|
|
553
|
+
toc: result.toc,
|
|
554
|
+
contentLength: content.length,
|
|
555
|
+
headingCount: result.toc.length,
|
|
556
|
+
frontmatter: {}
|
|
557
|
+
} }));
|
|
558
|
+
return result;
|
|
559
|
+
}
|
|
560
|
+
getContentHeadings(content) {
|
|
561
|
+
return this.inner.getContentHeadings(content);
|
|
562
|
+
}
|
|
563
|
+
enhance() {
|
|
564
|
+
this.inner.enhance();
|
|
565
|
+
}
|
|
566
|
+
static {
|
|
567
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
568
|
+
minVersion: "12.0.0",
|
|
569
|
+
version: "21.2.5",
|
|
570
|
+
ngImport: i0,
|
|
571
|
+
type: DevToolsContentRenderer,
|
|
572
|
+
deps: null,
|
|
573
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
static {
|
|
577
|
+
this.ɵprov = i0.ɵɵngDeclareInjectable({
|
|
578
|
+
minVersion: "12.0.0",
|
|
579
|
+
version: "21.2.5",
|
|
580
|
+
ngImport: i0,
|
|
581
|
+
type: DevToolsContentRenderer
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
586
|
+
minVersion: "12.0.0",
|
|
587
|
+
version: "21.2.5",
|
|
588
|
+
ngImport: i0,
|
|
589
|
+
type: DevToolsContentRenderer,
|
|
590
|
+
decorators: [{ type: Injectable }]
|
|
591
|
+
});
|
|
592
|
+
//#endregion
|
|
593
|
+
//#region packages/content/src/lib/devtools/content-devtools-plugin.ts
|
|
594
|
+
var import___vite_browser_external = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
595
|
+
module.exports = {};
|
|
596
|
+
})))();
|
|
597
|
+
/**
|
|
598
|
+
* Vite plugin that injects the Analog Content DevTools panel in dev mode.
|
|
599
|
+
*
|
|
600
|
+
* Shows render time, frontmatter data, TOC, and content stats in a floating
|
|
601
|
+
* panel. Dev-only — completely stripped from production builds.
|
|
602
|
+
*
|
|
603
|
+
* @experimental Content DevTools is experimental and may change in future releases.
|
|
604
|
+
*
|
|
605
|
+
* @example
|
|
606
|
+
* ```typescript
|
|
607
|
+
* // vite.config.ts
|
|
608
|
+
* import { contentDevToolsPlugin } from '@analogjs/content/devtools';
|
|
609
|
+
*
|
|
610
|
+
* export default defineConfig({
|
|
611
|
+
* plugins: [
|
|
612
|
+
* analog({ ... }),
|
|
613
|
+
* contentDevToolsPlugin(),
|
|
614
|
+
* ],
|
|
615
|
+
* });
|
|
616
|
+
* ```
|
|
617
|
+
*/
|
|
618
|
+
function contentDevToolsPlugin() {
|
|
619
|
+
let isDev = false;
|
|
620
|
+
return {
|
|
621
|
+
name: "analog-content-devtools",
|
|
622
|
+
apply: "serve",
|
|
623
|
+
configResolved(config) {
|
|
624
|
+
isDev = config.command === "serve";
|
|
625
|
+
},
|
|
626
|
+
transformIndexHtml: {
|
|
627
|
+
order: "post",
|
|
628
|
+
async handler(html) {
|
|
629
|
+
if (!isDev) return html;
|
|
630
|
+
const pluginDir = (0, import___vite_browser_external.dirname)((0, import___vite_browser_external.fileURLToPath)(import.meta.url));
|
|
631
|
+
const cssPath = (0, import___vite_browser_external.resolve)(pluginDir, "content-devtools.styles.css");
|
|
632
|
+
const clientPath = (0, import___vite_browser_external.resolve)(pluginDir, "content-devtools-client.ts");
|
|
633
|
+
let css;
|
|
634
|
+
let clientCode;
|
|
635
|
+
try {
|
|
636
|
+
css = (0, import___vite_browser_external.readFileSync)(cssPath, "utf-8");
|
|
637
|
+
clientCode = (0, import___vite_browser_external.readFileSync)(clientPath, "utf-8");
|
|
638
|
+
} catch {
|
|
639
|
+
return html;
|
|
640
|
+
}
|
|
641
|
+
const transformResult = await transformWithOxc(clientCode, "content-devtools-client.ts", { lang: "ts" });
|
|
642
|
+
const injection = `
|
|
643
|
+
<style>${css}</style>
|
|
644
|
+
<script type="module">${transformResult.code}<\/script>`;
|
|
645
|
+
return html.replace("</body>", `${injection}\n</body>`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
};
|
|
642
649
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
args: [{
|
|
646
|
-
selector: 'analog-markdown',
|
|
647
|
-
standalone: true,
|
|
648
|
-
hostDirectives: [AnchorNavigationDirective],
|
|
649
|
-
preserveWhitespaces: true,
|
|
650
|
-
encapsulation: ViewEncapsulation.None,
|
|
651
|
-
template: ` <div [innerHTML]="htmlContent()" [class]="classes()"></div> `,
|
|
652
|
-
}]
|
|
653
|
-
}], ctorParameters: () => [], propDecorators: { content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: false }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }] } });
|
|
654
|
-
|
|
650
|
+
//#endregion
|
|
651
|
+
//#region packages/content/src/lib/devtools/index.ts
|
|
655
652
|
/**
|
|
656
|
-
|
|
657
|
-
|
|
653
|
+
* Wraps the given ContentRenderer with DevTools instrumentation.
|
|
654
|
+
*
|
|
655
|
+
* The supplied renderer class is provided under DEVTOOLS_INNER_RENDERER and
|
|
656
|
+
* DevToolsContentRenderer becomes the new ContentRenderer, delegating
|
|
657
|
+
* all calls and collecting timing data.
|
|
658
|
+
*
|
|
659
|
+
* @param innerRenderer The renderer class to wrap with devtools instrumentation.
|
|
660
|
+
*
|
|
661
|
+
* @experimental Content DevTools is experimental and may change in future releases.
|
|
662
|
+
*
|
|
663
|
+
* @example
|
|
664
|
+
* ```typescript
|
|
665
|
+
* provideContent(
|
|
666
|
+
* withContentDevTools(NoopContentRenderer),
|
|
667
|
+
* );
|
|
668
|
+
* ```
|
|
669
|
+
*/
|
|
670
|
+
function withContentDevTools(innerRenderer) {
|
|
671
|
+
return [{
|
|
672
|
+
provide: DEVTOOLS_INNER_RENDERER,
|
|
673
|
+
useClass: innerRenderer
|
|
674
|
+
}, {
|
|
675
|
+
provide: ContentRenderer,
|
|
676
|
+
useClass: DevToolsContentRenderer
|
|
677
|
+
}];
|
|
678
|
+
}
|
|
679
|
+
//#endregion
|
|
680
|
+
export { AnchorNavigationDirective, CONTENT_FILE_LOADER, CONTENT_LIST_LOADER, ContentRenderer, DevToolsContentRenderer, FrontmatterValidationError, MERMAID_IMPORT_TOKEN, AnalogMarkdownComponent as MarkdownComponent, MarkdownContentRendererService, AnalogMarkdownRouteComponent as MarkdownRouteComponent, MarkedContentHighlighter, MarkedSetupService, NoopContentRenderer, contentDevToolsPlugin, injectContent, injectContentFileLoader, injectContentFiles, injectContentFilesMap, injectContentListLoader, parseRawContentFile, parseRawContentFileAsync, provideContent, withContentDevTools, withContentFileLoader, withContentListLoader, withHighlighter, withMarkdownRenderer };
|
|
658
681
|
|
|
659
|
-
|
|
660
|
-
//# sourceMappingURL=analogjs-content.mjs.map
|
|
682
|
+
//# sourceMappingURL=analogjs-content.mjs.map
|