@mintlify/scraping 4.0.1110 → 4.0.1111
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/__test__/sdk-typedoc.test.ts +612 -0
- package/bin/sdk/converters/typedoc.js +144 -14
- package/bin/sdk/converters/typedoc.js.map +1 -1
- package/bin/sdk/types.d.ts +1 -1
- package/bin/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/sdk/converters/typedoc.ts +194 -23
- package/src/sdk/types.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mintlify/scraping",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.1111",
|
|
4
4
|
"description": "Scrape documentation frameworks to Mintlify docs",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=18.0.0"
|
|
@@ -87,5 +87,5 @@
|
|
|
87
87
|
"optional": true
|
|
88
88
|
}
|
|
89
89
|
},
|
|
90
|
-
"gitHead": "
|
|
90
|
+
"gitHead": "acb0c81cdceb361f88c6b238623fbf13a7457edb"
|
|
91
91
|
}
|
|
@@ -2,7 +2,7 @@ import type { SdkSymbolKind } from '@mintlify/models';
|
|
|
2
2
|
import fse from 'fs-extra';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
|
|
5
|
-
import { markdownToMdx } from '../convert.js';
|
|
5
|
+
import { escapeMdxText, markdownToMdx } from '../convert.js';
|
|
6
6
|
import type { SdkNavGroup, SdkPage, SdkPageTarget, SdkReference } from '../types.js';
|
|
7
7
|
|
|
8
8
|
const KIND = {
|
|
@@ -52,12 +52,16 @@ type TypedocType = {
|
|
|
52
52
|
head?: string;
|
|
53
53
|
tail?: [TypedocType, string][];
|
|
54
54
|
};
|
|
55
|
+
type TypedocCategory = { title: string; children?: number[] };
|
|
56
|
+
type TypedocGroup = TypedocCategory & { categories?: TypedocCategory[] };
|
|
55
57
|
type TypedocNode = {
|
|
56
58
|
id: number;
|
|
57
59
|
name: string;
|
|
58
60
|
kind: number;
|
|
59
61
|
comment?: Comment;
|
|
60
62
|
children?: TypedocNode[];
|
|
63
|
+
groups?: TypedocGroup[];
|
|
64
|
+
categories?: TypedocCategory[];
|
|
61
65
|
signatures?: TypedocNode[];
|
|
62
66
|
parameters?: TypedocNode[];
|
|
63
67
|
type?: TypedocType;
|
|
@@ -70,6 +74,11 @@ type TypedocNode = {
|
|
|
70
74
|
inheritedFrom?: { name: string };
|
|
71
75
|
};
|
|
72
76
|
type TypeToken = { text: string; href?: string };
|
|
77
|
+
type MemberSection = {
|
|
78
|
+
title: string;
|
|
79
|
+
members: TypedocNode[];
|
|
80
|
+
categories?: { title: string; members: TypedocNode[] }[];
|
|
81
|
+
};
|
|
73
82
|
|
|
74
83
|
const SECTIONS: {
|
|
75
84
|
kind: number;
|
|
@@ -104,6 +113,32 @@ const SECTIONS: {
|
|
|
104
113
|
},
|
|
105
114
|
];
|
|
106
115
|
|
|
116
|
+
const MAX_PLACEMENTS_PER_PAGE = 8;
|
|
117
|
+
const MAX_DECLARATION_NAME_LENGTH = 512;
|
|
118
|
+
|
|
119
|
+
const MEMBER_KINDS = new Set<number>([KIND.constructor, KIND.property, KIND.accessor, KIND.method]);
|
|
120
|
+
|
|
121
|
+
const DEFAULT_GROUP_TITLE_BY_KIND = new Map<number, string>([
|
|
122
|
+
...SECTIONS.map((section) => [section.kind, section.group] as const),
|
|
123
|
+
[KIND.namespace, 'Namespaces'],
|
|
124
|
+
[KIND.constructor, 'Constructors'],
|
|
125
|
+
[KIND.property, 'Properties'],
|
|
126
|
+
[KIND.accessor, 'Accessors'],
|
|
127
|
+
[KIND.method, 'Methods'],
|
|
128
|
+
]);
|
|
129
|
+
|
|
130
|
+
function hasCustomGroups(container: TypedocNode): boolean {
|
|
131
|
+
const kindById = new Map((container.children ?? []).map((child) => [child.id, child.kind]));
|
|
132
|
+
return (container.groups ?? []).some(
|
|
133
|
+
(group) =>
|
|
134
|
+
group.categories?.length ||
|
|
135
|
+
(group.children ?? []).some((id) => {
|
|
136
|
+
const defaultTitle = DEFAULT_GROUP_TITLE_BY_KIND.get(kindById.get(id) ?? -1);
|
|
137
|
+
return defaultTitle !== undefined && defaultTitle !== group.title;
|
|
138
|
+
})
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
107
142
|
export async function convertTypedoc(sourcePath: string): Promise<SdkReference> {
|
|
108
143
|
const project = (await fse.readJson(sourcePath)) as TypedocNode;
|
|
109
144
|
const modules =
|
|
@@ -132,6 +167,7 @@ export async function convertTypedoc(sourcePath: string): Promise<SdkReference>
|
|
|
132
167
|
for (const module of modules) {
|
|
133
168
|
const prefix = multiModule ? `${module.name} ` : '';
|
|
134
169
|
const declarations = collectDeclarations(module);
|
|
170
|
+
const kindGroups: SdkNavGroup[] = [];
|
|
135
171
|
for (const section of SECTIONS) {
|
|
136
172
|
const entries = declarations
|
|
137
173
|
.filter(({ node }) => node.kind === section.kind && !node.flags?.isPrivate)
|
|
@@ -153,13 +189,76 @@ export async function convertTypedoc(sourcePath: string): Promise<SdkReference>
|
|
|
153
189
|
);
|
|
154
190
|
pages.push(...renderer.renderMemberPages(entry.node, parentSlug, qualifiedName(entry)));
|
|
155
191
|
}
|
|
156
|
-
|
|
192
|
+
kindGroups.push({ group: section.group, pages: sectionPages.map((p) => p.slug) });
|
|
157
193
|
}
|
|
194
|
+
const customGroups = hasCustomLayout(module) ? layoutGroups(module, slugById) : [];
|
|
195
|
+
const moduleGroups = customGroups.length ? customGroups : kindGroups;
|
|
196
|
+
groups.push(...moduleGroups.map((group) => ({ ...group, group: `${prefix}${group.group}` })));
|
|
158
197
|
}
|
|
159
198
|
|
|
160
199
|
return { pages, groups };
|
|
161
200
|
}
|
|
162
201
|
|
|
202
|
+
function hasCustomLayout(container: TypedocNode): boolean {
|
|
203
|
+
if (container.categories?.length) return true;
|
|
204
|
+
if (hasCustomGroups(container)) return true;
|
|
205
|
+
return (container.children ?? []).some(
|
|
206
|
+
(child) => child.kind === KIND.namespace && hasCustomLayout(child)
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function layoutGroups(container: TypedocNode, slugById: Map<number, string>): SdkNavGroup[] {
|
|
211
|
+
return layoutEntries(container, slugById, new Map()).filter(
|
|
212
|
+
(entry): entry is SdkNavGroup => typeof entry !== 'string'
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function layoutEntries(
|
|
217
|
+
container: TypedocNode,
|
|
218
|
+
slugById: Map<number, string>,
|
|
219
|
+
placements: Map<number, number>
|
|
220
|
+
): (string | SdkNavGroup)[] {
|
|
221
|
+
const childById = new Map((container.children ?? []).map((child) => [child.id, child]));
|
|
222
|
+
const entries = (ids: number[] = []): (string | SdkNavGroup)[] =>
|
|
223
|
+
[...new Set(ids)].flatMap((id): (string | SdkNavGroup)[] => {
|
|
224
|
+
const child = childById.get(id);
|
|
225
|
+
if (!child) return [];
|
|
226
|
+
const count = placements.get(id) ?? 0;
|
|
227
|
+
if (child.kind === KIND.namespace) {
|
|
228
|
+
if (count > 0) return [];
|
|
229
|
+
placements.set(id, 1);
|
|
230
|
+
return group(child.name, layoutEntries(child, slugById, placements));
|
|
231
|
+
}
|
|
232
|
+
const slug = slugById.get(id);
|
|
233
|
+
if (!slug) return [];
|
|
234
|
+
if (count >= MAX_PLACEMENTS_PER_PAGE) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`TypeDoc declaration "${truncate(child.name, 64)}" is listed in more than ${MAX_PLACEMENTS_PER_PAGE} navigation groups`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
placements.set(id, count + 1);
|
|
240
|
+
return [slug];
|
|
241
|
+
});
|
|
242
|
+
const group = (title: unknown, pages: (string | SdkNavGroup)[]): SdkNavGroup[] => {
|
|
243
|
+
const label = singleLine(title);
|
|
244
|
+
return label && pages.length ? [{ group: label, pages }] : [];
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
if (container.categories?.length) {
|
|
248
|
+
return container.categories.flatMap((category) =>
|
|
249
|
+
group(category.title, entries(category.children))
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
return (container.groups ?? []).flatMap((entry) =>
|
|
253
|
+
group(
|
|
254
|
+
entry.title,
|
|
255
|
+
entry.categories?.length
|
|
256
|
+
? entry.categories.flatMap((category) => group(category.title, entries(category.children)))
|
|
257
|
+
: entries(entry.children)
|
|
258
|
+
)
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
163
262
|
type Declaration = { node: TypedocNode; namespacePath: string[] };
|
|
164
263
|
|
|
165
264
|
function qualifiedName({ node, namespacePath }: Declaration): string {
|
|
@@ -178,7 +277,18 @@ function collectDeclarations(container: TypedocNode, namespacePath: string[] = [
|
|
|
178
277
|
return declarations;
|
|
179
278
|
}
|
|
180
279
|
|
|
280
|
+
function singleLine(value: unknown): string {
|
|
281
|
+
return String(value ?? '')
|
|
282
|
+
.replace(/\s+/g, ' ')
|
|
283
|
+
.trim();
|
|
284
|
+
}
|
|
285
|
+
|
|
181
286
|
function slugify(name: string): string {
|
|
287
|
+
if (name.length > MAX_DECLARATION_NAME_LENGTH) {
|
|
288
|
+
throw new Error(
|
|
289
|
+
`TypeDoc declaration name "${truncate(name, 64)}" exceeds ${MAX_DECLARATION_NAME_LENGTH} characters`
|
|
290
|
+
);
|
|
291
|
+
}
|
|
182
292
|
return name.replace(/[^a-zA-Z0-9-_.]+/g, '-').replace(/^-+|-+$/g, '') || 'item';
|
|
183
293
|
}
|
|
184
294
|
|
|
@@ -284,6 +394,22 @@ class TypedocRenderer {
|
|
|
284
394
|
}
|
|
285
395
|
|
|
286
396
|
const members = (node.children ?? []).filter((child) => !child.flags?.isPrivate);
|
|
397
|
+
const layout = this.memberLayout(node, members);
|
|
398
|
+
if (layout) {
|
|
399
|
+
for (const section of layout) {
|
|
400
|
+
lines.push(`## ${headingText(section.title)}`);
|
|
401
|
+
if (section.categories) {
|
|
402
|
+
for (const category of section.categories) {
|
|
403
|
+
lines.push(`### ${headingText(category.title)}`);
|
|
404
|
+
lines.push(...this.renderMembers(category.members, 4));
|
|
405
|
+
}
|
|
406
|
+
} else {
|
|
407
|
+
lines.push(...this.renderMembers(section.members, 3));
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return lines;
|
|
411
|
+
}
|
|
412
|
+
|
|
287
413
|
const constructors = members.filter((child) => child.kind === KIND.constructor);
|
|
288
414
|
const properties = members
|
|
289
415
|
.filter((child) => child.kind === KIND.property || child.kind === KIND.accessor)
|
|
@@ -294,36 +420,71 @@ class TypedocRenderer {
|
|
|
294
420
|
|
|
295
421
|
for (const ctor of constructors) {
|
|
296
422
|
lines.push('## Constructor');
|
|
297
|
-
lines.push(...this.
|
|
423
|
+
lines.push(...this.renderMembers([ctor], 3));
|
|
298
424
|
}
|
|
299
|
-
|
|
300
425
|
if (properties.length) {
|
|
301
426
|
lines.push('## Properties');
|
|
302
|
-
|
|
303
|
-
const type = property.kind === KIND.accessor ? property.getSignature?.type : property.type;
|
|
304
|
-
const optional = property.flags?.isOptional === true;
|
|
305
|
-
lines.push(
|
|
306
|
-
this.typeField(
|
|
307
|
-
property.name,
|
|
308
|
-
type,
|
|
309
|
-
optional,
|
|
310
|
-
this.comment(property.comment ?? property.getSignature?.comment)
|
|
311
|
-
)
|
|
312
|
-
);
|
|
313
|
-
}
|
|
427
|
+
lines.push(...this.renderMembers(properties, 3));
|
|
314
428
|
}
|
|
315
|
-
|
|
316
429
|
if (methods.length) {
|
|
317
430
|
lines.push('## Methods');
|
|
318
|
-
|
|
319
|
-
lines.push(`### ${method.name}()`);
|
|
320
|
-
lines.push(...this.renderSignatures(method, 4));
|
|
321
|
-
}
|
|
431
|
+
lines.push(...this.renderMembers(methods, 3));
|
|
322
432
|
}
|
|
323
433
|
|
|
324
434
|
return lines;
|
|
325
435
|
}
|
|
326
436
|
|
|
437
|
+
private memberLayout(node: TypedocNode, members: TypedocNode[]): MemberSection[] | undefined {
|
|
438
|
+
const memberById = new Map(members.map((member) => [member.id, member]));
|
|
439
|
+
const placed = new Set<number>();
|
|
440
|
+
const resolve = (ids: number[] = []) =>
|
|
441
|
+
ids.flatMap((id) => {
|
|
442
|
+
const member = memberById.get(id);
|
|
443
|
+
if (!member || !MEMBER_KINDS.has(member.kind) || placed.has(id)) return [];
|
|
444
|
+
placed.add(id);
|
|
445
|
+
return [member];
|
|
446
|
+
});
|
|
447
|
+
const sections = (categories: TypedocCategory[]) =>
|
|
448
|
+
categories
|
|
449
|
+
.map((category) => ({ title: category.title, members: resolve(category.children) }))
|
|
450
|
+
.filter((section) => section.members.length > 0);
|
|
451
|
+
|
|
452
|
+
if (node.categories?.length) return sections(node.categories);
|
|
453
|
+
if (!hasCustomGroups(node)) return undefined;
|
|
454
|
+
return (node.groups ?? [])
|
|
455
|
+
.map((group): MemberSection => {
|
|
456
|
+
if (!group.categories?.length)
|
|
457
|
+
return { title: group.title, members: resolve(group.children) };
|
|
458
|
+
return { title: group.title, members: [], categories: sections(group.categories) };
|
|
459
|
+
})
|
|
460
|
+
.filter((section) => section.members.length > 0 || section.categories?.length);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
private renderMembers(members: TypedocNode[], depth: number): string[] {
|
|
464
|
+
const lines: string[] = [];
|
|
465
|
+
for (const ctor of members.filter((member) => member.kind === KIND.constructor)) {
|
|
466
|
+
lines.push(...this.renderSignatures(ctor, depth));
|
|
467
|
+
}
|
|
468
|
+
for (const property of members.filter(
|
|
469
|
+
(member) => member.kind === KIND.property || member.kind === KIND.accessor
|
|
470
|
+
)) {
|
|
471
|
+
const type = property.kind === KIND.accessor ? property.getSignature?.type : property.type;
|
|
472
|
+
lines.push(
|
|
473
|
+
this.typeField(
|
|
474
|
+
property.name,
|
|
475
|
+
type,
|
|
476
|
+
property.flags?.isOptional === true,
|
|
477
|
+
this.comment(property.comment ?? property.getSignature?.comment)
|
|
478
|
+
)
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
for (const method of members.filter((member) => member.kind === KIND.method)) {
|
|
482
|
+
lines.push(`${'#'.repeat(depth)} ${headingText(method.name)}()`);
|
|
483
|
+
lines.push(...this.renderSignatures(method, depth + 1));
|
|
484
|
+
}
|
|
485
|
+
return lines;
|
|
486
|
+
}
|
|
487
|
+
|
|
327
488
|
private renderEnum(node: TypedocNode): string[] {
|
|
328
489
|
const lines: string[] = ['## Members'];
|
|
329
490
|
for (const member of node.children ?? []) {
|
|
@@ -352,7 +513,7 @@ class TypedocRenderer {
|
|
|
352
513
|
const comment = this.comment(signature.comment);
|
|
353
514
|
if (comment) lines.push(comment);
|
|
354
515
|
if (signature.parameters?.length) {
|
|
355
|
-
lines.push(
|
|
516
|
+
lines.push(detailHeading('Parameters', depth));
|
|
356
517
|
for (const param of signature.parameters) {
|
|
357
518
|
lines.push(
|
|
358
519
|
this.typeField(
|
|
@@ -367,7 +528,7 @@ class TypedocRenderer {
|
|
|
367
528
|
const returns = signature.comment?.blockTags?.find((tag) => tag.tag === '@returns');
|
|
368
529
|
const returnType = this.type(signature.type);
|
|
369
530
|
if (returnType && returnType !== 'void') {
|
|
370
|
-
lines.push(
|
|
531
|
+
lines.push(detailHeading('Returns', depth));
|
|
371
532
|
const linked = this.linkedType(signature.type) ?? inlineCode(returnType);
|
|
372
533
|
lines.push(`${linked}${returns ? `\n\n${this.parts(returns.content)}` : ''}`);
|
|
373
534
|
}
|
|
@@ -555,6 +716,16 @@ class TypedocRenderer {
|
|
|
555
716
|
}
|
|
556
717
|
}
|
|
557
718
|
|
|
719
|
+
const MAX_HEADING_DEPTH = 4;
|
|
720
|
+
|
|
721
|
+
function headingText(title: unknown): string {
|
|
722
|
+
return escapeMdxText(singleLine(title));
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function detailHeading(title: string, depth: number): string {
|
|
726
|
+
return depth <= MAX_HEADING_DEPTH ? `${'#'.repeat(depth)} ${title}` : `**${title}**`;
|
|
727
|
+
}
|
|
728
|
+
|
|
558
729
|
function indent(text: string): string {
|
|
559
730
|
return text
|
|
560
731
|
.split('\n')
|