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.
Files changed (34) hide show
  1. package/README.md +2 -2
  2. package/dev-server/app/favicon.ico +0 -0
  3. package/dev-server/next-env.d.ts +5 -0
  4. package/dev-server/package.json +1 -1
  5. package/dev-server/tsconfig.json +3 -2
  6. package/dist/cli.mjs +209 -53
  7. package/dist/cli.mjs.map +1 -1
  8. package/docs/app/globals.css +4 -2
  9. package/docs/components/docs/doc-header.tsx +35 -17
  10. package/docs/components/docs/doc-shell.tsx +46 -22
  11. package/docs/components/docs/doc-sidebar.tsx +13 -8
  12. package/docs/components/docs/mobile-nav.tsx +150 -152
  13. package/docs/components/icons/doc-icon.tsx +96 -0
  14. package/docs/components/mdx/card.tsx +60 -54
  15. package/docs/components/mdx/icon.tsx +2 -46
  16. package/docs/components/mdx/index.tsx +12 -1
  17. package/docs/components/mdx/tree.tsx +7 -7
  18. package/docs/components/ui/search.tsx +11 -7
  19. package/docs/lib/mdx.ts +2 -5
  20. package/docs/lib/navigation.ts +2 -2
  21. package/docs/lib/routes.ts +34 -0
  22. package/docs/lib/shiki.ts +6 -1
  23. package/package.json +13 -5
  24. package/packages/@repo/contracts/dist/tenant.d.ts +12 -0
  25. package/packages/@repo/contracts/dist/tenant.d.ts.map +1 -1
  26. package/packages/@repo/contracts/dist/tenant.js +20 -0
  27. package/packages/@repo/contracts/src/tenant.ts +38 -0
  28. package/packages/@repo/previewing/dist/fs-source.d.ts.map +1 -1
  29. package/packages/@repo/previewing/dist/fs-source.js +1 -8
  30. package/packages/@repo/previewing/src/fs-source.ts +1 -8
  31. package/packages/@repo/validation/src/mintlify-docs-schema.json +1 -1
  32. package/scripts/prepare-package.mjs +39 -0
  33. package/packages/@repo/common/src/common.unit.test.ts +0 -55
  34. 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
- });