@voltro/content 0.53.0
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/CHANGELOG.md +9987 -0
- package/LICENSE +57 -0
- package/README.md +26 -0
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +1349 -0
- package/dist/index.d.ts +161 -0
- package/dist/index.js +132 -0
- package/dist/markdown.d.ts +70 -0
- package/dist/markdown.js +118 -0
- package/package.json +49 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
|
|
3
|
+
export declare const buildFeed: <A>(entries: ReadonlyArray<A>, options: BuildFeedOptions<A>) => string;
|
|
4
|
+
|
|
5
|
+
export declare interface BuildFeedOptions<A> {
|
|
6
|
+
readonly title: string;
|
|
7
|
+
readonly siteUrl: string;
|
|
8
|
+
readonly description?: string;
|
|
9
|
+
/** Feed path (`/rss.xml`) — becomes the atom:link self href. */
|
|
10
|
+
readonly feedPath?: string;
|
|
11
|
+
/** Map one entry to its feed item. Return null to exclude it. */
|
|
12
|
+
readonly item: (entry: A) => FeedItemInput | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export declare interface CollectionDefinition<A = Record<string, unknown>> {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
/** App-root-relative content directory (`content/posts`). */
|
|
18
|
+
readonly directory: string;
|
|
19
|
+
/** Frontmatter schema (markdown collections) or file schema (data
|
|
20
|
+
* collections). Violations are BUILD/BOOT errors naming file + field. */
|
|
21
|
+
readonly schema: Schema.Schema<A, any, never>;
|
|
22
|
+
/** `'markdown'` (default): every `.md` under the directory, frontmatter +
|
|
23
|
+
* rendered body. `'data'`: every `.json`, decoded whole — the
|
|
24
|
+
* authors.json case, no render path. */
|
|
25
|
+
readonly kind?: 'markdown' | 'data';
|
|
26
|
+
readonly i18n?: CollectionLocales;
|
|
27
|
+
/** Render options for this collection's markdown bodies (link rewriting,
|
|
28
|
+
* a code-fence hook, …) — applied wherever the SERVER renders an entry. */
|
|
29
|
+
readonly markdown?: RenderMarkdownOptions;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export declare interface CollectionLocales {
|
|
33
|
+
/** The locale trees this collection carries (first path segment). */
|
|
34
|
+
readonly locales: ReadonlyArray<string>;
|
|
35
|
+
/**
|
|
36
|
+
* What a missing translation does: `'fallback'` (default) serves the
|
|
37
|
+
* defaultLocale's entry under the requested locale; `'missing'` omits it
|
|
38
|
+
* (the page 404s). Incomplete translations are the normal case — decide
|
|
39
|
+
* per collection instead of improvising per page.
|
|
40
|
+
*/
|
|
41
|
+
readonly missing?: 'fallback' | 'missing';
|
|
42
|
+
readonly defaultLocale: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export declare const contentArtifactPath: (collection: string, locale: string | undefined, slug?: string) => string;
|
|
46
|
+
|
|
47
|
+
export declare interface ContentEntry<A = Record<string, unknown>> {
|
|
48
|
+
readonly slug: string;
|
|
49
|
+
/** The locale tree this entry came from (localized collections only). When
|
|
50
|
+
* the missing-policy substituted the default locale's entry, `locale` is
|
|
51
|
+
* the REQUESTED locale and `fallback` is true. */
|
|
52
|
+
readonly locale?: string;
|
|
53
|
+
readonly fallback?: boolean;
|
|
54
|
+
readonly data: A;
|
|
55
|
+
/** Markdown collections: the rendered HTML (shiki included, server-rendered). */
|
|
56
|
+
readonly html?: string;
|
|
57
|
+
readonly headings?: ReadonlyArray<ContentHeading>;
|
|
58
|
+
/** Raw markdown body (server side; omitted from client artifacts by default). */
|
|
59
|
+
readonly body?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
declare interface ContentHeading {
|
|
63
|
+
readonly depth: number;
|
|
64
|
+
readonly slug: string;
|
|
65
|
+
readonly text: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export declare const contentRoot: () => string | null;
|
|
69
|
+
|
|
70
|
+
export declare const defineCollection: <A>(definition: CollectionDefinition<A>) => CollectionDefinition<A>;
|
|
71
|
+
|
|
72
|
+
export declare interface FeedItemInput {
|
|
73
|
+
readonly title: string;
|
|
74
|
+
readonly link: string;
|
|
75
|
+
readonly date: string | Date;
|
|
76
|
+
readonly description?: string;
|
|
77
|
+
readonly guid?: string;
|
|
78
|
+
/** `<category>` tags on the item. */
|
|
79
|
+
readonly categories?: ReadonlyArray<string>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Every entry of a collection (sorted by slug). Server: filesystem + render;
|
|
83
|
+
* client: the build artifact. The `data` type is the schema's decoded type. */
|
|
84
|
+
export declare const getCollection: <A = Record<string, unknown>>(name: string, options?: GetCollectionOptions) => Promise<ReadonlyArray<ContentEntry<A>>>;
|
|
85
|
+
|
|
86
|
+
export declare interface GetCollectionOptions {
|
|
87
|
+
readonly locale?: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** One entry by slug, or null. */
|
|
91
|
+
export declare const getEntry: <A = Record<string, unknown>>(name: string, slug: string, options?: GetCollectionOptions) => Promise<ContentEntry<A> | null>;
|
|
92
|
+
|
|
93
|
+
/** Dev watch invalidation + test seam. */
|
|
94
|
+
export declare const invalidateContentCache: (collection?: string) => void;
|
|
95
|
+
|
|
96
|
+
export declare const reference: (collection: string) => Schema.Schema<string>;
|
|
97
|
+
|
|
98
|
+
/** Every reference field declared by a collection's schema (top level):
|
|
99
|
+
* field name → target collection. */
|
|
100
|
+
export declare const referenceFieldsOf: (definition: CollectionDefinition) => ReadonlyMap<string, string>;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* `reference('<collection>')` — a frontmatter field that names an entry of
|
|
104
|
+
* another collection by slug (`author: jane` against an `authors` data
|
|
105
|
+
* collection). Decodes as the slug string; the BUILD validates every
|
|
106
|
+
* reference against the target collection, so a dangling one is a named
|
|
107
|
+
* build error, not a runtime null. Resolve it with
|
|
108
|
+
* `getEntry(<collection>, value)`.
|
|
109
|
+
*/
|
|
110
|
+
export declare const ReferenceTarget: unique symbol;
|
|
111
|
+
|
|
112
|
+
/** The referenced collection of one schema property, if it is a reference. */
|
|
113
|
+
export declare const referenceTargetOf: (ast: {
|
|
114
|
+
readonly annotations?: Readonly<Record<symbol, unknown>>;
|
|
115
|
+
}) => string | undefined;
|
|
116
|
+
|
|
117
|
+
export declare const registeredCollection: (name: string) => CollectionDefinition | undefined;
|
|
118
|
+
|
|
119
|
+
export declare const registeredCollections: () => ReadonlyArray<CollectionDefinition>;
|
|
120
|
+
|
|
121
|
+
declare interface RenderMarkdownOptions {
|
|
122
|
+
/** Rewrite relative links (`./sibling.md`, `../other/page.md`) into route
|
|
123
|
+
* paths. Receives the raw href; return the replacement (or the input). */
|
|
124
|
+
readonly resolveLink?: (href: string) => string;
|
|
125
|
+
/** Highlight code fences with shiki (node only; a browser call renders
|
|
126
|
+
* plain `<pre>`). Default true. */
|
|
127
|
+
readonly highlight?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Post-process one rendered code fence. Receives the raw code, the resolved
|
|
130
|
+
* language (first info-string token, lowercased), the FULL info string
|
|
131
|
+
* (` ```ts preview=data-table ` ⇒ `'ts preview=data-table'`), and the
|
|
132
|
+
* rendered html. Return the replacement html — e.g. append a live-demo
|
|
133
|
+
* mount point derived from the info string.
|
|
134
|
+
*/
|
|
135
|
+
readonly codeFence?: (fence: {
|
|
136
|
+
readonly text: string;
|
|
137
|
+
readonly lang: string;
|
|
138
|
+
readonly info: string;
|
|
139
|
+
readonly html: string;
|
|
140
|
+
}) => string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Test seam. */
|
|
144
|
+
export declare const resetCollectionsForTest: () => void;
|
|
145
|
+
|
|
146
|
+
export declare const setContentRoot: (root: string | null) => void;
|
|
147
|
+
|
|
148
|
+
/** Slug from the path relative to the collection directory — nested dirs
|
|
149
|
+
* stay in the slug, the extension is stripped, locale segment removed when
|
|
150
|
+
* the collection is localized. */
|
|
151
|
+
export declare const slugFromRelativePath: (relativePath: string) => string;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Validate every `reference('<collection>')` field across all registered
|
|
155
|
+
* collections: each value must name an existing entry of the target
|
|
156
|
+
* collection. The BUILD calls this after loading — a dangling reference is a
|
|
157
|
+
* named build error, not a runtime null.
|
|
158
|
+
*/
|
|
159
|
+
export declare const validateReferences: () => Promise<void>;
|
|
160
|
+
|
|
161
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Schema as e } from "effect";
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var t = Object.defineProperty, n = /* @__PURE__ */ ((e, n) => {
|
|
4
|
+
let r = {};
|
|
5
|
+
for (var i in e) t(r, i, {
|
|
6
|
+
get: e[i],
|
|
7
|
+
enumerable: !0
|
|
8
|
+
});
|
|
9
|
+
return n || t(r, Symbol.toStringTag, { value: "Module" }), r;
|
|
10
|
+
})({
|
|
11
|
+
ReferenceTarget: () => u,
|
|
12
|
+
defineCollection: () => a,
|
|
13
|
+
reference: () => d,
|
|
14
|
+
referenceFieldsOf: () => p,
|
|
15
|
+
referenceTargetOf: () => f,
|
|
16
|
+
registeredCollection: () => s,
|
|
17
|
+
registeredCollections: () => o,
|
|
18
|
+
resetCollectionsForTest: () => c,
|
|
19
|
+
slugFromRelativePath: () => l
|
|
20
|
+
}), r = Symbol.for("@voltro/content:collections"), i = () => {
|
|
21
|
+
let e = globalThis;
|
|
22
|
+
return e[r] ??= /* @__PURE__ */ new Map();
|
|
23
|
+
}, a = (e) => {
|
|
24
|
+
let t = i().get(e.name);
|
|
25
|
+
if (t !== void 0 && t.directory !== e.directory) throw Error(`defineCollection('${e.name}') is declared twice with different directories ('${t.directory}' vs '${e.directory}') — collection names are global.`);
|
|
26
|
+
return i().set(e.name, e), e;
|
|
27
|
+
}, o = () => [...i().values()], s = (e) => i().get(e), c = () => {
|
|
28
|
+
i().clear();
|
|
29
|
+
}, l = (e) => e.replace(/\\/g, "/").replace(/\.(md|mdx|json)$/i, ""), u = Symbol.for("@voltro/content:reference-target"), d = (t) => e.String.annotations({ [u]: t }), f = (e) => {
|
|
30
|
+
let t = e.annotations?.[u];
|
|
31
|
+
return typeof t == "string" ? t : void 0;
|
|
32
|
+
}, p = (e) => {
|
|
33
|
+
let t = /* @__PURE__ */ new Map(), n = e.schema.ast;
|
|
34
|
+
for (let e of n?.propertySignatures ?? []) {
|
|
35
|
+
let n = [e.type, ...e.type.types ?? []];
|
|
36
|
+
for (let r of n) {
|
|
37
|
+
let n = f(r);
|
|
38
|
+
n !== void 0 && typeof e.name == "string" && t.set(e.name, n);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return t;
|
|
42
|
+
}, m = typeof globalThis.process?.versions?.node == "string", h = Symbol.for("@voltro/content:root"), g = (e) => {
|
|
43
|
+
let t = globalThis;
|
|
44
|
+
t[h] = e;
|
|
45
|
+
}, _ = () => globalThis[h] ?? null, v = Symbol.for("@voltro/content:cache"), y = () => {
|
|
46
|
+
let e = globalThis;
|
|
47
|
+
return e[v] ??= /* @__PURE__ */ new Map();
|
|
48
|
+
}, b = (e) => {
|
|
49
|
+
if (e === void 0) {
|
|
50
|
+
y().clear();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
for (let t of [...y().keys()]) (t === e || t.startsWith(`${e}|`)) && y().delete(t);
|
|
54
|
+
}, x = (e, t) => {
|
|
55
|
+
let n = t === void 0 ? e.name : `${e.name}|${t}`, r = y().get(n);
|
|
56
|
+
return r === void 0 &&
|
|
57
|
+
// @vite-ignore keeps the whole render pipeline (marked + shiki grammars)
|
|
58
|
+
(r = import(
|
|
59
|
+
/* @vite-ignore */
|
|
60
|
+
"./serverLoad"
|
|
61
|
+
).then(({ loadServerCollection: n }) => n(e, t)), y().set(n, r)), r;
|
|
62
|
+
}, S = (e, t, n) => {
|
|
63
|
+
let r = t === void 0 ? e : `${e}.${t}`;
|
|
64
|
+
return n === void 0 ? `/assets/content/${r}/index.json` : `/assets/content/${r}/${n}.json`;
|
|
65
|
+
}, C = async (e) => {
|
|
66
|
+
let t = await fetch(e);
|
|
67
|
+
return t.ok ? await t.json() : null;
|
|
68
|
+
}, w = (e, t) => {
|
|
69
|
+
let n = s(e);
|
|
70
|
+
return n?.i18n === void 0 ? t : t ?? n.i18n.defaultLocale;
|
|
71
|
+
}, T = async (e, t = {}) => {
|
|
72
|
+
if (m) {
|
|
73
|
+
let n = s(e);
|
|
74
|
+
if (n === void 0) throw Error(`getCollection('${e}'): no such collection is defined — export a defineCollection({ name: '${e}', … }) from a *.collection.ts.`);
|
|
75
|
+
return (await x(n, t.locale)).entries;
|
|
76
|
+
}
|
|
77
|
+
return await C(S(e, w(e, t.locale))) ?? [];
|
|
78
|
+
}, E = async (e, t, n = {}) => {
|
|
79
|
+
if (m) {
|
|
80
|
+
let r = s(e);
|
|
81
|
+
if (r === void 0) throw Error(`getEntry('${e}'): no such collection is defined.`);
|
|
82
|
+
return (await x(r, n.locale)).bySlug.get(t) ?? null;
|
|
83
|
+
}
|
|
84
|
+
return await C(S(e, w(e, n.locale), t));
|
|
85
|
+
}, D = async () => {
|
|
86
|
+
let { referenceFieldsOf: e, registeredCollections: t } = await Promise.resolve().then(() => n), r = [];
|
|
87
|
+
for (let n of t()) {
|
|
88
|
+
let t = e(n);
|
|
89
|
+
if (t.size === 0) continue;
|
|
90
|
+
let i = n.i18n?.locales ?? [void 0];
|
|
91
|
+
for (let e of i) {
|
|
92
|
+
let i = await T(n.name, e === void 0 ? {} : { locale: e });
|
|
93
|
+
for (let e of i) for (let [i, a] of t) {
|
|
94
|
+
let t = e.data[i];
|
|
95
|
+
typeof t == "string" && t !== "" && await E(a, t) === null && r.push(`collection '${n.name}', entry '${e.slug}': field '${i}' references '${t}' in collection '${a}' — no such entry.`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (r.length > 0) throw Error(`dangling content reference(s):\n${r.join("\n")}`);
|
|
100
|
+
}, O = (e) => e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'"), k = (e) => {
|
|
101
|
+
let t = e instanceof Date ? e : new Date(e);
|
|
102
|
+
return Number.isNaN(t.getTime()) ? String(e) : t.toUTCString();
|
|
103
|
+
}, A = (e, t) => {
|
|
104
|
+
let n = t.siteUrl.replace(/\/+$/, ""), r = e.map((e) => t.item(e)).filter((e) => e !== null).sort((e, t) => new Date(t.date).getTime() - new Date(e.date).getTime()).map((e) => {
|
|
105
|
+
let t = e.link.startsWith("http") ? e.link : `${n}/${e.link.replace(/^\/+/, "")}`;
|
|
106
|
+
return [
|
|
107
|
+
" <item>",
|
|
108
|
+
` <title>${O(e.title)}</title>`,
|
|
109
|
+
` <link>${O(t)}</link>`,
|
|
110
|
+
` <guid isPermaLink="${e.guid === void 0 ? "true" : "false"}">${O(e.guid ?? t)}</guid>`,
|
|
111
|
+
` <pubDate>${O(k(e.date))}</pubDate>`,
|
|
112
|
+
...e.description === void 0 ? [] : [` <description>${O(e.description)}</description>`],
|
|
113
|
+
...(e.categories ?? []).map((e) => ` <category>${O(e)}</category>`),
|
|
114
|
+
" </item>"
|
|
115
|
+
].join("\n");
|
|
116
|
+
}).join("\n");
|
|
117
|
+
return [
|
|
118
|
+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
|
|
119
|
+
"<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">",
|
|
120
|
+
" <channel>",
|
|
121
|
+
` <title>${O(t.title)}</title>`,
|
|
122
|
+
` <link>${O(n)}</link>`,
|
|
123
|
+
...t.description === void 0 ? [" <description></description>"] : [` <description>${O(t.description)}</description>`],
|
|
124
|
+
` <atom:link href="${O(`${n}${t.feedPath ?? "/rss.xml"}`)}" rel="self" type="application/rss+xml" />`,
|
|
125
|
+
...r === "" ? [] : [r],
|
|
126
|
+
" </channel>",
|
|
127
|
+
"</rss>",
|
|
128
|
+
""
|
|
129
|
+
].join("\n");
|
|
130
|
+
};
|
|
131
|
+
//#endregion
|
|
132
|
+
export { u as ReferenceTarget, A as buildFeed, S as contentArtifactPath, _ as contentRoot, a as defineCollection, T as getCollection, E as getEntry, b as invalidateContentCache, d as reference, p as referenceFieldsOf, f as referenceTargetOf, s as registeredCollection, o as registeredCollections, c as resetCollectionsForTest, g as setContentRoot, l as slugFromRelativePath, D as validateReferences };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export declare interface ContentHeading {
|
|
2
|
+
readonly depth: number;
|
|
3
|
+
readonly slug: string;
|
|
4
|
+
readonly text: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Heading data (depth, slug, text) straight from markdown SOURCE — no render
|
|
9
|
+
* pass, browser-safe, sync. Slugs and dedup counters are computed by the SAME
|
|
10
|
+
* code `renderMarkdown` uses, so a TOC built from this always matches the
|
|
11
|
+
* rendered `id=` attributes.
|
|
12
|
+
*/
|
|
13
|
+
export declare const extractHeadings: (body: string) => ReadonlyArray<ContentHeading>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Highlight one raw code string to dual-theme shiki HTML — the SAME path a
|
|
17
|
+
* markdown fence takes, for callers rendering code outside markdown (a
|
|
18
|
+
* demo-source panel, say). Node only: in a browser (or when shiki is not
|
|
19
|
+
* installed) it returns an escaped plain `<pre>`.
|
|
20
|
+
*/
|
|
21
|
+
export declare const highlightCode: (code: string, lang: string | undefined) => Promise<string>;
|
|
22
|
+
|
|
23
|
+
export declare interface ParsedFrontmatter {
|
|
24
|
+
readonly data: Readonly<Record<string, string | ReadonlyArray<string>>>;
|
|
25
|
+
readonly body: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** `key: scalar` lines between `---` markers — the only shapes our content
|
|
29
|
+
* needs, parsed without a YAML dependency. */
|
|
30
|
+
export declare const parseFrontmatter: (source: string) => ParsedFrontmatter;
|
|
31
|
+
|
|
32
|
+
export declare interface RenderedMarkdown {
|
|
33
|
+
readonly html: string;
|
|
34
|
+
readonly headings: ReadonlyArray<ContentHeading>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Render a markdown body (GFM) to HTML with heading ids/anchors and the
|
|
39
|
+
* `headings[]` TOC data. When `highlight` (default) and running on node, code
|
|
40
|
+
* fences are shiki-highlighted with light+dark themes; the dark variant
|
|
41
|
+
* activates via the standard `.dark .shiki` / `data-theme` CSS convention
|
|
42
|
+
* shiki's dual-theme output uses.
|
|
43
|
+
*/
|
|
44
|
+
export declare const renderMarkdown: (body: string, options?: RenderMarkdownOptions) => Promise<RenderedMarkdown>;
|
|
45
|
+
|
|
46
|
+
export declare interface RenderMarkdownOptions {
|
|
47
|
+
/** Rewrite relative links (`./sibling.md`, `../other/page.md`) into route
|
|
48
|
+
* paths. Receives the raw href; return the replacement (or the input). */
|
|
49
|
+
readonly resolveLink?: (href: string) => string;
|
|
50
|
+
/** Highlight code fences with shiki (node only; a browser call renders
|
|
51
|
+
* plain `<pre>`). Default true. */
|
|
52
|
+
readonly highlight?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Post-process one rendered code fence. Receives the raw code, the resolved
|
|
55
|
+
* language (first info-string token, lowercased), the FULL info string
|
|
56
|
+
* (` ```ts preview=data-table ` ⇒ `'ts preview=data-table'`), and the
|
|
57
|
+
* rendered html. Return the replacement html — e.g. append a live-demo
|
|
58
|
+
* mount point derived from the info string.
|
|
59
|
+
*/
|
|
60
|
+
readonly codeFence?: (fence: {
|
|
61
|
+
readonly text: string;
|
|
62
|
+
readonly lang: string;
|
|
63
|
+
readonly info: string;
|
|
64
|
+
readonly html: string;
|
|
65
|
+
}) => string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export declare const slugifyHeading: (value: string) => string;
|
|
69
|
+
|
|
70
|
+
export { }
|
package/dist/markdown.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { marked as e } from "marked";
|
|
2
|
+
//#region src/markdown.ts
|
|
3
|
+
var t = typeof globalThis.process?.versions?.node == "string", n = (e) => {
|
|
4
|
+
let t = e.trim();
|
|
5
|
+
return t.startsWith("[") && t.endsWith("]") ? t.slice(1, -1).split(",").map((e) => e.trim().replace(/^['"]|['"]$/g, "")).filter((e) => e.length > 0) : t.replace(/^['"]|['"]$/g, "");
|
|
6
|
+
}, r = (e) => {
|
|
7
|
+
let t = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(e);
|
|
8
|
+
if (!t) return {
|
|
9
|
+
data: {},
|
|
10
|
+
body: e
|
|
11
|
+
};
|
|
12
|
+
let [, r, i] = t, a = {};
|
|
13
|
+
for (let e of (r ?? "").split(/\r?\n/)) {
|
|
14
|
+
let t = e.indexOf(":");
|
|
15
|
+
if (t < 0) continue;
|
|
16
|
+
let r = e.slice(0, t).trim();
|
|
17
|
+
r && (a[r] = n(e.slice(t + 1)));
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
data: a,
|
|
21
|
+
body: i ?? ""
|
|
22
|
+
};
|
|
23
|
+
}, i = (e) => e.map((e) => "text" in e && typeof e.text == "string" ? e.text : "").join(""), a = (e) => e.toLowerCase().trim().replace(/<[^>]+>/g, "").replace(/[^\p{Letter}\p{Number}]+/gu, "-").replace(/^-+|-+$/g, ""), o = null, s = [
|
|
24
|
+
"ts",
|
|
25
|
+
"tsx",
|
|
26
|
+
"typescript",
|
|
27
|
+
"js",
|
|
28
|
+
"jsx",
|
|
29
|
+
"json",
|
|
30
|
+
"bash",
|
|
31
|
+
"sh",
|
|
32
|
+
"shell",
|
|
33
|
+
"css",
|
|
34
|
+
"html",
|
|
35
|
+
"sql",
|
|
36
|
+
"yaml",
|
|
37
|
+
"toml",
|
|
38
|
+
"md",
|
|
39
|
+
"mdx",
|
|
40
|
+
"diff",
|
|
41
|
+
"text",
|
|
42
|
+
"txt"
|
|
43
|
+
], c = () => t ? (o ??= import("shiki").then((e) => e.createHighlighter({
|
|
44
|
+
themes: ["github-light", "github-dark-dimmed"],
|
|
45
|
+
langs: [...s]
|
|
46
|
+
})).catch(() => null), o) : Promise.resolve(null), l = async (t, n = {}) => {
|
|
47
|
+
let r = [], o = /* @__PURE__ */ new Map(), l = n.highlight === !1 ? null : await c(), u = new e.Renderer();
|
|
48
|
+
if (u.heading = ({ depth: t, tokens: n }) => {
|
|
49
|
+
let s = e.Parser.parseInline(n), c = i(n), l = a(c), u = o.get(l) ?? 0;
|
|
50
|
+
o.set(l, u + 1);
|
|
51
|
+
let d = u === 0 ? l : `${l}-${u}`;
|
|
52
|
+
return r.push({
|
|
53
|
+
depth: t,
|
|
54
|
+
slug: d,
|
|
55
|
+
text: c
|
|
56
|
+
}), `<h${t} id="${d}"><a class="heading-anchor" href="#${d}" aria-hidden="true">#</a>${s}</h${t}>\n`;
|
|
57
|
+
}, n.resolveLink !== void 0) {
|
|
58
|
+
let t = n.resolveLink;
|
|
59
|
+
u.link = ({ href: n, title: r, tokens: i }) => {
|
|
60
|
+
let a = e.Parser.parseInline(i);
|
|
61
|
+
return `<a href="${t(n)}"${r ? ` title="${r}"` : ""}>${a}</a>`;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return u.code = ({ text: e, lang: t }) => {
|
|
65
|
+
let r = (t ?? "").trim(), i = (r.split(/\s+/)[0] ?? "").toLowerCase(), a;
|
|
66
|
+
if (l !== null && s.includes(i)) try {
|
|
67
|
+
a = l.codeToHtml(e, {
|
|
68
|
+
lang: i,
|
|
69
|
+
themes: {
|
|
70
|
+
light: "github-light",
|
|
71
|
+
dark: "github-dark-dimmed"
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
} catch {
|
|
75
|
+
a = `<pre><code>${e.replace(/&/g, "&").replace(/</g, "<")}</code></pre>\n`;
|
|
76
|
+
}
|
|
77
|
+
else a = `<pre><code>${e.replace(/&/g, "&").replace(/</g, "<")}</code></pre>\n`;
|
|
78
|
+
return n.codeFence === void 0 ? a : n.codeFence({
|
|
79
|
+
text: e,
|
|
80
|
+
lang: i,
|
|
81
|
+
info: r,
|
|
82
|
+
html: a
|
|
83
|
+
});
|
|
84
|
+
}, {
|
|
85
|
+
html: await e.parse(t, {
|
|
86
|
+
async: !0,
|
|
87
|
+
gfm: !0,
|
|
88
|
+
renderer: u
|
|
89
|
+
}),
|
|
90
|
+
headings: r
|
|
91
|
+
};
|
|
92
|
+
}, u = async (e, t) => {
|
|
93
|
+
let n = await c(), r = (t ?? "").trim().split(/\s+/)[0]?.toLowerCase() ?? "";
|
|
94
|
+
if (n !== null && s.includes(r)) try {
|
|
95
|
+
return n.codeToHtml(e, {
|
|
96
|
+
lang: r,
|
|
97
|
+
themes: {
|
|
98
|
+
light: "github-light",
|
|
99
|
+
dark: "github-dark-dimmed"
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
} catch {}
|
|
103
|
+
return `<pre><code>${e.replace(/&/g, "&").replace(/</g, "<")}</code></pre>`;
|
|
104
|
+
}, d = (t) => {
|
|
105
|
+
let n = [], r = /* @__PURE__ */ new Map();
|
|
106
|
+
for (let o of e.lexer(t)) {
|
|
107
|
+
if (o.type !== "heading") continue;
|
|
108
|
+
let e = o, t = i(e.tokens), s = a(t), c = r.get(s) ?? 0;
|
|
109
|
+
r.set(s, c + 1), n.push({
|
|
110
|
+
depth: e.depth,
|
|
111
|
+
slug: c === 0 ? s : `${s}-${c}`,
|
|
112
|
+
text: t
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return n;
|
|
116
|
+
};
|
|
117
|
+
//#endregion
|
|
118
|
+
export { d as extractHeadings, u as highlightCode, r as parseFrontmatter, l as renderMarkdown, a as slugifyHeading };
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@voltro/content",
|
|
3
|
+
"version": "0.53.0",
|
|
4
|
+
"description": "Content collections — file-based, schema-typed markdown content (defineCollection over content/<name>/**/*.md with effect/Schema frontmatter), an isomorphic getCollection/getEntry (server: FS + rendered HTML with node-gated shiki; client: fetches the build-emitted JSON artifacts), heading extraction, locale trees with fallback, data collections, and a generic feed builder.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"voltro",
|
|
7
|
+
"typescript",
|
|
8
|
+
"framework"
|
|
9
|
+
],
|
|
10
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
11
|
+
"homepage": "https://voltro.dev",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"email": "support@voltro.dev"
|
|
14
|
+
},
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Voltro UG",
|
|
17
|
+
"url": "https://voltro.dev"
|
|
18
|
+
},
|
|
19
|
+
"type": "module",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./markdown": {
|
|
27
|
+
"types": "./dist/markdown.d.ts",
|
|
28
|
+
"import": "./dist/markdown.js",
|
|
29
|
+
"default": "./dist/markdown.js"
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"sideEffects": false,
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=24.0.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"marked": "^18.0.9"
|
|
39
|
+
},
|
|
40
|
+
"optionalDependencies": {
|
|
41
|
+
"shiki": "^4.4.3"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"effect": "^3.22.0"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
}
|
|
49
|
+
}
|