blodemd 0.0.6 → 0.0.8
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 +2 -2
- package/dev-server/app/favicon.ico +0 -0
- package/dev-server/next-env.d.ts +5 -0
- package/dev-server/package.json +1 -1
- package/dev-server/tsconfig.json +3 -2
- package/dist/cli.mjs +209 -53
- package/dist/cli.mjs.map +1 -1
- package/docs/app/globals.css +4 -2
- package/docs/components/docs/doc-header.tsx +35 -17
- package/docs/components/docs/doc-shell.tsx +46 -22
- package/docs/components/docs/doc-sidebar.tsx +13 -8
- package/docs/components/docs/mobile-nav.tsx +150 -152
- package/docs/components/icons/doc-icon.tsx +96 -0
- package/docs/components/mdx/card.tsx +60 -54
- package/docs/components/mdx/icon.tsx +2 -46
- package/docs/components/mdx/index.tsx +12 -1
- package/docs/components/mdx/tree.tsx +7 -7
- package/docs/components/ui/search.tsx +11 -7
- package/docs/lib/mdx.ts +2 -5
- package/docs/lib/navigation.ts +2 -2
- package/docs/lib/routes.ts +34 -0
- package/docs/lib/shiki.ts +6 -1
- package/package.json +13 -5
- package/packages/@repo/contracts/dist/tenant.d.ts +12 -0
- package/packages/@repo/contracts/dist/tenant.d.ts.map +1 -1
- package/packages/@repo/contracts/dist/tenant.js +20 -0
- package/packages/@repo/contracts/src/tenant.ts +38 -0
- package/packages/@repo/previewing/dist/fs-source.d.ts.map +1 -1
- package/packages/@repo/previewing/dist/fs-source.js +1 -8
- package/packages/@repo/previewing/src/fs-source.ts +1 -8
- package/packages/@repo/validation/src/mintlify-docs-schema.json +1 -1
- package/scripts/prepare-package.mjs +39 -0
- package/packages/@repo/common/src/common.unit.test.ts +0 -55
- package/packages/@repo/previewing/src/index.unit.test.ts +0 -290
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
clamp,
|
|
5
|
-
ensureArray,
|
|
6
|
-
normalizePath,
|
|
7
|
-
safeJsonParse,
|
|
8
|
-
slugify,
|
|
9
|
-
uniq,
|
|
10
|
-
withLeadingSlash,
|
|
11
|
-
withoutLeadingSlash,
|
|
12
|
-
} from "./index";
|
|
13
|
-
|
|
14
|
-
describe("common utilities", () => {
|
|
15
|
-
it("normalizes paths", () => {
|
|
16
|
-
expect(normalizePath("/docs/guide/")).toBe("docs/guide");
|
|
17
|
-
expect(normalizePath("\\docs\\guide\\")).toBe("docs/guide");
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("adds and removes leading slashes", () => {
|
|
21
|
-
expect(withLeadingSlash("docs")).toBe("/docs");
|
|
22
|
-
expect(withLeadingSlash("/docs")).toBe("/docs");
|
|
23
|
-
expect(withLeadingSlash("")).toBe("/");
|
|
24
|
-
expect(withoutLeadingSlash("/docs")).toBe("docs");
|
|
25
|
-
expect(withoutLeadingSlash("docs")).toBe("docs");
|
|
26
|
-
expect(withoutLeadingSlash("")).toBe("");
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("slugifies strings", () => {
|
|
30
|
-
expect(slugify("Hello, blodemd!")).toBe("hello-blodemd");
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("normalizes arrays", () => {
|
|
34
|
-
expect(ensureArray()).toEqual([]);
|
|
35
|
-
expect(ensureArray("one")).toEqual(["one"]);
|
|
36
|
-
expect(ensureArray(["one", "two"])).toEqual(["one", "two"]);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("deduplicates arrays", () => {
|
|
40
|
-
expect(uniq(["a", "a", "b"])).toEqual(["a", "b"]);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("safely parses JSON", () => {
|
|
44
|
-
expect(safeJsonParse<{ ok: boolean }>('{"ok":true}')).toEqual({
|
|
45
|
-
ok: true,
|
|
46
|
-
});
|
|
47
|
-
expect(safeJsonParse("{")).toBeNull();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("clamps values", () => {
|
|
51
|
-
expect(clamp(5, 0, 10)).toBe(5);
|
|
52
|
-
expect(clamp(-1, 0, 10)).toBe(0);
|
|
53
|
-
expect(clamp(11, 0, 10)).toBe(10);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import os from "node:os";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
|
|
5
|
-
import { afterEach, describe, expect, it } from "vitest";
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
buildUtilityArtifacts,
|
|
9
|
-
buildSearchIndex,
|
|
10
|
-
buildContentIndex,
|
|
11
|
-
buildUtilityIndex,
|
|
12
|
-
createFsSource,
|
|
13
|
-
getPrebuiltUtilityLlmPagePath,
|
|
14
|
-
loadPrebuiltUtilityIndex,
|
|
15
|
-
loadSiteConfig,
|
|
16
|
-
PREBUILT_UTILITY_LLMS_FULL_PATH,
|
|
17
|
-
PREBUILT_UTILITY_LLMS_PATH,
|
|
18
|
-
PREBUILT_UTILITY_INDEX_PATH,
|
|
19
|
-
PREBUILT_UTILITY_SITEMAP_PATH,
|
|
20
|
-
UTILITY_DOCS_ROOT_TOKEN,
|
|
21
|
-
serializeUtilityIndex,
|
|
22
|
-
} from "./index";
|
|
23
|
-
|
|
24
|
-
const tempRoots: string[] = [];
|
|
25
|
-
|
|
26
|
-
const createTempContentRoot = async (files: Record<string, string>) => {
|
|
27
|
-
const root = await fs.mkdtemp(path.join(os.tmpdir(), "previewing-"));
|
|
28
|
-
tempRoots.push(root);
|
|
29
|
-
|
|
30
|
-
for (const [relativePath, content] of Object.entries(files)) {
|
|
31
|
-
const absolutePath = path.join(root, relativePath);
|
|
32
|
-
await fs.mkdir(path.dirname(absolutePath), { recursive: true });
|
|
33
|
-
await fs.writeFile(absolutePath, content);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return root;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
afterEach(async () => {
|
|
40
|
-
await Promise.all(
|
|
41
|
-
tempRoots.splice(0).map(async (root) => {
|
|
42
|
-
await fs.rm(root, { force: true, recursive: true });
|
|
43
|
-
})
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe("loadSiteConfig", () => {
|
|
48
|
-
it("loads docs.json as the only supported config file", async () => {
|
|
49
|
-
const root = await createTempContentRoot({
|
|
50
|
-
"config/navigation.json": JSON.stringify(
|
|
51
|
-
{
|
|
52
|
-
groups: [{ group: "Getting Started", pages: ["index"] }],
|
|
53
|
-
},
|
|
54
|
-
null,
|
|
55
|
-
2
|
|
56
|
-
),
|
|
57
|
-
"docs.json": JSON.stringify(
|
|
58
|
-
{
|
|
59
|
-
$schema: "https://mintlify.com/docs.json",
|
|
60
|
-
api: {
|
|
61
|
-
openapi: "openapi.yaml",
|
|
62
|
-
playground: {
|
|
63
|
-
proxy: true,
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
appearance: {
|
|
67
|
-
strict: true,
|
|
68
|
-
},
|
|
69
|
-
colors: {
|
|
70
|
-
primary: "#171717",
|
|
71
|
-
},
|
|
72
|
-
fonts: {
|
|
73
|
-
family: "Inter",
|
|
74
|
-
},
|
|
75
|
-
name: "Example Docs",
|
|
76
|
-
navbar: {
|
|
77
|
-
links: [{ href: "https://example.com", label: "Website" }],
|
|
78
|
-
},
|
|
79
|
-
navigation: {
|
|
80
|
-
$ref: "./config/navigation.json",
|
|
81
|
-
},
|
|
82
|
-
theme: "mint",
|
|
83
|
-
},
|
|
84
|
-
null,
|
|
85
|
-
2
|
|
86
|
-
),
|
|
87
|
-
"index.mdx": "---\ntitle: Welcome\n---\n",
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
const result = await loadSiteConfig(createFsSource(root));
|
|
91
|
-
|
|
92
|
-
expect(result.ok).toBe(true);
|
|
93
|
-
if (!result.ok) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
expect(result.warnings).toEqual([]);
|
|
98
|
-
expect(result.config.name).toBe("Example Docs");
|
|
99
|
-
expect(result.config.collections).toHaveLength(1);
|
|
100
|
-
expect(result.config.collections[0]?.openapi).toBe("openapi.yaml");
|
|
101
|
-
expect(result.config.navigation?.groups).toEqual([
|
|
102
|
-
{ group: "Getting Started", pages: ["index"] },
|
|
103
|
-
]);
|
|
104
|
-
expect(result.config.navigation?.global?.links).toEqual([
|
|
105
|
-
{ href: "https://example.com", label: "Website" },
|
|
106
|
-
]);
|
|
107
|
-
expect(result.config.features?.themeToggle).toBe(false);
|
|
108
|
-
expect(result.config.openapiProxy?.enabled).toBe(true);
|
|
109
|
-
expect(result.config.fonts?.cssUrl).toContain("Inter");
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it("errors when docs.json is missing", async () => {
|
|
113
|
-
const root = await createTempContentRoot({
|
|
114
|
-
"site.json": JSON.stringify({ name: "Old config" }, null, 2),
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
const result = await loadSiteConfig(createFsSource(root));
|
|
118
|
-
|
|
119
|
-
expect(result.ok).toBe(false);
|
|
120
|
-
if (result.ok) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
expect(result.errors).toEqual(["docs.json not found."]);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it("loads the shipped tenant docs roots", async () => {
|
|
128
|
-
const exampleResult = await loadSiteConfig(
|
|
129
|
-
createFsSource(path.resolve(process.cwd(), "apps/docs/content/example"))
|
|
130
|
-
);
|
|
131
|
-
const blodeResult = await loadSiteConfig(
|
|
132
|
-
createFsSource(path.resolve(process.cwd(), "apps/docs/content/blode"))
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
expect(exampleResult.ok).toBe(true);
|
|
136
|
-
expect(blodeResult.ok).toBe(true);
|
|
137
|
-
|
|
138
|
-
if (!exampleResult.ok || !blodeResult.ok) {
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
expect(exampleResult.config.collections[0]?.openapi).toEqual({
|
|
143
|
-
directory: "api",
|
|
144
|
-
source: "openapi.yaml",
|
|
145
|
-
});
|
|
146
|
-
expect(exampleResult.config.openapiProxy?.enabled).toBe(true);
|
|
147
|
-
expect(blodeResult.config.navigation?.groups).toMatchObject([
|
|
148
|
-
{
|
|
149
|
-
group: "Getting Started",
|
|
150
|
-
pages: ["index", "installation"],
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
group: "Customization",
|
|
154
|
-
pages: ["typography", "theming"],
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
group: "Components",
|
|
158
|
-
pages: ["components/breadcrumb"],
|
|
159
|
-
},
|
|
160
|
-
]);
|
|
161
|
-
expect(blodeResult.config.navigation?.groups).toHaveLength(3);
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
describe("buildUtilityIndex", () => {
|
|
166
|
-
it("prebuilds content and OpenAPI utility pages", async () => {
|
|
167
|
-
const root = await createTempContentRoot({
|
|
168
|
-
"docs.json": JSON.stringify(
|
|
169
|
-
{
|
|
170
|
-
$schema: "https://mintlify.com/docs.json",
|
|
171
|
-
api: {
|
|
172
|
-
openapi: "openapi.yaml",
|
|
173
|
-
},
|
|
174
|
-
appearance: {
|
|
175
|
-
strict: true,
|
|
176
|
-
},
|
|
177
|
-
colors: {
|
|
178
|
-
primary: "#171717",
|
|
179
|
-
},
|
|
180
|
-
fonts: {
|
|
181
|
-
family: "Inter",
|
|
182
|
-
},
|
|
183
|
-
name: "Example Docs",
|
|
184
|
-
navbar: {
|
|
185
|
-
links: [{ href: "https://example.com", label: "Website" }],
|
|
186
|
-
},
|
|
187
|
-
navigation: {
|
|
188
|
-
groups: [{ group: "Docs", pages: ["index", "guide"] }],
|
|
189
|
-
},
|
|
190
|
-
theme: "mint",
|
|
191
|
-
},
|
|
192
|
-
null,
|
|
193
|
-
2
|
|
194
|
-
),
|
|
195
|
-
"guide.mdx": "---\ntitle: Guide\n---\n# Guide\n\nShip it.\n",
|
|
196
|
-
"hidden.mdx": "---\ntitle: Hidden\nhidden: true\n---\n# Hidden\n",
|
|
197
|
-
"index.mdx": "---\ntitle: Welcome\n---\n# Welcome\n\nHello there.\n",
|
|
198
|
-
"openapi.yaml": [
|
|
199
|
-
"openapi: 3.0.0",
|
|
200
|
-
"paths:",
|
|
201
|
-
" /projects:",
|
|
202
|
-
" get:",
|
|
203
|
-
" summary: List projects",
|
|
204
|
-
" description: Return every project.",
|
|
205
|
-
" tags:",
|
|
206
|
-
" - Projects",
|
|
207
|
-
" responses:",
|
|
208
|
-
" '200':",
|
|
209
|
-
" description: OK",
|
|
210
|
-
].join("\n"),
|
|
211
|
-
});
|
|
212
|
-
const source = createFsSource(root);
|
|
213
|
-
const configResult = await loadSiteConfig(source);
|
|
214
|
-
|
|
215
|
-
expect(configResult.ok).toBe(true);
|
|
216
|
-
if (!configResult.ok) {
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const contentIndex = await buildContentIndex(source, configResult.config);
|
|
221
|
-
const utilityIndex = await buildUtilityIndex(
|
|
222
|
-
contentIndex,
|
|
223
|
-
source,
|
|
224
|
-
configResult.config
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
expect(utilityIndex.name).toBe("Example Docs");
|
|
228
|
-
expect(utilityIndex.pages.map((page) => page.slug)).toEqual([
|
|
229
|
-
"api/get-projects",
|
|
230
|
-
"guide",
|
|
231
|
-
"index",
|
|
232
|
-
]);
|
|
233
|
-
expect(utilityIndex.pages.find((page) => page.slug === "hidden")).toBe(
|
|
234
|
-
undefined
|
|
235
|
-
);
|
|
236
|
-
expect(
|
|
237
|
-
utilityIndex.pages.find((page) => page.slug === "api/get-projects")
|
|
238
|
-
?.content
|
|
239
|
-
).toContain("Method: GET");
|
|
240
|
-
|
|
241
|
-
expect(
|
|
242
|
-
buildSearchIndex(contentIndex, configResult.config, utilityIndex)
|
|
243
|
-
).toEqual(
|
|
244
|
-
expect.arrayContaining([
|
|
245
|
-
expect.objectContaining({
|
|
246
|
-
path: "api/get-projects",
|
|
247
|
-
title: "List projects",
|
|
248
|
-
}),
|
|
249
|
-
])
|
|
250
|
-
);
|
|
251
|
-
|
|
252
|
-
const artifacts = buildUtilityArtifacts(utilityIndex);
|
|
253
|
-
expect(artifacts.map((artifact) => artifact.path)).toContain(
|
|
254
|
-
PREBUILT_UTILITY_SITEMAP_PATH
|
|
255
|
-
);
|
|
256
|
-
expect(artifacts.map((artifact) => artifact.path)).toContain(
|
|
257
|
-
PREBUILT_UTILITY_LLMS_PATH
|
|
258
|
-
);
|
|
259
|
-
expect(artifacts.map((artifact) => artifact.path)).toContain(
|
|
260
|
-
PREBUILT_UTILITY_LLMS_FULL_PATH
|
|
261
|
-
);
|
|
262
|
-
expect(artifacts.map((artifact) => artifact.path)).toContain(
|
|
263
|
-
getPrebuiltUtilityLlmPagePath("guide")
|
|
264
|
-
);
|
|
265
|
-
expect(
|
|
266
|
-
artifacts.find(
|
|
267
|
-
(artifact) => artifact.path === PREBUILT_UTILITY_SITEMAP_PATH
|
|
268
|
-
)?.content
|
|
269
|
-
).toContain(`${UTILITY_DOCS_ROOT_TOKEN}/api/get-projects`);
|
|
270
|
-
expect(
|
|
271
|
-
artifacts.find(
|
|
272
|
-
(artifact) => artifact.path === PREBUILT_UTILITY_LLMS_FULL_PATH
|
|
273
|
-
)?.content
|
|
274
|
-
).toContain("# Guide (__BLODEMD_DOCS_ROOT__/guide)\n\nShip it.");
|
|
275
|
-
expect(
|
|
276
|
-
artifacts.find(
|
|
277
|
-
(artifact) => artifact.path === getPrebuiltUtilityLlmPagePath("guide")
|
|
278
|
-
)?.content
|
|
279
|
-
).toBe("# Guide\n\nShip it.");
|
|
280
|
-
|
|
281
|
-
await fs.writeFile(
|
|
282
|
-
path.join(root, PREBUILT_UTILITY_INDEX_PATH),
|
|
283
|
-
serializeUtilityIndex(utilityIndex)
|
|
284
|
-
);
|
|
285
|
-
|
|
286
|
-
await expect(loadPrebuiltUtilityIndex(source)).resolves.toEqual(
|
|
287
|
-
utilityIndex
|
|
288
|
-
);
|
|
289
|
-
});
|
|
290
|
-
});
|