@analogjs/content 3.0.0-alpha.11 → 3.0.0-alpha.13

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.
@@ -0,0 +1,284 @@
1
+ import * as i0 from "@angular/core";
2
+ import { Injectable, InjectionToken, TransferState, inject, makeStateKey, signal, ɵPendingTasksInternal } from "@angular/core";
3
+ //#region packages/content/src/lib/content-renderer.ts
4
+ var ContentRenderer = class ContentRenderer {
5
+ async render(content) {
6
+ return {
7
+ content,
8
+ toc: []
9
+ };
10
+ }
11
+ getContentHeadings(_content) {
12
+ return [];
13
+ }
14
+ enhance() {}
15
+ static {
16
+ this.ɵfac = i0.ɵɵngDeclareFactory({
17
+ minVersion: "12.0.0",
18
+ version: "21.2.5",
19
+ ngImport: i0,
20
+ type: ContentRenderer,
21
+ deps: [],
22
+ target: i0.ɵɵFactoryTarget.Injectable
23
+ });
24
+ }
25
+ static {
26
+ this.ɵprov = i0.ɵɵngDeclareInjectable({
27
+ minVersion: "12.0.0",
28
+ version: "21.2.5",
29
+ ngImport: i0,
30
+ type: ContentRenderer
31
+ });
32
+ }
33
+ };
34
+ i0.ɵɵngDeclareClassMetadata({
35
+ minVersion: "12.0.0",
36
+ version: "21.2.5",
37
+ ngImport: i0,
38
+ type: ContentRenderer,
39
+ decorators: [{ type: Injectable }]
40
+ });
41
+ var NoopContentRenderer = class {
42
+ constructor() {
43
+ this.transferState = inject(TransferState);
44
+ this.contentId = 0;
45
+ }
46
+ /**
47
+ * Generates a hash from the content string
48
+ * to be used with the transfer state
49
+ */
50
+ generateHash(str) {
51
+ let hash = 0;
52
+ for (let i = 0, len = str.length; i < len; i++) {
53
+ const chr = str.charCodeAt(i);
54
+ hash = (hash << 5) - hash + chr;
55
+ hash |= 0;
56
+ }
57
+ return hash;
58
+ }
59
+ async render(content) {
60
+ this.contentId = this.generateHash(content);
61
+ const toc = this.getContentHeadings(content);
62
+ const key = makeStateKey(`content-headings-${this.contentId}`);
63
+ return {
64
+ content,
65
+ toc: this.transferState.get(key, toc)
66
+ };
67
+ }
68
+ enhance() {}
69
+ getContentHeadings(content) {
70
+ return this.extractHeadings(content);
71
+ }
72
+ extractHeadings(content) {
73
+ const markdownHeadings = this.extractHeadingsFromMarkdown(content);
74
+ if (markdownHeadings.length > 0) return markdownHeadings;
75
+ return this.extractHeadingsFromHtml(content);
76
+ }
77
+ extractHeadingsFromMarkdown(content) {
78
+ const lines = content.split("\n");
79
+ const toc = [];
80
+ const slugCounts = /* @__PURE__ */ new Map();
81
+ for (const line of lines) {
82
+ const match = /^(#{1,6})\s+(.+?)\s*$/.exec(line);
83
+ if (!match) continue;
84
+ const level = match[1].length;
85
+ const text = match[2].trim();
86
+ if (!text) continue;
87
+ const baseSlug = text.toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-");
88
+ const count = slugCounts.get(baseSlug) ?? 0;
89
+ slugCounts.set(baseSlug, count + 1);
90
+ const id = count === 0 ? baseSlug : `${baseSlug}-${count}`;
91
+ toc.push({
92
+ id,
93
+ level,
94
+ text
95
+ });
96
+ }
97
+ return toc;
98
+ }
99
+ extractHeadingsFromHtml(content) {
100
+ const toc = [];
101
+ const slugCounts = /* @__PURE__ */ new Map();
102
+ for (const match of content.matchAll(/<h([1-6])([^>]*)>([\s\S]*?)<\/h\1>/gi)) {
103
+ const level = Number(match[1]);
104
+ const attrs = match[2] ?? "";
105
+ const text = (match[3] ?? "").replace(/<[^>]+>/g, "").trim();
106
+ if (!text) continue;
107
+ const idMatch = /\sid=(['"])(.*?)\1/i.exec(attrs) ?? /\sid=([^\s>]+)/i.exec(attrs);
108
+ let id = idMatch?.[2] ?? idMatch?.[1] ?? "";
109
+ if (!id) id = this.makeSlug(text, slugCounts);
110
+ toc.push({
111
+ id,
112
+ level,
113
+ text
114
+ });
115
+ }
116
+ return toc;
117
+ }
118
+ makeSlug(text, slugCounts) {
119
+ const baseSlug = text.toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-");
120
+ const count = slugCounts.get(baseSlug) ?? 0;
121
+ slugCounts.set(baseSlug, count + 1);
122
+ return count === 0 ? baseSlug : `${baseSlug}-${count}`;
123
+ }
124
+ };
125
+ //#endregion
126
+ //#region packages/content/src/lib/get-content-files.ts
127
+ /**
128
+ * Returns the list of content files by filename with ?analog-content-list=true.
129
+ * We use the query param to transform the return into an array of
130
+ * just front matter attributes.
131
+ *
132
+ * @returns
133
+ */
134
+ var getContentFilesList = () => {
135
+ return {};
136
+ };
137
+ /**
138
+ * Returns the lazy loaded content files for lookups.
139
+ *
140
+ * @returns
141
+ */
142
+ var getContentFiles = () => {
143
+ return {};
144
+ };
145
+ //#endregion
146
+ //#region packages/content/src/lib/content-files-list-token.ts
147
+ function getSlug(filename) {
148
+ const base = (filename.split(/[/\\]/).pop() || "").trim().replace(/\.[^./\\]+$/, "");
149
+ return base === "index" ? "" : base;
150
+ }
151
+ var CONTENT_FILES_LIST_TOKEN = new InjectionToken("@analogjs/content Content Files List", {
152
+ providedIn: "root",
153
+ factory() {
154
+ const contentFiles = getContentFilesList();
155
+ return Object.keys(contentFiles).map((filename) => {
156
+ const attributes = contentFiles[filename];
157
+ const slug = attributes["slug"];
158
+ return {
159
+ filename,
160
+ attributes,
161
+ slug: slug ? encodeURI(slug) : encodeURI(getSlug(filename))
162
+ };
163
+ });
164
+ }
165
+ });
166
+ //#endregion
167
+ //#region packages/content/src/lib/content-files-token.ts
168
+ var CONTENT_FILES_TOKEN = new InjectionToken("@analogjs/content Content Files", {
169
+ providedIn: "root",
170
+ factory() {
171
+ const allFiles = { ...getContentFiles() };
172
+ const contentFilesList = inject(CONTENT_FILES_LIST_TOKEN);
173
+ const lookup = {};
174
+ contentFilesList.forEach((item) => {
175
+ const contentFilename = item.filename.replace(/(.*?)\/content/, "/src/content");
176
+ const fileParts = contentFilename.split("/");
177
+ const filePath = fileParts.slice(0, fileParts.length - 1).join("/");
178
+ const fileNameParts = fileParts[fileParts.length - 1].split(".");
179
+ const ext = fileNameParts[fileNameParts.length - 1];
180
+ let slug = item.slug ?? "";
181
+ if (slug === "") slug = "index";
182
+ lookup[contentFilename] = `${slug.includes("/") ? `/src/content/${slug}` : `${filePath}/${slug}`}.${ext}`.replace(/\/{2,}/g, "/");
183
+ });
184
+ const objectUsingSlugAttribute = {};
185
+ Object.entries(allFiles).forEach((entry) => {
186
+ const filename = entry[0];
187
+ const value = entry[1];
188
+ const newFilename = lookup[filename.replace(/^\/(.*?)\/content/, "/src/content")];
189
+ if (newFilename !== void 0) {
190
+ const objectFilename = newFilename.replace(/^\/(.*?)\/content/, "/src/content");
191
+ objectUsingSlugAttribute[objectFilename] = value;
192
+ }
193
+ });
194
+ return objectUsingSlugAttribute;
195
+ }
196
+ });
197
+ new InjectionToken("@analogjs/content Content Files", {
198
+ providedIn: "root",
199
+ factory() {
200
+ return signal(inject(CONTENT_FILES_TOKEN));
201
+ }
202
+ });
203
+ //#endregion
204
+ //#region packages/content/src/lib/render-task.service.ts
205
+ var RenderTaskService = class RenderTaskService {
206
+ #pendingTasks = inject(ɵPendingTasksInternal);
207
+ addRenderTask() {
208
+ return this.#pendingTasks.add();
209
+ }
210
+ clearRenderTask(clear) {
211
+ if (typeof clear === "function") clear();
212
+ else if (typeof this.#pendingTasks.remove === "function") this.#pendingTasks.remove(clear);
213
+ }
214
+ static {
215
+ this.ɵfac = i0.ɵɵngDeclareFactory({
216
+ minVersion: "12.0.0",
217
+ version: "21.2.5",
218
+ ngImport: i0,
219
+ type: RenderTaskService,
220
+ deps: [],
221
+ target: i0.ɵɵFactoryTarget.Injectable
222
+ });
223
+ }
224
+ static {
225
+ this.ɵprov = i0.ɵɵngDeclareInjectable({
226
+ minVersion: "12.0.0",
227
+ version: "21.2.5",
228
+ ngImport: i0,
229
+ type: RenderTaskService
230
+ });
231
+ }
232
+ };
233
+ i0.ɵɵngDeclareClassMetadata({
234
+ minVersion: "12.0.0",
235
+ version: "21.2.5",
236
+ ngImport: i0,
237
+ type: RenderTaskService,
238
+ decorators: [{ type: Injectable }]
239
+ });
240
+ //#endregion
241
+ //#region packages/content/src/lib/inject-content-files.ts
242
+ function injectContentFiles(filterFn) {
243
+ const renderTaskService = inject(RenderTaskService);
244
+ const task = renderTaskService.addRenderTask();
245
+ const allContentFiles = inject(CONTENT_FILES_LIST_TOKEN);
246
+ renderTaskService.clearRenderTask(task);
247
+ if (filterFn) return allContentFiles.filter(filterFn);
248
+ return allContentFiles;
249
+ }
250
+ function injectContentFilesMap() {
251
+ return inject(CONTENT_FILES_TOKEN);
252
+ }
253
+ //#endregion
254
+ //#region packages/content/src/lib/content-file-loader.ts
255
+ var CONTENT_FILE_LOADER = new InjectionToken("@analogjs/content/resource File Loader");
256
+ function injectContentFileLoader() {
257
+ return inject(CONTENT_FILE_LOADER);
258
+ }
259
+ function withContentFileLoader() {
260
+ return {
261
+ provide: CONTENT_FILE_LOADER,
262
+ useFactory() {
263
+ return async () => injectContentFilesMap();
264
+ }
265
+ };
266
+ }
267
+ //#endregion
268
+ //#region packages/content/src/lib/content-list-loader.ts
269
+ var CONTENT_LIST_LOADER = new InjectionToken("@analogjs/content/resource List Loader");
270
+ function injectContentListLoader() {
271
+ return inject(CONTENT_LIST_LOADER);
272
+ }
273
+ function withContentListLoader() {
274
+ return {
275
+ provide: CONTENT_LIST_LOADER,
276
+ useFactory() {
277
+ return async () => injectContentFiles();
278
+ }
279
+ };
280
+ }
281
+ //#endregion
282
+ export { injectContentFileLoader as a, injectContentFilesMap as c, ContentRenderer as d, NoopContentRenderer as f, CONTENT_FILE_LOADER as i, RenderTaskService as l, injectContentListLoader as n, withContentFileLoader as o, withContentListLoader as r, injectContentFiles as s, CONTENT_LIST_LOADER as t, CONTENT_FILES_TOKEN as u };
283
+
284
+ //# sourceMappingURL=content-list-loader.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@analogjs/content",
3
- "version": "3.0.0-alpha.11",
3
+ "version": "3.0.0-alpha.13",
4
4
  "description": "Content Rendering for Analog",
5
5
  "type": "module",
6
6
  "author": "Brandon Roberts <robertsbt@gmail.com>",
@@ -29,7 +29,7 @@
29
29
  "@angular/router": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
30
30
  "@nx/devkit": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0 || ^22",
31
31
  "front-matter": "^4.0.2",
32
- "marked": "^15.0.7",
32
+ "marked": "^17.0.5",
33
33
  "marked-gfm-heading-id": "^4.1.1",
34
34
  "marked-highlight": "^2.2.1",
35
35
  "marked-mangle": "^1.1.10",
@@ -1,5 +1,5 @@
1
1
  import { InjectionToken } from '@angular/core';
2
- import { ContentRenderer, RenderedContent, TableOfContentItem } from '@analogjs/content';
2
+ import { ContentRenderer, RenderedContent, TableOfContentItem } from '../../../src/lib/content-renderer';
3
3
  import * as i0 from "@angular/core";
4
4
  /**
5
5
  * Options for the experimental md4x-based content renderer.
@@ -1,4 +1,4 @@
1
- import { ContentRenderer, RenderedContent, TableOfContentItem } from '@analogjs/content';
1
+ import { ContentRenderer, RenderedContent, TableOfContentItem } from '../../../src/lib/content-renderer';
2
2
  import * as i0 from "@angular/core";
3
3
  /**
4
4
  * Content renderer backed by md4x/wasm for client-side (browser) rendering.
@@ -1,9 +0,0 @@
1
- import { t as __commonJSMin } from "./chunk.mjs";
2
- //#region __vite-browser-external
3
- var require___vite_browser_external = /* @__PURE__ */ __commonJSMin(((exports, module) => {
4
- module.exports = {};
5
- }));
6
- //#endregion
7
- export { require___vite_browser_external as t };
8
-
9
- //# sourceMappingURL=__vite-browser-external.mjs.map
@@ -1,7 +0,0 @@
1
- //#region __vite-optional-peer-dep:tsx/cjs/api:vite
2
- var api_vite_default = {};
3
- throw new Error(`Could not resolve "tsx/cjs/api" imported by "vite". Is it installed?`);
4
- //#endregion
5
- export { api_vite_default as default };
6
-
7
- //# sourceMappingURL=api_vite.mjs.map