@tiramisu-docs/kit 0.1.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/README.md +103 -0
- package/components.json +14 -0
- package/dist/bin/mcp.d.ts +2 -0
- package/dist/bin/mcp.js +4 -0
- package/dist/config.d.ts +99 -0
- package/dist/config.js +36 -0
- package/dist/highlight.d.ts +10 -0
- package/dist/highlight.js +93 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/lib/components/index.d.ts +16 -0
- package/dist/lib/components/index.js +18 -0
- package/dist/lib/components/tiramisu/lang-icons.d.ts +4 -0
- package/dist/lib/components/tiramisu/lang-icons.js +77 -0
- package/dist/lib/components/ui/alert/index.d.ts +5 -0
- package/dist/lib/components/ui/alert/index.js +6 -0
- package/dist/lib/components/ui/badge/index.d.ts +2 -0
- package/dist/lib/components/ui/badge/index.js +1 -0
- package/dist/lib/components/ui/button/index.d.ts +4 -0
- package/dist/lib/components/ui/button/index.js +2 -0
- package/dist/lib/components/ui/card/index.d.ts +8 -0
- package/dist/lib/components/ui/card/index.js +10 -0
- package/dist/lib/components/ui/collapsible/index.d.ts +1 -0
- package/dist/lib/components/ui/collapsible/index.js +1 -0
- package/dist/lib/components/ui/dropdown-menu/index.d.ts +18 -0
- package/dist/lib/components/ui/dropdown-menu/index.js +18 -0
- package/dist/lib/components/ui/scroll-area/index.d.ts +1 -0
- package/dist/lib/components/ui/scroll-area/index.js +1 -0
- package/dist/lib/components/ui/separator/index.d.ts +1 -0
- package/dist/lib/components/ui/separator/index.js +1 -0
- package/dist/lib/components/ui/sheet/index.d.ts +3 -0
- package/dist/lib/components/ui/sheet/index.js +3 -0
- package/dist/lib/components/ui/tabs/index.d.ts +5 -0
- package/dist/lib/components/ui/tabs/index.js +7 -0
- package/dist/lib/open-links.d.ts +22 -0
- package/dist/lib/open-links.js +33 -0
- package/dist/lib/routes/docs/[...slug]/+page.d.ts +25 -0
- package/dist/lib/routes/docs/[...slug]/+page.js +109 -0
- package/dist/lib/utils.d.ts +5 -0
- package/dist/lib/utils.js +5 -0
- package/dist/mcp.d.ts +24 -0
- package/dist/mcp.js +155 -0
- package/dist/scan.d.ts +15 -0
- package/dist/scan.js +72 -0
- package/dist/seo.d.ts +63 -0
- package/dist/seo.js +160 -0
- package/dist/tiramisu-grammar.d.ts +2 -0
- package/dist/tiramisu-grammar.js +77 -0
- package/dist/types.d.ts +66 -0
- package/dist/types.js +1 -0
- package/dist/vite.d.ts +33 -0
- package/dist/vite.js +406 -0
- package/package.json +74 -0
- package/src/config.ts +133 -0
- package/src/highlight.ts +110 -0
- package/src/index.ts +6 -0
- package/src/lib/components/DocPage.svelte +430 -0
- package/src/lib/components/DocsLayout.svelte +145 -0
- package/src/lib/components/Footer.svelte +26 -0
- package/src/lib/components/Navbar.svelte +117 -0
- package/src/lib/components/PageFooter.svelte +63 -0
- package/src/lib/components/PrevNextNav.svelte +83 -0
- package/src/lib/components/SearchDialog.svelte +130 -0
- package/src/lib/components/Sidebar.svelte +237 -0
- package/src/lib/components/TableOfContents.svelte +50 -0
- package/src/lib/components/TopBar.svelte +407 -0
- package/src/lib/components/index.ts +19 -0
- package/src/lib/components/tiramisu/Accordion.svelte +16 -0
- package/src/lib/components/tiramisu/Badge.svelte +16 -0
- package/src/lib/components/tiramisu/Callout.svelte +26 -0
- package/src/lib/components/tiramisu/CodeBlock.svelte +56 -0
- package/src/lib/components/tiramisu/CodeTabs.svelte +123 -0
- package/src/lib/components/tiramisu/Demo.svelte +15 -0
- package/src/lib/components/tiramisu/FileTree.svelte +67 -0
- package/src/lib/components/tiramisu/MathBlock.svelte +26 -0
- package/src/lib/components/tiramisu/Mermaid.svelte +30 -0
- package/src/lib/components/tiramisu/NavCard.svelte +49 -0
- package/src/lib/components/tiramisu/Steps.svelte +60 -0
- package/src/lib/components/tiramisu/Tabs.svelte +87 -0
- package/src/lib/components/tiramisu/ZoomImage.svelte +114 -0
- package/src/lib/components/tiramisu/lang-icons.ts +81 -0
- package/src/lib/open-links.ts +50 -0
- package/src/lib/routes/docs/[...slug]/+page.svelte +26 -0
- package/src/lib/routes/docs/[...slug]/+page.ts +117 -0
- package/src/lib/styles/theme.css +222 -0
- package/src/lib/utils.ts +10 -0
- package/src/mcp.ts +180 -0
- package/src/scan.ts +92 -0
- package/src/seo.ts +193 -0
- package/src/tiramisu-grammar.ts +80 -0
- package/src/types.ts +71 -0
- package/src/virtual.d.ts +11 -0
- package/src/vite.ts +478 -0
- package/tests/config.test.ts +60 -0
- package/tests/mcp.test.ts +116 -0
- package/tests/scan.test.ts +48 -0
- package/tests/seo.test.ts +174 -0
- package/tests/vite.test.ts +283 -0
- package/tsconfig.json +19 -0
package/dist/vite.js
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import { compileTiramisu } from "@tiramisu-docs/core";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { highlightCodeBlocks } from "./highlight.js";
|
|
6
|
+
import { findTiramisuFiles, extractPlainText, titleCase } from "./scan.js";
|
|
7
|
+
/** Wrap compileTiramisu to produce Vite-friendly errors with file location */
|
|
8
|
+
function compileWithLocation(source, filePath) {
|
|
9
|
+
const dir = path.dirname(filePath);
|
|
10
|
+
try {
|
|
11
|
+
return compileTiramisu(source, {
|
|
12
|
+
resolveFile(src) {
|
|
13
|
+
const resolved = path.resolve(dir, src);
|
|
14
|
+
return fs.readFileSync(resolved, "utf-8");
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
const isParseError = err instanceof Error && "line" in err;
|
|
20
|
+
if (isParseError) {
|
|
21
|
+
const parseErr = err;
|
|
22
|
+
const enhanced = new Error(parseErr.hint ?? parseErr.message);
|
|
23
|
+
Object.assign(enhanced, {
|
|
24
|
+
id: filePath,
|
|
25
|
+
loc: { file: filePath, line: parseErr.line, column: parseErr.column ?? 0 },
|
|
26
|
+
plugin: "tiramisu-docs",
|
|
27
|
+
});
|
|
28
|
+
if (parseErr.stack)
|
|
29
|
+
enhanced.stack = parseErr.stack;
|
|
30
|
+
throw enhanced;
|
|
31
|
+
}
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function resolveLastEdited(filePath, meta) {
|
|
36
|
+
if (meta.lastEdited)
|
|
37
|
+
return new Date(meta.lastEdited).toISOString();
|
|
38
|
+
try {
|
|
39
|
+
const stdout = execSync(`git log -1 --format=%cI "${filePath}"`, {
|
|
40
|
+
encoding: "utf-8",
|
|
41
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
42
|
+
}).trim();
|
|
43
|
+
if (stdout)
|
|
44
|
+
return new Date(stdout).toISOString();
|
|
45
|
+
}
|
|
46
|
+
catch { }
|
|
47
|
+
return fs.statSync(filePath).mtime.toISOString();
|
|
48
|
+
}
|
|
49
|
+
const VIRTUAL_MODULE_ID = "virtual:tiramisu-docs";
|
|
50
|
+
const RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
|
|
51
|
+
const TIRAMISU_SVELTE_SUFFIX = ".tiramisu.svelte";
|
|
52
|
+
export function buildSidebarTree(docs, groupOrder = []) {
|
|
53
|
+
const groupMap = new Map();
|
|
54
|
+
function getOrCreateGroup(label) {
|
|
55
|
+
// Normalize lookup: case-insensitive match against existing groups
|
|
56
|
+
for (const [key, group] of groupMap) {
|
|
57
|
+
if (key.toLowerCase() === label.toLowerCase()) {
|
|
58
|
+
return group;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const group = { label, items: [] };
|
|
62
|
+
groupMap.set(label, group);
|
|
63
|
+
return group;
|
|
64
|
+
}
|
|
65
|
+
function getOrCreateSubgroup(parent, segment) {
|
|
66
|
+
const key = segment.toLowerCase();
|
|
67
|
+
const existing = parent.find((e) => e.type === "subgroup" && e.key === key);
|
|
68
|
+
if (existing)
|
|
69
|
+
return existing;
|
|
70
|
+
const sub = {
|
|
71
|
+
type: "subgroup",
|
|
72
|
+
key,
|
|
73
|
+
label: titleCase(segment),
|
|
74
|
+
items: [],
|
|
75
|
+
order: 999,
|
|
76
|
+
};
|
|
77
|
+
parent.push(sub);
|
|
78
|
+
return sub;
|
|
79
|
+
}
|
|
80
|
+
for (const doc of docs) {
|
|
81
|
+
const segments = doc.slug.split("/");
|
|
82
|
+
const item = {
|
|
83
|
+
type: "item",
|
|
84
|
+
title: doc.meta.title ?? doc.slug,
|
|
85
|
+
slug: doc.slug,
|
|
86
|
+
order: doc.meta.order ?? 999,
|
|
87
|
+
icon: doc.meta.icon,
|
|
88
|
+
};
|
|
89
|
+
if (segments.length === 1) {
|
|
90
|
+
// Root-level file: use meta.group
|
|
91
|
+
const groupLabel = doc.meta.group ?? "Docs";
|
|
92
|
+
const group = getOrCreateGroup(groupLabel);
|
|
93
|
+
group.items.push(item);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Nested file: first segment = group (static heading), deeper segments = subgroups
|
|
97
|
+
const groupLabel = titleCase(segments[0]);
|
|
98
|
+
const group = getOrCreateGroup(groupLabel);
|
|
99
|
+
const fileName = segments[segments.length - 1];
|
|
100
|
+
// Walk/create subgroups for middle segments (starting at segments[1])
|
|
101
|
+
let parent = group.items;
|
|
102
|
+
let lastSub = null;
|
|
103
|
+
for (let i = 1; i < segments.length - 1; i++) {
|
|
104
|
+
const sub = getOrCreateSubgroup(parent, segments[i]);
|
|
105
|
+
lastSub = sub;
|
|
106
|
+
parent = sub.items;
|
|
107
|
+
}
|
|
108
|
+
if (fileName === "index" && lastSub) {
|
|
109
|
+
// Deeper folder index → sets subgroup slug/label
|
|
110
|
+
lastSub.slug = item.slug;
|
|
111
|
+
if (item.order < lastSub.order)
|
|
112
|
+
lastSub.order = item.order;
|
|
113
|
+
if (item.title !== item.slug)
|
|
114
|
+
lastSub.label = item.title;
|
|
115
|
+
if (doc.meta.icon)
|
|
116
|
+
lastSub.icon = doc.meta.icon;
|
|
117
|
+
}
|
|
118
|
+
else if (fileName === "index" && !lastSub) {
|
|
119
|
+
// Top-level folder index (e.g. "integrations/index") → sets group label only
|
|
120
|
+
if (item.title !== item.slug) {
|
|
121
|
+
group.label = item.title;
|
|
122
|
+
}
|
|
123
|
+
if (doc.meta.icon)
|
|
124
|
+
group.icon = doc.meta.icon;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
parent.push(item);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Sort entries recursively
|
|
132
|
+
function sortEntries(entries) {
|
|
133
|
+
for (const entry of entries) {
|
|
134
|
+
if (entry.type === "subgroup") {
|
|
135
|
+
sortEntries(entry.items);
|
|
136
|
+
// Derive subgroup order from its index page, or min child order
|
|
137
|
+
if (!entry.slug) {
|
|
138
|
+
const minOrder = entry.items.reduce((min, e) => {
|
|
139
|
+
const o = e.order;
|
|
140
|
+
return o < min ? o : min;
|
|
141
|
+
}, entry.order);
|
|
142
|
+
entry.order = minOrder;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
entries.sort((a, b) => {
|
|
147
|
+
const ao = a.type === "item" ? a.order : a.order;
|
|
148
|
+
const bo = b.type === "item" ? b.order : b.order;
|
|
149
|
+
return ao - bo;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
const sidebar = [];
|
|
153
|
+
for (const [, group] of groupMap) {
|
|
154
|
+
sortEntries(group.items);
|
|
155
|
+
sidebar.push({ label: group.label, items: group.items, icon: group.icon });
|
|
156
|
+
}
|
|
157
|
+
// Sort groups by groupOrder config
|
|
158
|
+
if (groupOrder.length > 0) {
|
|
159
|
+
sidebar.sort((a, b) => {
|
|
160
|
+
const ai = groupOrder.indexOf(a.label);
|
|
161
|
+
const bi = groupOrder.indexOf(b.label);
|
|
162
|
+
const ao = ai === -1 ? Infinity : ai;
|
|
163
|
+
const bo = bi === -1 ? Infinity : bi;
|
|
164
|
+
return ao - bo;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
return sidebar;
|
|
168
|
+
}
|
|
169
|
+
export function buildSectionSidebars(docs, sections, implicitLabel) {
|
|
170
|
+
const allSectionPaths = flattenSectionPaths(sections);
|
|
171
|
+
const rootDocs = docs.filter((d) => !allSectionPaths.some((p) => d.slug === p || d.slug.startsWith(p + "/")));
|
|
172
|
+
const result = [];
|
|
173
|
+
if (rootDocs.length > 0) {
|
|
174
|
+
result.push({
|
|
175
|
+
label: implicitLabel ?? "Docs",
|
|
176
|
+
sidebar: buildSidebarTree(rootDocs),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
for (const section of sections) {
|
|
180
|
+
result.push(resolveSection(docs, section));
|
|
181
|
+
}
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
function flattenSectionPaths(sections) {
|
|
185
|
+
const paths = [];
|
|
186
|
+
for (const s of sections) {
|
|
187
|
+
if (s.path)
|
|
188
|
+
paths.push(s.path);
|
|
189
|
+
if (s.children)
|
|
190
|
+
paths.push(...flattenSectionPaths(s.children));
|
|
191
|
+
}
|
|
192
|
+
return paths;
|
|
193
|
+
}
|
|
194
|
+
function resolveSection(docs, section) {
|
|
195
|
+
if (section.href) {
|
|
196
|
+
return { label: section.label, href: section.href, icon: section.icon };
|
|
197
|
+
}
|
|
198
|
+
if (section.children) {
|
|
199
|
+
return {
|
|
200
|
+
label: section.label,
|
|
201
|
+
icon: section.icon,
|
|
202
|
+
children: section.children.map((c) => resolveSection(docs, c)),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const sectionDocs = docs
|
|
206
|
+
.filter((d) => d.slug === section.path || d.slug.startsWith(section.path + "/"));
|
|
207
|
+
const sidebar = buildSidebarTree(sectionDocs);
|
|
208
|
+
return {
|
|
209
|
+
label: section.label,
|
|
210
|
+
path: section.path,
|
|
211
|
+
icon: section.icon,
|
|
212
|
+
sidebar,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
export function buildLocaleData(allDocs, locales) {
|
|
216
|
+
const result = {};
|
|
217
|
+
for (const locale of locales) {
|
|
218
|
+
const prefix = locale.code + "/";
|
|
219
|
+
const docs = allDocs
|
|
220
|
+
.filter((d) => d.slug.startsWith(prefix))
|
|
221
|
+
.map((d) => ({ slug: d.slug.slice(prefix.length), meta: d.meta, headings: d.headings, lastEdited: d.lastEdited }));
|
|
222
|
+
result[locale.code] = { docs };
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
export function tiramisuPlugin(options = {}) {
|
|
227
|
+
const docsDir = options.docsDir ?? "src/docs";
|
|
228
|
+
const config = options.config;
|
|
229
|
+
const groupOrder = config?.sidebar?.groupOrder ?? [];
|
|
230
|
+
let viteRoot = process.cwd();
|
|
231
|
+
function resolveDocsDir() {
|
|
232
|
+
return path.resolve(viteRoot, docsDir);
|
|
233
|
+
}
|
|
234
|
+
function buildVirtualModule() {
|
|
235
|
+
const absDocsDir = resolveDocsDir();
|
|
236
|
+
const files = findTiramisuFiles(absDocsDir);
|
|
237
|
+
const docs = [];
|
|
238
|
+
for (const file of files) {
|
|
239
|
+
const source = fs.readFileSync(file, "utf-8");
|
|
240
|
+
const { meta, headings } = compileWithLocation(source, file);
|
|
241
|
+
const relativePath = path.relative(absDocsDir, file);
|
|
242
|
+
const slug = relativePath.replace(/\.tiramisu$/, "").replace(/\\/g, "/");
|
|
243
|
+
const lastEdited = resolveLastEdited(file, meta);
|
|
244
|
+
docs.push({ slug, meta, headings, lastEdited });
|
|
245
|
+
}
|
|
246
|
+
// Build search index with hierarchical group paths
|
|
247
|
+
function resolveSearchGroup(slug, meta) {
|
|
248
|
+
const segments = slug.split("/");
|
|
249
|
+
if (segments.length === 1)
|
|
250
|
+
return meta.group ?? "Docs";
|
|
251
|
+
return segments
|
|
252
|
+
.slice(0, -1)
|
|
253
|
+
.map(titleCase)
|
|
254
|
+
.join(" > ");
|
|
255
|
+
}
|
|
256
|
+
const searchIndex = docs.map((doc) => {
|
|
257
|
+
const file = path.resolve(absDocsDir, doc.slug + ".tiramisu");
|
|
258
|
+
const source = fs.readFileSync(file, "utf-8");
|
|
259
|
+
const { svelte } = compileWithLocation(source, file);
|
|
260
|
+
const text = extractPlainText(svelte);
|
|
261
|
+
return {
|
|
262
|
+
id: doc.slug,
|
|
263
|
+
title: doc.meta.title ?? doc.slug,
|
|
264
|
+
group: resolveSearchGroup(doc.slug, doc.meta),
|
|
265
|
+
slug: doc.slug,
|
|
266
|
+
headings: doc.headings.map((h) => h.text).join(" "),
|
|
267
|
+
text,
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
// Build dynamic imports — use .tiramisu.svelte suffix so resolveId maps them
|
|
271
|
+
const importsEntries = docs
|
|
272
|
+
.map((doc) => {
|
|
273
|
+
const absPath = path
|
|
274
|
+
.resolve(absDocsDir, doc.slug + ".tiramisu")
|
|
275
|
+
.replace(/\\/g, "/");
|
|
276
|
+
return ` "${doc.slug}": () => import("${absPath}")`;
|
|
277
|
+
})
|
|
278
|
+
.join(",\n");
|
|
279
|
+
if (config?.i18n) {
|
|
280
|
+
const localeData = buildLocaleData(docs, config.i18n.locales);
|
|
281
|
+
const localeBlocks = config.i18n.locales.map((locale) => {
|
|
282
|
+
const localeDocs = localeData[locale.code].docs;
|
|
283
|
+
const localeSections = config.sections
|
|
284
|
+
? buildSectionSidebars(localeDocs, config.sections, config.title)
|
|
285
|
+
: undefined;
|
|
286
|
+
const localeSidebar = buildSidebarTree(localeDocs, groupOrder);
|
|
287
|
+
// Build locale-specific search index
|
|
288
|
+
const localeSearchIndex = localeDocs.map((doc) => {
|
|
289
|
+
const fullSlug = `${locale.code}/${doc.slug}`;
|
|
290
|
+
const file = path.resolve(absDocsDir, fullSlug + ".tiramisu");
|
|
291
|
+
const source = fs.readFileSync(file, "utf-8");
|
|
292
|
+
const { svelte } = compileWithLocation(source, file);
|
|
293
|
+
const text = extractPlainText(svelte);
|
|
294
|
+
return {
|
|
295
|
+
id: doc.slug,
|
|
296
|
+
title: doc.meta.title ?? doc.slug,
|
|
297
|
+
group: resolveSearchGroup(doc.slug, doc.meta),
|
|
298
|
+
slug: doc.slug,
|
|
299
|
+
headings: docs.find(d => d.slug === fullSlug)?.headings?.map(h => h.text).join(" ") ?? "",
|
|
300
|
+
text,
|
|
301
|
+
};
|
|
302
|
+
});
|
|
303
|
+
const localeImports = localeDocs
|
|
304
|
+
.map((doc) => {
|
|
305
|
+
const fullSlug = `${locale.code}/${doc.slug}`;
|
|
306
|
+
const absPath = path.resolve(absDocsDir, fullSlug + ".tiramisu").replace(/\\/g, "/");
|
|
307
|
+
return ` "${doc.slug}": () => import("${absPath}")`;
|
|
308
|
+
})
|
|
309
|
+
.join(",\n");
|
|
310
|
+
return ` "${locale.code}": {
|
|
311
|
+
sections: ${JSON.stringify(localeSections, null, 2)},
|
|
312
|
+
sidebar: ${JSON.stringify(localeSidebar, null, 2)},
|
|
313
|
+
docs: ${JSON.stringify(localeDocs, null, 2)},
|
|
314
|
+
searchIndex: ${JSON.stringify(localeSearchIndex)},
|
|
315
|
+
docImports: {\n${localeImports}\n },
|
|
316
|
+
}`;
|
|
317
|
+
}).join(",\n");
|
|
318
|
+
return [
|
|
319
|
+
`export const locales = {\n${localeBlocks}\n};`,
|
|
320
|
+
`export const defaultLocale = "${config.i18n.defaultLocale}";`,
|
|
321
|
+
`export const sidebar = locales["${config.i18n.defaultLocale}"].sidebar;`,
|
|
322
|
+
`export const sections = locales["${config.i18n.defaultLocale}"].sections;`,
|
|
323
|
+
`export const docs = locales["${config.i18n.defaultLocale}"].docs;`,
|
|
324
|
+
`export const searchIndex = locales["${config.i18n.defaultLocale}"].searchIndex;`,
|
|
325
|
+
`export const docImports = locales["${config.i18n.defaultLocale}"].docImports;`,
|
|
326
|
+
].join("\n\n");
|
|
327
|
+
}
|
|
328
|
+
if (config?.sections) {
|
|
329
|
+
const resolvedSections = buildSectionSidebars(docs, config.sections, config.title);
|
|
330
|
+
return [
|
|
331
|
+
`export const locales = undefined;`,
|
|
332
|
+
`export const defaultLocale = undefined;`,
|
|
333
|
+
`export const sections = ${JSON.stringify(resolvedSections, null, 2)};`,
|
|
334
|
+
`export const sidebar = [];`,
|
|
335
|
+
`export const docs = ${JSON.stringify(docs, null, 2)};`,
|
|
336
|
+
`export const searchIndex = ${JSON.stringify(searchIndex)};`,
|
|
337
|
+
`export const docImports = {\n${importsEntries}\n};`,
|
|
338
|
+
].join("\n\n");
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
const sidebar = buildSidebarTree(docs, groupOrder);
|
|
342
|
+
return [
|
|
343
|
+
`export const locales = undefined;`,
|
|
344
|
+
`export const defaultLocale = undefined;`,
|
|
345
|
+
`export const sections = undefined;`,
|
|
346
|
+
`export const sidebar = ${JSON.stringify(sidebar, null, 2)};`,
|
|
347
|
+
`export const docs = ${JSON.stringify(docs, null, 2)};`,
|
|
348
|
+
`export const searchIndex = ${JSON.stringify(searchIndex)};`,
|
|
349
|
+
`export const docImports = {\n${importsEntries}\n};`,
|
|
350
|
+
].join("\n\n");
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
name: "tiramisu-docs",
|
|
355
|
+
enforce: "pre",
|
|
356
|
+
configResolved(config) {
|
|
357
|
+
viteRoot = config.root;
|
|
358
|
+
},
|
|
359
|
+
resolveId(id) {
|
|
360
|
+
if (id === VIRTUAL_MODULE_ID) {
|
|
361
|
+
return RESOLVED_VIRTUAL_MODULE_ID;
|
|
362
|
+
}
|
|
363
|
+
// Resolve .tiramisu imports to a virtual .svelte ID
|
|
364
|
+
// so the Svelte plugin processes the output
|
|
365
|
+
if (id.endsWith(".tiramisu")) {
|
|
366
|
+
const resolved = path.isAbsolute(id) ? id : path.resolve(viteRoot, id);
|
|
367
|
+
if (fs.existsSync(resolved)) {
|
|
368
|
+
return resolved + ".svelte";
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return undefined;
|
|
372
|
+
},
|
|
373
|
+
async load(id) {
|
|
374
|
+
if (id === RESOLVED_VIRTUAL_MODULE_ID) {
|
|
375
|
+
return buildVirtualModule();
|
|
376
|
+
}
|
|
377
|
+
// Compile .tiramisu files to Svelte component source
|
|
378
|
+
// These come in as .tiramisu.svelte due to resolveId above
|
|
379
|
+
if (id.endsWith(TIRAMISU_SVELTE_SUFFIX)) {
|
|
380
|
+
const tiramisuPath = id.slice(0, -".svelte".length);
|
|
381
|
+
if (!fs.existsSync(tiramisuPath))
|
|
382
|
+
return undefined;
|
|
383
|
+
const source = fs.readFileSync(tiramisuPath, "utf-8");
|
|
384
|
+
const { svelte } = compileWithLocation(source, tiramisuPath);
|
|
385
|
+
return await highlightCodeBlocks(svelte);
|
|
386
|
+
}
|
|
387
|
+
return undefined;
|
|
388
|
+
},
|
|
389
|
+
handleHotUpdate(ctx) {
|
|
390
|
+
if (ctx.file.endsWith(".tiramisu")) {
|
|
391
|
+
// Invalidate the virtual module so sidebar/docs get rebuilt
|
|
392
|
+
const virtualMod = ctx.server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_MODULE_ID);
|
|
393
|
+
if (virtualMod) {
|
|
394
|
+
ctx.server.moduleGraph.invalidateModule(virtualMod);
|
|
395
|
+
}
|
|
396
|
+
// Invalidate the compiled .svelte version too
|
|
397
|
+
const svelteMod = ctx.server.moduleGraph.getModuleById(ctx.file + ".svelte");
|
|
398
|
+
if (svelteMod) {
|
|
399
|
+
ctx.server.moduleGraph.invalidateModule(svelteMod);
|
|
400
|
+
}
|
|
401
|
+
ctx.server.ws.send({ type: "full-reload" });
|
|
402
|
+
return [];
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tiramisu-docs/kit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"svelte": "src/index.ts",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"svelte": "./src/index.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./vite": {
|
|
16
|
+
"types": "./dist/vite.d.ts",
|
|
17
|
+
"default": "./dist/vite.js"
|
|
18
|
+
},
|
|
19
|
+
"./seo": {
|
|
20
|
+
"types": "./dist/seo.d.ts",
|
|
21
|
+
"default": "./dist/seo.js"
|
|
22
|
+
},
|
|
23
|
+
"./mcp": {
|
|
24
|
+
"types": "./dist/mcp.d.ts",
|
|
25
|
+
"default": "./dist/mcp.js"
|
|
26
|
+
},
|
|
27
|
+
"./components/*": {
|
|
28
|
+
"svelte": "./src/lib/components/*",
|
|
29
|
+
"default": "./src/lib/components/*"
|
|
30
|
+
},
|
|
31
|
+
"./styles/*": {
|
|
32
|
+
"default": "./src/lib/styles/*"
|
|
33
|
+
},
|
|
34
|
+
"./types": {
|
|
35
|
+
"types": "./dist/types.d.ts",
|
|
36
|
+
"default": "./dist/types.js"
|
|
37
|
+
},
|
|
38
|
+
"./utils": {
|
|
39
|
+
"types": "./src/lib/utils.ts",
|
|
40
|
+
"default": "./src/lib/utils.ts"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc",
|
|
45
|
+
"prepublishOnly": "bun run build",
|
|
46
|
+
"test": "bun test"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@timeleap/tiramisu": "^1.9.0",
|
|
50
|
+
"@tiramisu-docs/core": "workspace:*",
|
|
51
|
+
"clsx": "^2.1.1",
|
|
52
|
+
"iconify-icon": "^2.3.0",
|
|
53
|
+
"minisearch": "^7.2.0",
|
|
54
|
+
"katex": "^0.16.21",
|
|
55
|
+
"mermaid": "^11.6.0",
|
|
56
|
+
"shiki": "^4.0.1",
|
|
57
|
+
"tailwind-merge": "^3.5.0"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"@sveltejs/kit": "^2.0.0",
|
|
61
|
+
"svelte": "^5.0.0",
|
|
62
|
+
"vite": ">=6.0.0"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@internationalized/date": "^3.10.0",
|
|
66
|
+
"@sveltejs/kit": "^2.53.4",
|
|
67
|
+
"@types/node": "^20.12.11",
|
|
68
|
+
"svelte": "^5.0.0",
|
|
69
|
+
"tailwind-variants": "^3.2.2",
|
|
70
|
+
"tailwindcss": "^4.0.0",
|
|
71
|
+
"typescript": "^5.4.5",
|
|
72
|
+
"vite": "^7.3.1"
|
|
73
|
+
}
|
|
74
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
export interface LogoConfig {
|
|
2
|
+
light: string
|
|
3
|
+
dark: string
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface SectionConfig {
|
|
7
|
+
label: string
|
|
8
|
+
path?: string
|
|
9
|
+
href?: string
|
|
10
|
+
icon?: string
|
|
11
|
+
children?: SectionConfig[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface LocaleConfig {
|
|
15
|
+
code: string
|
|
16
|
+
label: string
|
|
17
|
+
flag?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface I18nConfig {
|
|
21
|
+
defaultLocale: string
|
|
22
|
+
locales: LocaleConfig[]
|
|
23
|
+
fallback?: "default-language" | "404"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ResolvedI18nConfig {
|
|
27
|
+
defaultLocale: string
|
|
28
|
+
locales: LocaleConfig[]
|
|
29
|
+
fallback: "default-language" | "404"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface GitHubConfig {
|
|
33
|
+
repo: string
|
|
34
|
+
branch?: string
|
|
35
|
+
dir?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface FooterSocials {
|
|
39
|
+
github?: string
|
|
40
|
+
x?: string
|
|
41
|
+
discord?: string
|
|
42
|
+
bluesky?: string
|
|
43
|
+
mastodon?: string
|
|
44
|
+
youtube?: string
|
|
45
|
+
linkedin?: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface FooterConfig {
|
|
49
|
+
socials?: FooterSocials
|
|
50
|
+
copyright?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface InstantOgConfig {
|
|
54
|
+
siteId: string
|
|
55
|
+
template?: string
|
|
56
|
+
theme?: "light" | "dark"
|
|
57
|
+
accentColor?: string
|
|
58
|
+
gradientBg?: boolean
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface TiramisuDocsConfig {
|
|
62
|
+
title?: string
|
|
63
|
+
description?: string
|
|
64
|
+
url?: string
|
|
65
|
+
logo?: string | LogoConfig
|
|
66
|
+
nav?: { label: string; href: string }[]
|
|
67
|
+
sidebar?: {
|
|
68
|
+
groupOrder?: string[]
|
|
69
|
+
}
|
|
70
|
+
sections?: SectionConfig[]
|
|
71
|
+
i18n?: I18nConfig
|
|
72
|
+
github?: GitHubConfig
|
|
73
|
+
mcp?: boolean | string
|
|
74
|
+
footer?: FooterConfig
|
|
75
|
+
instantOg?: InstantOgConfig
|
|
76
|
+
theme?: {
|
|
77
|
+
primary?: string
|
|
78
|
+
radius?: string
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface ResolvedConfig {
|
|
83
|
+
title: string
|
|
84
|
+
description: string
|
|
85
|
+
url?: string
|
|
86
|
+
logo: LogoConfig
|
|
87
|
+
nav: { label: string; href: string }[]
|
|
88
|
+
sections?: SectionConfig[]
|
|
89
|
+
i18n?: ResolvedI18nConfig
|
|
90
|
+
github?: GitHubConfig
|
|
91
|
+
mcp?: boolean | string
|
|
92
|
+
footer?: FooterConfig
|
|
93
|
+
instantOg?: InstantOgConfig
|
|
94
|
+
sidebar: { groupOrder: string[] }
|
|
95
|
+
theme: { primary: string; radius: string }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function defineConfig(config: TiramisuDocsConfig): TiramisuDocsConfig {
|
|
99
|
+
return config
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function resolveLogo(logo?: string | LogoConfig): LogoConfig {
|
|
103
|
+
if (!logo) return { light: "", dark: "" }
|
|
104
|
+
if (typeof logo === "string") return { light: logo, dark: logo }
|
|
105
|
+
return logo
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function resolveConfig(config: TiramisuDocsConfig): ResolvedConfig {
|
|
109
|
+
return {
|
|
110
|
+
title: config.title ?? "Documentation",
|
|
111
|
+
description: config.description ?? "",
|
|
112
|
+
url: config.url?.replace(/\/+$/, ""),
|
|
113
|
+
logo: resolveLogo(config.logo),
|
|
114
|
+
nav: config.nav ?? [],
|
|
115
|
+
sections: config.sections,
|
|
116
|
+
github: config.github,
|
|
117
|
+
mcp: config.mcp,
|
|
118
|
+
footer: config.footer,
|
|
119
|
+
instantOg: config.instantOg,
|
|
120
|
+
i18n: config.i18n ? {
|
|
121
|
+
defaultLocale: config.i18n.defaultLocale,
|
|
122
|
+
locales: config.i18n.locales,
|
|
123
|
+
fallback: config.i18n.fallback ?? "default-language",
|
|
124
|
+
} : undefined,
|
|
125
|
+
sidebar: {
|
|
126
|
+
groupOrder: config.sidebar?.groupOrder ?? [],
|
|
127
|
+
},
|
|
128
|
+
theme: {
|
|
129
|
+
primary: config.theme?.primary ?? "hsl(262, 83%, 58%)",
|
|
130
|
+
radius: config.theme?.radius ?? "0.5rem",
|
|
131
|
+
},
|
|
132
|
+
}
|
|
133
|
+
}
|