@aravindc26/velu 0.11.0 → 0.11.3
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/package.json +15 -6
- package/schema/velu.schema.json +1251 -115
- package/src/build.ts +1121 -304
- package/src/cli.ts +90 -26
- package/src/engine/_server.mjs +1684 -277
- package/src/engine/app/(docs)/[...slug]/layout.tsx +371 -0
- package/src/engine/app/(docs)/[...slug]/page.tsx +926 -0
- package/src/engine/app/api/proxy/route.ts +23 -0
- package/src/engine/app/copy-page.css +59 -1
- package/src/engine/app/global.css +3157 -3
- package/src/engine/app/layout.tsx +56 -1
- package/src/engine/app/llms-file/route.ts +87 -0
- package/src/engine/app/llms-full-file/route.ts +62 -0
- package/src/engine/app/md-file/[...slug]/route.ts +409 -0
- package/src/engine/app/page.tsx +45 -0
- package/src/engine/app/robots.txt/route.ts +63 -0
- package/src/engine/app/rss-file/[...slug]/route.ts +169 -0
- package/src/engine/app/sitemap.xml/route.ts +82 -0
- package/src/engine/components/assistant.tsx +16 -5
- package/src/engine/components/changelog-filters.tsx +114 -0
- package/src/engine/components/code-group.tsx +383 -0
- package/src/engine/components/color.tsx +118 -0
- package/src/engine/components/expandable.tsx +77 -0
- package/src/engine/components/icon.tsx +136 -0
- package/src/engine/components/image-zoom-fallback.tsx +147 -0
- package/src/engine/components/image.tsx +111 -0
- package/src/engine/components/manual-api-playground.tsx +154 -0
- package/src/engine/components/mermaid.tsx +142 -0
- package/src/engine/components/openapi-toc-sync.tsx +59 -0
- package/src/engine/components/openapi.tsx +1682 -0
- package/src/engine/components/page-feedback.tsx +153 -0
- package/src/engine/components/product-switcher.tsx +27 -3
- package/src/engine/components/prompt.tsx +90 -0
- package/src/engine/components/providers.tsx +1 -6
- package/src/engine/components/search.tsx +4 -0
- package/src/engine/components/sidebar-links.tsx +13 -15
- package/src/engine/components/synced-tabs.tsx +57 -0
- package/src/engine/components/toc-examples.tsx +110 -0
- package/src/engine/components/view.tsx +344 -0
- package/src/engine/generated/redirects.ts +3 -0
- package/src/engine/lib/changelog.ts +246 -0
- package/src/engine/lib/layout.shared.ts +30 -2
- package/src/engine/lib/llms.ts +444 -0
- package/src/engine/lib/navigation-normalize.mjs +481 -412
- package/src/engine/lib/navigation-normalize.ts +261 -54
- package/src/engine/lib/redirects.ts +194 -0
- package/src/engine/lib/source.ts +107 -4
- package/src/engine/lib/velu.ts +368 -2
- package/src/engine/mdx-components.tsx +648 -0
- package/src/engine/middleware.ts +66 -0
- package/src/engine/public/icons/cursor-dark.svg +12 -0
- package/src/engine/public/icons/cursor-light.svg +12 -0
- package/src/engine/source.config.ts +98 -1
- package/src/engine/src/components/PageTitle.astro +16 -5
- package/src/engine/src/lib/velu.ts +11 -3
- package/src/navigation-normalize.ts +252 -54
- package/src/themes.ts +6 -6
- package/src/validate.ts +119 -6
- package/src/engine/app/(docs)/[[...slug]]/layout.tsx +0 -87
- package/src/engine/app/(docs)/[[...slug]]/page.tsx +0 -146
|
@@ -6,16 +6,21 @@ interface NavLink {
|
|
|
6
6
|
href: string;
|
|
7
7
|
label: string;
|
|
8
8
|
icon?: string;
|
|
9
|
+
iconType?: string;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
interface NavGroup {
|
|
12
13
|
group: string;
|
|
13
14
|
slug: string;
|
|
14
15
|
icon?: string;
|
|
16
|
+
iconType?: string;
|
|
17
|
+
version?: string;
|
|
15
18
|
tag?: string;
|
|
16
19
|
expanded?: boolean;
|
|
17
20
|
description?: string;
|
|
18
21
|
hidden?: boolean;
|
|
22
|
+
openapi?: OpenApiValue;
|
|
23
|
+
asyncapi?: OpenApiValue;
|
|
19
24
|
pages: NavEntry[];
|
|
20
25
|
}
|
|
21
26
|
|
|
@@ -23,12 +28,17 @@ interface NavTab {
|
|
|
23
28
|
tab: string;
|
|
24
29
|
slug: string;
|
|
25
30
|
icon?: string;
|
|
31
|
+
iconType?: string;
|
|
32
|
+
version?: string;
|
|
26
33
|
href?: string;
|
|
34
|
+
openapi?: OpenApiValue;
|
|
35
|
+
asyncapi?: OpenApiValue;
|
|
27
36
|
pages?: Array<string | NavSeparator | NavLink>;
|
|
28
37
|
groups?: NavGroup[];
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
type NavEntry = string | NavGroup | NavSeparator | NavLink;
|
|
41
|
+
type OpenApiValue = string | string[] | { source: string | string[]; directory?: string };
|
|
32
42
|
|
|
33
43
|
function isObject(value: unknown): value is Record<string, unknown> {
|
|
34
44
|
return typeof value === "object" && value !== null;
|
|
@@ -89,10 +99,45 @@ function uniqueSlug(base: string, used: Set<string>): string {
|
|
|
89
99
|
return candidate;
|
|
90
100
|
}
|
|
91
101
|
|
|
102
|
+
function normalizeOpenApiValue(value: unknown): OpenApiValue | undefined {
|
|
103
|
+
if (typeof value === "string") {
|
|
104
|
+
const trimmed = value.trim();
|
|
105
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
106
|
+
}
|
|
107
|
+
if (Array.isArray(value)) {
|
|
108
|
+
const items = value
|
|
109
|
+
.filter((entry): entry is string => typeof entry === "string")
|
|
110
|
+
.map((entry) => entry.trim())
|
|
111
|
+
.filter((entry) => entry.length > 0);
|
|
112
|
+
return items.length > 0 ? items : undefined;
|
|
113
|
+
}
|
|
114
|
+
if (isObject(value)) {
|
|
115
|
+
const source = normalizeOpenApiValue(value.source);
|
|
116
|
+
if (source === undefined || typeof source === "object" && !Array.isArray(source)) return undefined;
|
|
117
|
+
const rawDirectory = typeof value.directory === "string" ? value.directory.trim() : "";
|
|
118
|
+
const directory = rawDirectory ? rawDirectory.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+$/, "") : undefined;
|
|
119
|
+
return directory ? { source, directory } : { source };
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function resolveOpenApiValue(local: unknown, inherited?: OpenApiValue): OpenApiValue | undefined {
|
|
125
|
+
return normalizeOpenApiValue(local) ?? inherited;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function resolveVersionValue(local: unknown, inherited?: string): string | undefined {
|
|
129
|
+
if (typeof local === "string") {
|
|
130
|
+
const trimmed = local.trim();
|
|
131
|
+
if (trimmed.length > 0) return trimmed;
|
|
132
|
+
}
|
|
133
|
+
return inherited;
|
|
134
|
+
}
|
|
135
|
+
|
|
92
136
|
|
|
93
137
|
function normalizeLink(value: NavLink): NavLink {
|
|
94
138
|
const out: NavLink = { href: value.href, label: value.label };
|
|
95
139
|
if (typeof value.icon === "string" && value.icon.length > 0) out.icon = value.icon;
|
|
140
|
+
if (typeof value.iconType === "string" && value.iconType.length > 0) out.iconType = value.iconType;
|
|
96
141
|
return out;
|
|
97
142
|
}
|
|
98
143
|
|
|
@@ -100,7 +145,11 @@ function normalizeAnchorLink(value: Record<string, unknown>): NavLink {
|
|
|
100
145
|
const href = typeof value.href === "string" ? value.href : "#";
|
|
101
146
|
const label = typeof value.anchor === "string" ? value.anchor : "Link";
|
|
102
147
|
const icon = typeof value.icon === "string" ? value.icon : undefined;
|
|
103
|
-
|
|
148
|
+
const iconType = typeof value.iconType === "string" ? value.iconType : undefined;
|
|
149
|
+
const out: NavLink = { href, label };
|
|
150
|
+
if (icon) out.icon = icon;
|
|
151
|
+
if (iconType) out.iconType = iconType;
|
|
152
|
+
return out;
|
|
104
153
|
}
|
|
105
154
|
|
|
106
155
|
function hasContent(value: Record<string, unknown>): boolean {
|
|
@@ -109,7 +158,9 @@ function hasContent(value: Record<string, unknown>): boolean {
|
|
|
109
158
|
(Array.isArray(value.groups) && value.groups.length > 0) ||
|
|
110
159
|
(Array.isArray(value.menu) && value.menu.length > 0) ||
|
|
111
160
|
(Array.isArray(value.tabs) && value.tabs.length > 0) ||
|
|
112
|
-
(Array.isArray(value.dropdowns) && value.dropdowns.length > 0)
|
|
161
|
+
(Array.isArray(value.dropdowns) && value.dropdowns.length > 0) ||
|
|
162
|
+
normalizeOpenApiValue(value.openapi) !== undefined ||
|
|
163
|
+
normalizeOpenApiValue(value.asyncapi) !== undefined;
|
|
113
164
|
|
|
114
165
|
const hasAnchors =
|
|
115
166
|
Array.isArray(value.anchors) &&
|
|
@@ -127,57 +178,105 @@ function hasContent(value: Record<string, unknown>): boolean {
|
|
|
127
178
|
return hasSimple || hasAnchors;
|
|
128
179
|
}
|
|
129
180
|
|
|
130
|
-
function normalizeGroup(
|
|
181
|
+
function normalizeGroup(
|
|
182
|
+
rawGroup: Record<string, unknown>,
|
|
183
|
+
usedGroupSlugs: Set<string>,
|
|
184
|
+
inheritedOpenApi?: OpenApiValue,
|
|
185
|
+
inheritedVersion?: string,
|
|
186
|
+
): NavGroup {
|
|
131
187
|
const groupName = typeof rawGroup.group === "string" ? rawGroup.group : "Group";
|
|
132
188
|
const rawSlug = typeof rawGroup.slug === "string" ? rawGroup.slug : groupName;
|
|
133
189
|
const groupSlug = uniqueSlug(slugify(rawSlug, "group"), usedGroupSlugs);
|
|
190
|
+
const openapi = resolveOpenApiValue(rawGroup.openapi, inheritedOpenApi);
|
|
191
|
+
const asyncapi = normalizeOpenApiValue(rawGroup.asyncapi);
|
|
192
|
+
const version = resolveVersionValue(rawGroup.version, inheritedVersion);
|
|
134
193
|
|
|
135
194
|
const childUsedSlugs = new Set<string>();
|
|
136
|
-
const pages = collectEntries(rawGroup, childUsedSlugs);
|
|
195
|
+
const pages = collectEntries(rawGroup, childUsedSlugs, openapi, version);
|
|
137
196
|
|
|
138
197
|
const out: NavGroup = { group: groupName, slug: groupSlug, pages };
|
|
139
198
|
if (typeof rawGroup.icon === "string") out.icon = rawGroup.icon;
|
|
199
|
+
if (typeof rawGroup.iconType === "string") out.iconType = rawGroup.iconType;
|
|
200
|
+
if (version !== undefined) out.version = version;
|
|
140
201
|
if (typeof rawGroup.tag === "string") out.tag = rawGroup.tag;
|
|
141
202
|
if (typeof rawGroup.expanded === "boolean") out.expanded = rawGroup.expanded;
|
|
142
203
|
if (typeof rawGroup.description === "string") out.description = rawGroup.description;
|
|
143
204
|
if (typeof rawGroup.hidden === "boolean") out.hidden = rawGroup.hidden;
|
|
205
|
+
if (openapi !== undefined) out.openapi = openapi;
|
|
206
|
+
if (asyncapi !== undefined) out.asyncapi = asyncapi;
|
|
144
207
|
return out;
|
|
145
208
|
}
|
|
146
209
|
|
|
147
|
-
function normalizeMenuItem(
|
|
210
|
+
function normalizeMenuItem(
|
|
211
|
+
rawItem: Record<string, unknown>,
|
|
212
|
+
usedGroupSlugs: Set<string>,
|
|
213
|
+
inheritedOpenApi?: OpenApiValue,
|
|
214
|
+
inheritedVersion?: string,
|
|
215
|
+
): NavGroup {
|
|
148
216
|
const name = typeof rawItem.item === "string" ? rawItem.item : "Menu";
|
|
149
217
|
const rawSlug = typeof rawItem.slug === "string" ? rawItem.slug : name;
|
|
150
218
|
const slug = uniqueSlug(slugify(rawSlug, "menu"), usedGroupSlugs);
|
|
219
|
+
const openapi = resolveOpenApiValue(rawItem.openapi, inheritedOpenApi);
|
|
220
|
+
const asyncapi = normalizeOpenApiValue(rawItem.asyncapi);
|
|
221
|
+
const version = resolveVersionValue(rawItem.version, inheritedVersion);
|
|
151
222
|
|
|
152
223
|
const nestedGroupSlugs = new Set<string>();
|
|
153
|
-
const pages = collectEntries(rawItem, nestedGroupSlugs);
|
|
224
|
+
const pages = collectEntries(rawItem, nestedGroupSlugs, openapi, version);
|
|
154
225
|
const out: NavGroup = { group: name, slug, pages };
|
|
155
226
|
if (typeof rawItem.icon === "string") out.icon = rawItem.icon;
|
|
227
|
+
if (typeof rawItem.iconType === "string") out.iconType = rawItem.iconType;
|
|
228
|
+
if (version !== undefined) out.version = version;
|
|
229
|
+
if (openapi !== undefined) out.openapi = openapi;
|
|
230
|
+
if (asyncapi !== undefined) out.asyncapi = asyncapi;
|
|
156
231
|
return out;
|
|
157
232
|
}
|
|
158
233
|
|
|
159
|
-
function normalizeTabAsGroup(
|
|
234
|
+
function normalizeTabAsGroup(
|
|
235
|
+
rawTab: Record<string, unknown>,
|
|
236
|
+
usedGroupSlugs: Set<string>,
|
|
237
|
+
inheritedOpenApi?: OpenApiValue,
|
|
238
|
+
inheritedVersion?: string,
|
|
239
|
+
): NavGroup {
|
|
160
240
|
const tabName = typeof rawTab.tab === "string" ? rawTab.tab : "Tab";
|
|
161
241
|
const rawSlug = typeof rawTab.slug === "string" ? rawTab.slug : tabName;
|
|
162
242
|
const slug = uniqueSlug(slugify(rawSlug, "tab"), usedGroupSlugs);
|
|
243
|
+
const openapi = resolveOpenApiValue(rawTab.openapi, inheritedOpenApi);
|
|
244
|
+
const asyncapi = normalizeOpenApiValue(rawTab.asyncapi);
|
|
245
|
+
const version = resolveVersionValue(rawTab.version, inheritedVersion);
|
|
163
246
|
const nestedGroupSlugs = new Set<string>();
|
|
164
|
-
const pages = collectEntries(rawTab, nestedGroupSlugs);
|
|
247
|
+
const pages = collectEntries(rawTab, nestedGroupSlugs, openapi, version);
|
|
165
248
|
|
|
166
249
|
if (typeof rawTab.href === "string" && rawTab.href.length > 0 && !hasContent(rawTab)) {
|
|
167
|
-
pages.push({
|
|
250
|
+
pages.push({
|
|
251
|
+
href: rawTab.href,
|
|
252
|
+
label: tabName,
|
|
253
|
+
...(typeof rawTab.icon === "string" ? { icon: rawTab.icon } : {}),
|
|
254
|
+
...(typeof rawTab.iconType === "string" ? { iconType: rawTab.iconType } : {}),
|
|
255
|
+
});
|
|
168
256
|
}
|
|
169
257
|
|
|
170
258
|
const out: NavGroup = { group: tabName, slug, pages };
|
|
171
259
|
if (typeof rawTab.icon === "string") out.icon = rawTab.icon;
|
|
260
|
+
if (typeof rawTab.iconType === "string") out.iconType = rawTab.iconType;
|
|
261
|
+
if (version !== undefined) out.version = version;
|
|
262
|
+
if (openapi !== undefined) out.openapi = openapi;
|
|
263
|
+
if (asyncapi !== undefined) out.asyncapi = asyncapi;
|
|
172
264
|
return out;
|
|
173
265
|
}
|
|
174
266
|
|
|
175
|
-
function normalizeDropdownAsGroup(
|
|
267
|
+
function normalizeDropdownAsGroup(
|
|
268
|
+
rawDropdown: Record<string, unknown>,
|
|
269
|
+
usedGroupSlugs: Set<string>,
|
|
270
|
+
inheritedOpenApi?: OpenApiValue,
|
|
271
|
+
inheritedVersion?: string,
|
|
272
|
+
): NavGroup {
|
|
176
273
|
return normalizeTabAsGroup(
|
|
177
274
|
{
|
|
178
275
|
tab: rawDropdown.dropdown,
|
|
179
276
|
slug: rawDropdown.slug,
|
|
180
277
|
icon: rawDropdown.icon,
|
|
278
|
+
iconType: rawDropdown.iconType,
|
|
279
|
+
version: rawDropdown.version,
|
|
181
280
|
href: rawDropdown.href,
|
|
182
281
|
groups: rawDropdown.groups,
|
|
183
282
|
pages: rawDropdown.pages,
|
|
@@ -185,39 +284,60 @@ function normalizeDropdownAsGroup(rawDropdown: Record<string, unknown>, usedGrou
|
|
|
185
284
|
anchors: rawDropdown.anchors,
|
|
186
285
|
dropdowns: rawDropdown.dropdowns,
|
|
187
286
|
tabs: rawDropdown.tabs,
|
|
287
|
+
openapi: rawDropdown.openapi,
|
|
288
|
+
asyncapi: rawDropdown.asyncapi,
|
|
188
289
|
},
|
|
189
|
-
usedGroupSlugs
|
|
290
|
+
usedGroupSlugs,
|
|
291
|
+
inheritedOpenApi,
|
|
292
|
+
inheritedVersion
|
|
190
293
|
);
|
|
191
294
|
}
|
|
192
295
|
|
|
193
|
-
function normalizeAnchorAsGroup(
|
|
296
|
+
function normalizeAnchorAsGroup(
|
|
297
|
+
rawAnchor: Record<string, unknown>,
|
|
298
|
+
usedGroupSlugs: Set<string>,
|
|
299
|
+
inheritedOpenApi?: OpenApiValue,
|
|
300
|
+
inheritedVersion?: string,
|
|
301
|
+
): NavGroup {
|
|
194
302
|
const anchorName = typeof rawAnchor.anchor === "string" ? rawAnchor.anchor : "Anchor";
|
|
195
303
|
const rawSlug = typeof rawAnchor.slug === "string" ? rawAnchor.slug : anchorName;
|
|
196
304
|
const slug = uniqueSlug(slugify(rawSlug, "anchor"), usedGroupSlugs);
|
|
305
|
+
const openapi = resolveOpenApiValue(rawAnchor.openapi, inheritedOpenApi);
|
|
306
|
+
const asyncapi = normalizeOpenApiValue(rawAnchor.asyncapi);
|
|
307
|
+
const version = resolveVersionValue(rawAnchor.version, inheritedVersion);
|
|
197
308
|
const nestedGroupSlugs = new Set<string>();
|
|
198
|
-
const pages = collectEntries(rawAnchor, nestedGroupSlugs);
|
|
309
|
+
const pages = collectEntries(rawAnchor, nestedGroupSlugs, openapi, version);
|
|
199
310
|
|
|
200
311
|
const out: NavGroup = { group: anchorName, slug, pages };
|
|
201
312
|
if (typeof rawAnchor.icon === "string") out.icon = rawAnchor.icon;
|
|
313
|
+
if (typeof rawAnchor.iconType === "string") out.iconType = rawAnchor.iconType;
|
|
314
|
+
if (version !== undefined) out.version = version;
|
|
315
|
+
if (openapi !== undefined) out.openapi = openapi;
|
|
316
|
+
if (asyncapi !== undefined) out.asyncapi = asyncapi;
|
|
202
317
|
return out;
|
|
203
318
|
}
|
|
204
319
|
|
|
205
|
-
function collectEntries(
|
|
320
|
+
function collectEntries(
|
|
321
|
+
rawSection: Record<string, unknown>,
|
|
322
|
+
usedGroupSlugs: Set<string>,
|
|
323
|
+
inheritedOpenApi?: OpenApiValue,
|
|
324
|
+
inheritedVersion?: string,
|
|
325
|
+
): NavEntry[] {
|
|
206
326
|
const entries: NavEntry[] = [];
|
|
207
327
|
|
|
208
328
|
for (const item of Array.isArray(rawSection.menu) ? rawSection.menu : []) {
|
|
209
|
-
if (isMenuItem(item)) entries.push(normalizeMenuItem(item, usedGroupSlugs));
|
|
329
|
+
if (isMenuItem(item)) entries.push(normalizeMenuItem(item, usedGroupSlugs, inheritedOpenApi, inheritedVersion));
|
|
210
330
|
}
|
|
211
331
|
|
|
212
332
|
for (const group of Array.isArray(rawSection.groups) ? rawSection.groups : []) {
|
|
213
|
-
if (isGroupLike(group)) entries.push(normalizeGroup(group, usedGroupSlugs));
|
|
333
|
+
if (isGroupLike(group)) entries.push(normalizeGroup(group, usedGroupSlugs, inheritedOpenApi, inheritedVersion));
|
|
214
334
|
}
|
|
215
335
|
|
|
216
336
|
for (const item of Array.isArray(rawSection.pages) ? rawSection.pages : []) {
|
|
217
337
|
if (typeof item === "string") entries.push(item);
|
|
218
338
|
else if (isSeparator(item)) entries.push({ separator: item.separator });
|
|
219
339
|
else if (isLink(item)) entries.push(normalizeLink(item));
|
|
220
|
-
else if (isGroupLike(item)) entries.push(normalizeGroup(item, usedGroupSlugs));
|
|
340
|
+
else if (isGroupLike(item)) entries.push(normalizeGroup(item, usedGroupSlugs, inheritedOpenApi, inheritedVersion));
|
|
221
341
|
}
|
|
222
342
|
|
|
223
343
|
for (const anchor of Array.isArray(rawSection.anchors) ? rawSection.anchors : []) {
|
|
@@ -225,29 +345,42 @@ function collectEntries(rawSection: Record<string, unknown>, usedGroupSlugs: Set
|
|
|
225
345
|
|
|
226
346
|
const hrefOnly = typeof anchor.href === "string" && anchor.href.length > 0 && !hasContent(anchor);
|
|
227
347
|
if (hrefOnly) entries.push(normalizeAnchorLink(anchor));
|
|
228
|
-
else entries.push(normalizeAnchorAsGroup(anchor, usedGroupSlugs));
|
|
348
|
+
else entries.push(normalizeAnchorAsGroup(anchor, usedGroupSlugs, inheritedOpenApi, inheritedVersion));
|
|
229
349
|
}
|
|
230
350
|
|
|
231
351
|
for (const dropdown of Array.isArray(rawSection.dropdowns) ? rawSection.dropdowns : []) {
|
|
232
|
-
if (isDropdownLike(dropdown)) entries.push(normalizeDropdownAsGroup(dropdown, usedGroupSlugs));
|
|
352
|
+
if (isDropdownLike(dropdown)) entries.push(normalizeDropdownAsGroup(dropdown, usedGroupSlugs, inheritedOpenApi, inheritedVersion));
|
|
233
353
|
}
|
|
234
354
|
|
|
235
355
|
for (const tab of Array.isArray(rawSection.tabs) ? rawSection.tabs : []) {
|
|
236
|
-
if (isTabLike(tab)) entries.push(normalizeTabAsGroup(tab, usedGroupSlugs));
|
|
356
|
+
if (isTabLike(tab)) entries.push(normalizeTabAsGroup(tab, usedGroupSlugs, inheritedOpenApi, inheritedVersion));
|
|
237
357
|
}
|
|
238
358
|
|
|
239
359
|
return entries;
|
|
240
360
|
}
|
|
241
361
|
|
|
242
|
-
function normalizeTab(
|
|
362
|
+
function normalizeTab(
|
|
363
|
+
rawTab: Record<string, unknown>,
|
|
364
|
+
usedTabSlugs: Set<string>,
|
|
365
|
+
slugPrefix: string,
|
|
366
|
+
inheritedOpenApi?: OpenApiValue,
|
|
367
|
+
inheritedVersion?: string,
|
|
368
|
+
): NavTab {
|
|
243
369
|
const tabName = typeof rawTab.tab === "string" ? rawTab.tab : "Tab";
|
|
244
370
|
const rawSlug = typeof rawTab.slug === "string" ? rawTab.slug : tabName;
|
|
245
371
|
const tabSlugPart = slugify(rawSlug, "tab");
|
|
246
372
|
const fullSlug = slugPrefix ? `${slugPrefix}/${tabSlugPart}` : tabSlugPart;
|
|
247
373
|
const slug = uniqueSlug(fullSlug, usedTabSlugs);
|
|
374
|
+
const openapi = resolveOpenApiValue(rawTab.openapi, inheritedOpenApi);
|
|
375
|
+
const asyncapi = normalizeOpenApiValue(rawTab.asyncapi);
|
|
376
|
+
const version = resolveVersionValue(rawTab.version, inheritedVersion);
|
|
248
377
|
|
|
249
378
|
const out: NavTab = { tab: tabName, slug };
|
|
250
379
|
if (typeof rawTab.icon === "string") out.icon = rawTab.icon;
|
|
380
|
+
if (typeof rawTab.iconType === "string") out.iconType = rawTab.iconType;
|
|
381
|
+
if (version !== undefined) out.version = version;
|
|
382
|
+
if (openapi !== undefined) out.openapi = openapi;
|
|
383
|
+
if (asyncapi !== undefined) out.asyncapi = asyncapi;
|
|
251
384
|
|
|
252
385
|
if (typeof rawTab.href === "string" && rawTab.href.length > 0 && !hasContent(rawTab)) {
|
|
253
386
|
out.href = rawTab.href;
|
|
@@ -255,7 +388,7 @@ function normalizeTab(rawTab: Record<string, unknown>, usedTabSlugs: Set<string>
|
|
|
255
388
|
}
|
|
256
389
|
|
|
257
390
|
const groupSlugSet = new Set<string>();
|
|
258
|
-
const entries = collectEntries(rawTab, groupSlugSet);
|
|
391
|
+
const entries = collectEntries(rawTab, groupSlugSet, openapi, version);
|
|
259
392
|
const groups: NavGroup[] = [];
|
|
260
393
|
const pages: Array<string | NavSeparator | NavLink> = [];
|
|
261
394
|
|
|
@@ -269,57 +402,92 @@ function normalizeTab(rawTab: Record<string, unknown>, usedTabSlugs: Set<string>
|
|
|
269
402
|
return out;
|
|
270
403
|
}
|
|
271
404
|
|
|
272
|
-
function normalizeDropdownToTab(
|
|
405
|
+
function normalizeDropdownToTab(
|
|
406
|
+
rawDropdown: Record<string, unknown>,
|
|
407
|
+
usedTabSlugs: Set<string>,
|
|
408
|
+
slugPrefix: string,
|
|
409
|
+
inheritedOpenApi?: OpenApiValue,
|
|
410
|
+
inheritedVersion?: string,
|
|
411
|
+
): NavTab {
|
|
273
412
|
return normalizeTab(
|
|
274
413
|
{
|
|
275
414
|
tab: rawDropdown.dropdown,
|
|
276
415
|
slug: rawDropdown.slug,
|
|
277
416
|
icon: rawDropdown.icon,
|
|
417
|
+
iconType: rawDropdown.iconType,
|
|
418
|
+
version: rawDropdown.version,
|
|
278
419
|
href: rawDropdown.href,
|
|
279
420
|
groups: rawDropdown.groups,
|
|
280
421
|
pages: rawDropdown.pages,
|
|
281
422
|
menu: rawDropdown.menu,
|
|
282
423
|
anchors: rawDropdown.anchors,
|
|
283
424
|
dropdowns: rawDropdown.dropdowns,
|
|
284
|
-
|
|
285
|
-
|
|
425
|
+
tabs: rawDropdown.tabs,
|
|
426
|
+
openapi: rawDropdown.openapi,
|
|
427
|
+
asyncapi: rawDropdown.asyncapi,
|
|
428
|
+
},
|
|
286
429
|
usedTabSlugs,
|
|
287
|
-
slugPrefix
|
|
430
|
+
slugPrefix,
|
|
431
|
+
inheritedOpenApi,
|
|
432
|
+
inheritedVersion
|
|
288
433
|
);
|
|
289
434
|
}
|
|
290
435
|
|
|
291
|
-
function normalizeTabList(
|
|
436
|
+
function normalizeTabList(
|
|
437
|
+
rawTabs: unknown[],
|
|
438
|
+
usedTabSlugs: Set<string>,
|
|
439
|
+
slugPrefix = "",
|
|
440
|
+
inheritedOpenApi?: OpenApiValue,
|
|
441
|
+
inheritedVersion?: string,
|
|
442
|
+
): NavTab[] {
|
|
292
443
|
const tabs: NavTab[] = [];
|
|
293
444
|
for (const item of rawTabs) {
|
|
294
|
-
if (isTabLike(item)) tabs.push(normalizeTab(item, usedTabSlugs, slugPrefix));
|
|
445
|
+
if (isTabLike(item)) tabs.push(normalizeTab(item, usedTabSlugs, slugPrefix, inheritedOpenApi, inheritedVersion));
|
|
295
446
|
}
|
|
296
447
|
return tabs;
|
|
297
448
|
}
|
|
298
449
|
|
|
299
|
-
function normalizeDropdownList(
|
|
450
|
+
function normalizeDropdownList(
|
|
451
|
+
rawDropdowns: unknown[],
|
|
452
|
+
usedTabSlugs: Set<string>,
|
|
453
|
+
slugPrefix = "",
|
|
454
|
+
inheritedOpenApi?: OpenApiValue,
|
|
455
|
+
inheritedVersion?: string,
|
|
456
|
+
): NavTab[] {
|
|
300
457
|
const tabs: NavTab[] = [];
|
|
301
458
|
for (const item of rawDropdowns) {
|
|
302
|
-
if (isDropdownLike(item)) tabs.push(normalizeDropdownToTab(item, usedTabSlugs, slugPrefix));
|
|
459
|
+
if (isDropdownLike(item)) tabs.push(normalizeDropdownToTab(item, usedTabSlugs, slugPrefix, inheritedOpenApi, inheritedVersion));
|
|
303
460
|
}
|
|
304
|
-
return tabs;
|
|
461
|
+
if (inheritedOpenApi === undefined && inheritedVersion === undefined) return tabs;
|
|
462
|
+
return tabs.map((tab) => ({
|
|
463
|
+
...tab,
|
|
464
|
+
openapi: tab.openapi ?? inheritedOpenApi,
|
|
465
|
+
version: tab.version ?? inheritedVersion,
|
|
466
|
+
}));
|
|
305
467
|
}
|
|
306
468
|
|
|
307
|
-
function normalizeNavigationTabs(
|
|
469
|
+
function normalizeNavigationTabs(
|
|
470
|
+
navigation: unknown,
|
|
471
|
+
usedTabSlugs = new Set<string>(),
|
|
472
|
+
inheritedOpenApi?: OpenApiValue,
|
|
473
|
+
): NavTab[] {
|
|
308
474
|
if (!isObject(navigation)) return [];
|
|
309
475
|
|
|
476
|
+
const sectionOpenApi = resolveOpenApiValue(navigation.openapi, inheritedOpenApi);
|
|
310
477
|
const tabs: NavTab[] = [];
|
|
311
478
|
|
|
312
|
-
tabs.push(...normalizeTabList(Array.isArray(navigation.tabs) ? navigation.tabs : [], usedTabSlugs));
|
|
313
|
-
tabs.push(...normalizeDropdownList(Array.isArray(navigation.dropdowns) ? navigation.dropdowns : [], usedTabSlugs));
|
|
479
|
+
tabs.push(...normalizeTabList(Array.isArray(navigation.tabs) ? navigation.tabs : [], usedTabSlugs, "", sectionOpenApi));
|
|
480
|
+
tabs.push(...normalizeDropdownList(Array.isArray(navigation.dropdowns) ? navigation.dropdowns : [], usedTabSlugs, "", sectionOpenApi));
|
|
314
481
|
|
|
315
482
|
if (Array.isArray(navigation.products)) {
|
|
316
483
|
navigation.products.forEach((product, index) => {
|
|
317
484
|
if (!isObject(product)) return;
|
|
318
485
|
const productName = typeof product.product === "string" ? product.product : `Product ${index + 1}`;
|
|
319
486
|
const prefix = slugify(productName, `product-${index + 1}`);
|
|
487
|
+
const productOpenApi = resolveOpenApiValue(product.openapi, sectionOpenApi);
|
|
320
488
|
|
|
321
|
-
tabs.push(...normalizeTabList(Array.isArray(product.tabs) ? product.tabs : [], usedTabSlugs, prefix));
|
|
322
|
-
tabs.push(...normalizeDropdownList(Array.isArray(product.dropdowns) ? product.dropdowns : [], usedTabSlugs, prefix));
|
|
489
|
+
tabs.push(...normalizeTabList(Array.isArray(product.tabs) ? product.tabs : [], usedTabSlugs, prefix, productOpenApi));
|
|
490
|
+
tabs.push(...normalizeDropdownList(Array.isArray(product.dropdowns) ? product.dropdowns : [], usedTabSlugs, prefix, productOpenApi));
|
|
323
491
|
|
|
324
492
|
if (!Array.isArray(product.tabs) && !Array.isArray(product.dropdowns)) {
|
|
325
493
|
if (hasContent(product)) {
|
|
@@ -329,15 +497,19 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
329
497
|
tab: productName,
|
|
330
498
|
slug: prefix,
|
|
331
499
|
icon: product.icon,
|
|
500
|
+
iconType: product.iconType,
|
|
332
501
|
groups: product.groups,
|
|
333
502
|
pages: product.pages,
|
|
334
503
|
menu: product.menu,
|
|
335
504
|
anchors: product.anchors,
|
|
336
505
|
dropdowns: product.dropdowns,
|
|
337
506
|
tabs: product.tabs,
|
|
338
|
-
|
|
507
|
+
openapi: productOpenApi,
|
|
508
|
+
asyncapi: normalizeOpenApiValue(product.asyncapi),
|
|
509
|
+
},
|
|
339
510
|
usedTabSlugs,
|
|
340
|
-
""
|
|
511
|
+
"",
|
|
512
|
+
productOpenApi
|
|
341
513
|
)
|
|
342
514
|
);
|
|
343
515
|
} else if (typeof product.href === "string" && product.href.length > 0) {
|
|
@@ -347,10 +519,14 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
347
519
|
tab: productName,
|
|
348
520
|
slug: prefix,
|
|
349
521
|
icon: product.icon,
|
|
522
|
+
iconType: product.iconType,
|
|
350
523
|
href: product.href,
|
|
351
|
-
|
|
524
|
+
openapi: productOpenApi,
|
|
525
|
+
asyncapi: normalizeOpenApiValue(product.asyncapi),
|
|
526
|
+
},
|
|
352
527
|
usedTabSlugs,
|
|
353
|
-
""
|
|
528
|
+
"",
|
|
529
|
+
productOpenApi
|
|
354
530
|
)
|
|
355
531
|
);
|
|
356
532
|
}
|
|
@@ -363,9 +539,10 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
363
539
|
if (!isObject(version)) return;
|
|
364
540
|
const versionName = typeof version.version === "string" ? version.version : `Version ${index + 1}`;
|
|
365
541
|
const prefix = slugify(versionName, `version-${index + 1}`);
|
|
542
|
+
const versionOpenApi = resolveOpenApiValue(version.openapi, sectionOpenApi);
|
|
366
543
|
|
|
367
|
-
tabs.push(...normalizeTabList(Array.isArray(version.tabs) ? version.tabs : [], usedTabSlugs, prefix));
|
|
368
|
-
tabs.push(...normalizeDropdownList(Array.isArray(version.dropdowns) ? version.dropdowns : [], usedTabSlugs, prefix));
|
|
544
|
+
tabs.push(...normalizeTabList(Array.isArray(version.tabs) ? version.tabs : [], usedTabSlugs, prefix, versionOpenApi));
|
|
545
|
+
tabs.push(...normalizeDropdownList(Array.isArray(version.dropdowns) ? version.dropdowns : [], usedTabSlugs, prefix, versionOpenApi));
|
|
369
546
|
|
|
370
547
|
if (!Array.isArray(version.tabs) && !Array.isArray(version.dropdowns)) {
|
|
371
548
|
if (hasContent(version)) {
|
|
@@ -380,9 +557,12 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
380
557
|
anchors: version.anchors,
|
|
381
558
|
dropdowns: version.dropdowns,
|
|
382
559
|
tabs: version.tabs,
|
|
383
|
-
|
|
560
|
+
openapi: versionOpenApi,
|
|
561
|
+
asyncapi: normalizeOpenApiValue(version.asyncapi),
|
|
562
|
+
},
|
|
384
563
|
usedTabSlugs,
|
|
385
|
-
""
|
|
564
|
+
"",
|
|
565
|
+
versionOpenApi
|
|
386
566
|
)
|
|
387
567
|
);
|
|
388
568
|
} else if (typeof version.href === "string" && version.href.length > 0) {
|
|
@@ -392,9 +572,12 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
392
572
|
tab: versionName,
|
|
393
573
|
slug: prefix,
|
|
394
574
|
href: version.href,
|
|
395
|
-
|
|
575
|
+
openapi: versionOpenApi,
|
|
576
|
+
asyncapi: normalizeOpenApiValue(version.asyncapi),
|
|
577
|
+
},
|
|
396
578
|
usedTabSlugs,
|
|
397
|
-
""
|
|
579
|
+
"",
|
|
580
|
+
versionOpenApi
|
|
398
581
|
)
|
|
399
582
|
);
|
|
400
583
|
}
|
|
@@ -408,9 +591,11 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
408
591
|
|
|
409
592
|
const anchorName = typeof anchor.anchor === "string" ? anchor.anchor : `Anchor ${index + 1}`;
|
|
410
593
|
const prefix = slugify(anchorName, `anchor-${index + 1}`);
|
|
594
|
+
const anchorOpenApi = resolveOpenApiValue(anchor.openapi, sectionOpenApi);
|
|
595
|
+
const anchorVersion = resolveVersionValue(anchor.version);
|
|
411
596
|
|
|
412
597
|
if (Array.isArray(anchor.tabs)) {
|
|
413
|
-
tabs.push(...normalizeTabList(anchor.tabs, usedTabSlugs, prefix));
|
|
598
|
+
tabs.push(...normalizeTabList(anchor.tabs, usedTabSlugs, prefix, anchorOpenApi, anchorVersion));
|
|
414
599
|
} else if (hasContent(anchor)) {
|
|
415
600
|
tabs.push(
|
|
416
601
|
normalizeTab(
|
|
@@ -418,15 +603,21 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
418
603
|
tab: anchorName,
|
|
419
604
|
slug: prefix,
|
|
420
605
|
icon: anchor.icon,
|
|
606
|
+
iconType: anchor.iconType,
|
|
607
|
+
version: anchorVersion,
|
|
421
608
|
groups: anchor.groups,
|
|
422
609
|
pages: anchor.pages,
|
|
423
610
|
menu: anchor.menu,
|
|
424
611
|
anchors: anchor.anchors,
|
|
425
612
|
dropdowns: anchor.dropdowns,
|
|
426
613
|
tabs: anchor.tabs,
|
|
614
|
+
openapi: anchorOpenApi,
|
|
615
|
+
asyncapi: normalizeOpenApiValue(anchor.asyncapi),
|
|
427
616
|
},
|
|
428
617
|
usedTabSlugs,
|
|
429
|
-
""
|
|
618
|
+
"",
|
|
619
|
+
anchorOpenApi,
|
|
620
|
+
anchorVersion
|
|
430
621
|
)
|
|
431
622
|
);
|
|
432
623
|
}
|
|
@@ -449,38 +640,45 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
449
640
|
anchors: navigation.anchors,
|
|
450
641
|
dropdowns: navigation.dropdowns,
|
|
451
642
|
tabs: navigation.tabs,
|
|
643
|
+
openapi: sectionOpenApi,
|
|
644
|
+
asyncapi: normalizeOpenApiValue(navigation.asyncapi),
|
|
452
645
|
},
|
|
453
646
|
usedTabSlugs,
|
|
454
|
-
""
|
|
647
|
+
"",
|
|
648
|
+
sectionOpenApi
|
|
455
649
|
)
|
|
456
650
|
);
|
|
457
651
|
}
|
|
458
652
|
|
|
459
653
|
return tabs;
|
|
460
654
|
}
|
|
461
|
-
|
|
462
|
-
function normalizeLanguageEntries(languages: unknown): Array<Record<string, unknown>> {
|
|
655
|
+
function normalizeLanguageEntries(languages: unknown, inheritedOpenApi?: OpenApiValue): Array<Record<string, unknown>> {
|
|
463
656
|
if (!Array.isArray(languages)) return [];
|
|
464
657
|
|
|
465
658
|
return languages
|
|
466
659
|
.filter(isObject)
|
|
467
660
|
.map((entry) => {
|
|
468
661
|
const usedTabSlugs = new Set<string>();
|
|
662
|
+
const languageOpenApi = resolveOpenApiValue(entry.openapi, inheritedOpenApi);
|
|
469
663
|
return {
|
|
470
664
|
...entry,
|
|
471
|
-
tabs: normalizeNavigationTabs(entry, usedTabSlugs),
|
|
665
|
+
tabs: normalizeNavigationTabs(entry, usedTabSlugs, languageOpenApi),
|
|
472
666
|
};
|
|
473
667
|
});
|
|
474
668
|
}
|
|
475
669
|
|
|
476
670
|
export function normalizeConfigNavigation<T extends { navigation?: unknown }>(config: T): T {
|
|
477
671
|
const nav = isObject(config.navigation) ? config.navigation : {};
|
|
672
|
+
const navOpenApi = resolveOpenApiValue((nav as Record<string, unknown>).openapi);
|
|
673
|
+
const navAsyncApi = normalizeOpenApiValue((nav as Record<string, unknown>).asyncapi);
|
|
478
674
|
return {
|
|
479
675
|
...config,
|
|
480
676
|
navigation: {
|
|
481
677
|
...nav,
|
|
482
|
-
|
|
483
|
-
|
|
678
|
+
...(navOpenApi !== undefined ? { openapi: navOpenApi } : {}),
|
|
679
|
+
...(navAsyncApi !== undefined ? { asyncapi: navAsyncApi } : {}),
|
|
680
|
+
tabs: normalizeNavigationTabs(nav, new Set<string>(), navOpenApi),
|
|
681
|
+
languages: normalizeLanguageEntries(nav.languages, navOpenApi),
|
|
484
682
|
products: Array.isArray(nav.products) ? nav.products : [],
|
|
485
683
|
versions: Array.isArray(nav.versions) ? nav.versions : [],
|
|
486
684
|
},
|
package/src/themes.ts
CHANGED
|
@@ -129,11 +129,11 @@ function generateThemeCss(config: ThemeConfig): string {
|
|
|
129
129
|
if (lightAccent) {
|
|
130
130
|
const palette = deriveAccentPalette(lightAccent);
|
|
131
131
|
lines.push(":root {");
|
|
132
|
-
lines.push(` --color-fd-primary: ${
|
|
133
|
-
lines.push(` --color-fd-primary-foreground: ${textColorFor(
|
|
132
|
+
lines.push(` --color-fd-primary: ${lightAccent};`);
|
|
133
|
+
lines.push(` --color-fd-primary-foreground: ${textColorFor(lightAccent)};`);
|
|
134
134
|
lines.push(` --color-fd-accent: ${palette.light.accentLow};`);
|
|
135
135
|
lines.push(` --color-fd-accent-foreground: ${textColorFor(palette.light.accentLow)};`);
|
|
136
|
-
lines.push(` --color-fd-ring: ${
|
|
136
|
+
lines.push(` --color-fd-ring: ${lightAccent};`);
|
|
137
137
|
lines.push("}");
|
|
138
138
|
lines.push("");
|
|
139
139
|
}
|
|
@@ -141,11 +141,11 @@ function generateThemeCss(config: ThemeConfig): string {
|
|
|
141
141
|
if (darkAccent) {
|
|
142
142
|
const palette = deriveAccentPalette(darkAccent);
|
|
143
143
|
lines.push(".dark {");
|
|
144
|
-
lines.push(` --color-fd-primary: ${
|
|
145
|
-
lines.push(` --color-fd-primary-foreground: ${textColorFor(
|
|
144
|
+
lines.push(` --color-fd-primary: ${darkAccent};`);
|
|
145
|
+
lines.push(` --color-fd-primary-foreground: ${textColorFor(darkAccent)};`);
|
|
146
146
|
lines.push(` --color-fd-accent: ${palette.dark.accentLow};`);
|
|
147
147
|
lines.push(` --color-fd-accent-foreground: ${textColorFor(palette.dark.accentLow)};`);
|
|
148
|
-
lines.push(` --color-fd-ring: ${
|
|
148
|
+
lines.push(` --color-fd-ring: ${darkAccent};`);
|
|
149
149
|
lines.push("}");
|
|
150
150
|
lines.push("");
|
|
151
151
|
}
|