@analogjs/content 3.0.0-alpha.10 → 3.0.0-alpha.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
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";
1
2
  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 { Component, Directive, HostListener, Injectable, InjectionToken, Input, NgZone, PLATFORM_ID, ViewEncapsulation, computed, inject, input } from "@angular/core";
3
4
  import { DOCUMENT, Location, isPlatformBrowser } from "@angular/common";
4
5
  import { ActivatedRoute, Router } from "@angular/router";
5
6
  import { Observable, from, of } from "rxjs";
@@ -10,6 +11,10 @@ import { marked } from "marked";
10
11
  import { mangle } from "marked-mangle";
11
12
  import { DomSanitizer } from "@angular/platform-browser";
12
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
13
18
  //#region packages/content/src/lib/anchor-navigation.directive.ts
14
19
  var AnchorNavigationDirective = class AnchorNavigationDirective {
15
20
  constructor() {
@@ -75,207 +80,6 @@ function isInternalUrl(anchorElement, document) {
75
80
  return anchorElement.host === document.location.host && anchorElement.protocol === document.location.protocol;
76
81
  }
77
82
  //#endregion
78
- //#region packages/content/src/lib/content-renderer.ts
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: "21.1.1",
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: "21.1.1",
104
- ngImport: i0,
105
- type: ContentRenderer
106
- });
107
- }
108
- };
109
- i0.ɵɵngDeclareClassMetadata({
110
- minVersion: "12.0.0",
111
- version: "21.1.1",
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
- /**
122
- * Generates a hash from the content string
123
- * to be used with the transfer state
124
- */
125
- generateHash(str) {
126
- let hash = 0;
127
- for (let i = 0, len = str.length; i < len; i++) {
128
- const chr = str.charCodeAt(i);
129
- hash = (hash << 5) - hash + chr;
130
- hash |= 0;
131
- }
132
- return hash;
133
- }
134
- async render(content) {
135
- this.contentId = this.generateHash(content);
136
- const toc = this.getContentHeadings(content);
137
- const key = makeStateKey(`content-headings-${this.contentId}`);
138
- return {
139
- content,
140
- toc: this.transferState.get(key, toc)
141
- };
142
- }
143
- enhance() {}
144
- getContentHeadings(content) {
145
- return this.extractHeadings(content);
146
- }
147
- extractHeadings(content) {
148
- const markdownHeadings = this.extractHeadingsFromMarkdown(content);
149
- if (markdownHeadings.length > 0) return markdownHeadings;
150
- return this.extractHeadingsFromHtml(content);
151
- }
152
- extractHeadingsFromMarkdown(content) {
153
- const lines = content.split("\n");
154
- const toc = [];
155
- const slugCounts = /* @__PURE__ */ new Map();
156
- for (const line of lines) {
157
- const match = /^(#{1,6})\s+(.+?)\s*$/.exec(line);
158
- if (!match) continue;
159
- const level = match[1].length;
160
- const text = match[2].trim();
161
- if (!text) continue;
162
- const baseSlug = text.toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-");
163
- const count = slugCounts.get(baseSlug) ?? 0;
164
- slugCounts.set(baseSlug, count + 1);
165
- const id = count === 0 ? baseSlug : `${baseSlug}-${count}`;
166
- toc.push({
167
- id,
168
- level,
169
- text
170
- });
171
- }
172
- return toc;
173
- }
174
- extractHeadingsFromHtml(content) {
175
- const toc = [];
176
- const slugCounts = /* @__PURE__ */ new Map();
177
- for (const match of content.matchAll(/<h([1-6])([^>]*)>([\s\S]*?)<\/h\1>/gi)) {
178
- const level = Number(match[1]);
179
- const attrs = match[2] ?? "";
180
- const text = (match[3] ?? "").replace(/<[^>]+>/g, "").trim();
181
- if (!text) continue;
182
- const idMatch = /\sid=(['"])(.*?)\1/i.exec(attrs) ?? /\sid=([^\s>]+)/i.exec(attrs);
183
- let id = idMatch?.[2] ?? idMatch?.[1] ?? "";
184
- if (!id) id = this.makeSlug(text, slugCounts);
185
- toc.push({
186
- id,
187
- level,
188
- text
189
- });
190
- }
191
- return toc;
192
- }
193
- makeSlug(text, slugCounts) {
194
- const baseSlug = text.toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-");
195
- const count = slugCounts.get(baseSlug) ?? 0;
196
- slugCounts.set(baseSlug, count + 1);
197
- return count === 0 ? baseSlug : `${baseSlug}-${count}`;
198
- }
199
- };
200
- //#endregion
201
- //#region packages/content/src/lib/get-content-files.ts
202
- /**
203
- * Returns the list of content files by filename with ?analog-content-list=true.
204
- * We use the query param to transform the return into an array of
205
- * just front matter attributes.
206
- *
207
- * @returns
208
- */
209
- var getContentFilesList = () => {
210
- return {};
211
- };
212
- /**
213
- * Returns the lazy loaded content files for lookups.
214
- *
215
- * @returns
216
- */
217
- var getContentFiles = () => {
218
- return {};
219
- };
220
- //#endregion
221
- //#region packages/content/src/lib/content-files-list-token.ts
222
- function getSlug(filename) {
223
- const base = (filename.split(/[/\\]/).pop() || "").trim().replace(/\.[^./\\]+$/, "");
224
- return base === "index" ? "" : base;
225
- }
226
- var CONTENT_FILES_LIST_TOKEN = new InjectionToken("@analogjs/content Content Files List", {
227
- providedIn: "root",
228
- factory() {
229
- const contentFiles = getContentFilesList();
230
- return Object.keys(contentFiles).map((filename) => {
231
- const attributes = contentFiles[filename];
232
- const slug = attributes["slug"];
233
- return {
234
- filename,
235
- attributes,
236
- slug: slug ? encodeURI(slug) : encodeURI(getSlug(filename))
237
- };
238
- });
239
- }
240
- });
241
- //#endregion
242
- //#region packages/content/src/lib/content-files-token.ts
243
- var CONTENT_FILES_TOKEN = new InjectionToken("@analogjs/content Content Files", {
244
- providedIn: "root",
245
- factory() {
246
- const allFiles = { ...getContentFiles() };
247
- const contentFilesList = inject(CONTENT_FILES_LIST_TOKEN);
248
- const lookup = {};
249
- contentFilesList.forEach((item) => {
250
- const contentFilename = item.filename.replace(/(.*?)\/content/, "/src/content");
251
- const fileParts = contentFilename.split("/");
252
- const filePath = fileParts.slice(0, fileParts.length - 1).join("/");
253
- const fileNameParts = fileParts[fileParts.length - 1].split(".");
254
- const ext = fileNameParts[fileNameParts.length - 1];
255
- let slug = item.slug ?? "";
256
- if (slug === "") slug = "index";
257
- lookup[contentFilename] = `${slug.includes("/") ? `/src/content/${slug}` : `${filePath}/${slug}`}.${ext}`.replace(/\/{2,}/g, "/");
258
- });
259
- const objectUsingSlugAttribute = {};
260
- Object.entries(allFiles).forEach((entry) => {
261
- const filename = entry[0];
262
- const value = entry[1];
263
- const newFilename = lookup[filename.replace(/^\/(.*?)\/content/, "/src/content")];
264
- if (newFilename !== void 0) {
265
- const objectFilename = newFilename.replace(/^\/(.*?)\/content/, "/src/content");
266
- objectUsingSlugAttribute[objectFilename] = value;
267
- }
268
- });
269
- return objectUsingSlugAttribute;
270
- }
271
- });
272
- new InjectionToken("@analogjs/content Content Files", {
273
- providedIn: "root",
274
- factory() {
275
- return signal(inject(CONTENT_FILES_TOKEN));
276
- }
277
- });
278
- //#endregion
279
83
  //#region packages/content/src/lib/parse-raw-content-file.ts
280
84
  var FrontmatterValidationError = class extends Error {
281
85
  constructor(issues, filename) {
@@ -317,43 +121,6 @@ async function parseRawContentFileAsync(rawContentFile, schema, filename) {
317
121
  };
318
122
  }
319
123
  //#endregion
320
- //#region packages/content/src/lib/render-task.service.ts
321
- var RenderTaskService = class RenderTaskService {
322
- #pendingTasks = inject(ɵPendingTasksInternal);
323
- addRenderTask() {
324
- return this.#pendingTasks.add();
325
- }
326
- clearRenderTask(clear) {
327
- if (typeof clear === "function") clear();
328
- else if (typeof this.#pendingTasks.remove === "function") this.#pendingTasks.remove(clear);
329
- }
330
- static {
331
- this.ɵfac = i0.ɵɵngDeclareFactory({
332
- minVersion: "12.0.0",
333
- version: "21.1.1",
334
- ngImport: i0,
335
- type: RenderTaskService,
336
- deps: [],
337
- target: i0.ɵɵFactoryTarget.Injectable
338
- });
339
- }
340
- static {
341
- this.ɵprov = i0.ɵɵngDeclareInjectable({
342
- minVersion: "12.0.0",
343
- version: "21.1.1",
344
- ngImport: i0,
345
- type: RenderTaskService
346
- });
347
- }
348
- };
349
- i0.ɵɵngDeclareClassMetadata({
350
- minVersion: "12.0.0",
351
- version: "21.1.1",
352
- ngImport: i0,
353
- type: RenderTaskService,
354
- decorators: [{ type: Injectable }]
355
- });
356
- //#endregion
357
124
  //#region packages/content/src/lib/content.ts
358
125
  function getContentFile(contentFiles, prefix, slug, fallback, renderTaskService, contentRenderer) {
359
126
  const normalizedFiles = {};
@@ -431,19 +198,6 @@ function injectContent(param = "slug", fallback = "No Content Found") {
431
198
  } else return getContentFile(contentFiles, "", param.customFilename, fallback, renderTaskService, contentRenderer).pipe(tap(() => renderTaskService.clearRenderTask(task)));
432
199
  }
433
200
  //#endregion
434
- //#region packages/content/src/lib/inject-content-files.ts
435
- function injectContentFiles(filterFn) {
436
- const renderTaskService = inject(RenderTaskService);
437
- const task = renderTaskService.addRenderTask();
438
- const allContentFiles = inject(CONTENT_FILES_LIST_TOKEN);
439
- renderTaskService.clearRenderTask(task);
440
- if (filterFn) return allContentFiles.filter(filterFn);
441
- return allContentFiles;
442
- }
443
- function injectContentFilesMap() {
444
- return inject(CONTENT_FILES_TOKEN);
445
- }
446
- //#endregion
447
201
  //#region packages/content/src/lib/marked-content-highlighter.ts
448
202
  var MarkedContentHighlighter = class MarkedContentHighlighter {
449
203
  static {
@@ -579,34 +333,6 @@ i0.ɵɵngDeclareClassMetadata({
579
333
  decorators: [{ type: Injectable }]
580
334
  });
581
335
  //#endregion
582
- //#region packages/content/src/lib/content-file-loader.ts
583
- var CONTENT_FILE_LOADER = new InjectionToken("@analogjs/content/resource File Loader");
584
- function injectContentFileLoader() {
585
- return inject(CONTENT_FILE_LOADER);
586
- }
587
- function withContentFileLoader() {
588
- return {
589
- provide: CONTENT_FILE_LOADER,
590
- useFactory() {
591
- return async () => injectContentFilesMap();
592
- }
593
- };
594
- }
595
- //#endregion
596
- //#region packages/content/src/lib/content-list-loader.ts
597
- var CONTENT_LIST_LOADER = new InjectionToken("@analogjs/content/resource List Loader");
598
- function injectContentListLoader() {
599
- return inject(CONTENT_LIST_LOADER);
600
- }
601
- function withContentListLoader() {
602
- return {
603
- provide: CONTENT_LIST_LOADER,
604
- useFactory() {
605
- return async () => injectContentFiles();
606
- }
607
- };
608
- }
609
- //#endregion
610
336
  //#region packages/content/src/lib/provide-content.ts
611
337
  var CONTENT_RENDERER_PROVIDERS = [
612
338
  {
@@ -804,6 +530,157 @@ i0.ɵɵngDeclareClassMetadata({
804
530
  }
805
531
  });
806
532
  //#endregion
807
- export { AnchorNavigationDirective, CONTENT_FILE_LOADER, CONTENT_LIST_LOADER, ContentRenderer, FrontmatterValidationError, MERMAID_IMPORT_TOKEN, AnalogMarkdownComponent as MarkdownComponent, MarkdownContentRendererService, AnalogMarkdownRouteComponent as MarkdownRouteComponent, MarkedContentHighlighter, MarkedSetupService, NoopContentRenderer, injectContent, injectContentFileLoader, injectContentFiles, injectContentFilesMap, injectContentListLoader, parseRawContentFile, parseRawContentFileAsync, provideContent, withContentFileLoader, withContentListLoader, withHighlighter, withMarkdownRenderer };
533
+ //#region packages/content/src/lib/devtools/content-devtools-renderer.ts
534
+ /**
535
+ * Token for the wrapped renderer that DevTools delegates to.
536
+ * @internal
537
+ */
538
+ var DEVTOOLS_INNER_RENDERER = new InjectionToken("devtools_inner_renderer");
539
+ /**
540
+ * Wraps an existing ContentRenderer to collect timing and metadata for the
541
+ * Content DevTools panel. Dispatches a custom event on the window after
542
+ * each render so the devtools client can update.
543
+ *
544
+ * @experimental Content DevTools is experimental and may change in future releases.
545
+ */
546
+ var DevToolsContentRenderer = class DevToolsContentRenderer extends ContentRenderer {
547
+ constructor() {
548
+ super(...arguments);
549
+ this.inner = inject(DEVTOOLS_INNER_RENDERER);
550
+ }
551
+ async render(content) {
552
+ const start = performance.now();
553
+ const result = await this.inner.render(content);
554
+ const elapsed = performance.now() - start;
555
+ if (typeof window !== "undefined") window.dispatchEvent(new CustomEvent("analog-content-devtools-data", { detail: {
556
+ renderer: this.inner.constructor.name,
557
+ renderTimeMs: elapsed,
558
+ toc: result.toc,
559
+ contentLength: content.length,
560
+ headingCount: result.toc.length
561
+ } }));
562
+ return result;
563
+ }
564
+ getContentHeadings(content) {
565
+ return this.inner.getContentHeadings(content);
566
+ }
567
+ enhance() {
568
+ this.inner.enhance();
569
+ }
570
+ static {
571
+ this.ɵfac = i0.ɵɵngDeclareFactory({
572
+ minVersion: "12.0.0",
573
+ version: "21.1.1",
574
+ ngImport: i0,
575
+ type: DevToolsContentRenderer,
576
+ deps: null,
577
+ target: i0.ɵɵFactoryTarget.Injectable
578
+ });
579
+ }
580
+ static {
581
+ this.ɵprov = i0.ɵɵngDeclareInjectable({
582
+ minVersion: "12.0.0",
583
+ version: "21.1.1",
584
+ ngImport: i0,
585
+ type: DevToolsContentRenderer
586
+ });
587
+ }
588
+ };
589
+ i0.ɵɵngDeclareClassMetadata({
590
+ minVersion: "12.0.0",
591
+ version: "21.1.1",
592
+ ngImport: i0,
593
+ type: DevToolsContentRenderer,
594
+ decorators: [{ type: Injectable }]
595
+ });
596
+ //#endregion
597
+ //#region packages/content/src/lib/devtools/content-devtools-plugin.ts
598
+ var import___vite_browser_external = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
599
+ module.exports = {};
600
+ })))();
601
+ /**
602
+ * Vite plugin that injects the Analog Content DevTools panel in dev mode.
603
+ *
604
+ * Shows render time, frontmatter data, TOC, and content stats in a floating
605
+ * panel. Dev-only — completely stripped from production builds.
606
+ *
607
+ * @experimental Content DevTools is experimental and may change in future releases.
608
+ *
609
+ * @example
610
+ * ```typescript
611
+ * // vite.config.ts
612
+ * import { contentDevToolsPlugin } from '@analogjs/content/devtools';
613
+ *
614
+ * export default defineConfig({
615
+ * plugins: [
616
+ * analog({ ... }),
617
+ * contentDevToolsPlugin(),
618
+ * ],
619
+ * });
620
+ * ```
621
+ */
622
+ function contentDevToolsPlugin() {
623
+ let isDev = false;
624
+ return {
625
+ name: "analog-content-devtools",
626
+ apply: "serve",
627
+ configResolved(config) {
628
+ isDev = config.command === "serve";
629
+ },
630
+ transformIndexHtml: {
631
+ order: "post",
632
+ async handler(html) {
633
+ if (!isDev) return html;
634
+ const pluginDir = (0, import___vite_browser_external.dirname)((0, import___vite_browser_external.fileURLToPath)(import.meta.url));
635
+ const cssPath = (0, import___vite_browser_external.resolve)(pluginDir, "content-devtools.styles.css");
636
+ const clientPath = (0, import___vite_browser_external.resolve)(pluginDir, "content-devtools-client.ts");
637
+ let css;
638
+ let clientCode;
639
+ try {
640
+ css = (0, import___vite_browser_external.readFileSync)(cssPath, "utf-8");
641
+ clientCode = (0, import___vite_browser_external.readFileSync)(clientPath, "utf-8");
642
+ } catch {
643
+ return html;
644
+ }
645
+ const transformResult = await transformWithOxc(clientCode, "content-devtools-client.ts", { lang: "ts" });
646
+ const injection = `
647
+ <style>${css}</style>
648
+ <script type="module">${transformResult.code}<\/script>`;
649
+ return html.replace("</body>", `${injection}\n</body>`);
650
+ }
651
+ }
652
+ };
653
+ }
654
+ //#endregion
655
+ //#region packages/content/src/lib/devtools/index.ts
656
+ /**
657
+ * Wraps the given ContentRenderer with DevTools instrumentation.
658
+ *
659
+ * The supplied renderer class is provided under DEVTOOLS_INNER_RENDERER and
660
+ * DevToolsContentRenderer becomes the new ContentRenderer, delegating
661
+ * all calls and collecting timing data.
662
+ *
663
+ * @param innerRenderer The renderer class to wrap with devtools instrumentation.
664
+ *
665
+ * @experimental Content DevTools is experimental and may change in future releases.
666
+ *
667
+ * @example
668
+ * ```typescript
669
+ * provideContent(
670
+ * withContentDevTools(NoopContentRenderer),
671
+ * );
672
+ * ```
673
+ */
674
+ function withContentDevTools(innerRenderer) {
675
+ return [{
676
+ provide: DEVTOOLS_INNER_RENDERER,
677
+ useClass: innerRenderer
678
+ }, {
679
+ provide: ContentRenderer,
680
+ useClass: DevToolsContentRenderer
681
+ }];
682
+ }
683
+ //#endregion
684
+ 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 };
808
685
 
809
686
  //# sourceMappingURL=analogjs-content.mjs.map