@mintlify/scraping 4.0.1190 → 4.0.1191
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 +325 -0
- package/bin/sdk/converters/go.js +4 -51
- package/bin/sdk/converters/go.js.map +1 -1
- package/bin/sdk/converters/typedoc.js +62 -5
- package/bin/sdk/converters/typedoc.js.map +1 -1
- package/bin/sdk/readme.d.ts +3 -0
- package/bin/sdk/readme.js +58 -0
- package/bin/sdk/readme.js.map +1 -0
- package/bin/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/sdk/converters/go.ts +5 -44
- package/src/sdk/converters/typedoc.ts +76 -5
- package/src/sdk/readme.ts +51 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mintlify/scraping",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.1191",
|
|
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": "78b82add935dc4b66178be74d41b67a59712367c"
|
|
91
91
|
}
|
package/src/sdk/converters/go.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import fse from 'fs-extra';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ImageReference, LinkReference, Paragraph, Root as MdastRoot } from 'mdast';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { visit } from 'unist-util-visit';
|
|
5
5
|
import { z } from 'zod';
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
truncate,
|
|
23
23
|
type LinkedToken,
|
|
24
24
|
} from '../mdx.js';
|
|
25
|
+
import { parseSdkReadme, unparseableMarkdown } from '../readme.js';
|
|
25
26
|
import type { SdkNavGroup, SdkPage, SdkPageTarget, SdkReference } from '../types.js';
|
|
26
27
|
|
|
27
28
|
const SUPPORTED_SCHEMA_VERSION = 1;
|
|
@@ -29,7 +30,6 @@ const PACKAGE_PAGE = 'package-overview';
|
|
|
29
30
|
const MEMBER_HEADING_DEPTH = 3;
|
|
30
31
|
const MIN_COMMENT_HEADING_DEPTH = 4;
|
|
31
32
|
const MAX_TYPE_ATTR = 80;
|
|
32
|
-
const MIN_README_HEADING_DEPTH = 2;
|
|
33
33
|
const GITHUB_MODULE = /^github\.com\/([^/]+)\/([^/]+)((?:\/[^/]+)*?)(?:\/v(?:[2-9]|[1-9]\d+))?$/;
|
|
34
34
|
|
|
35
35
|
const MAX_TEXT = 1 << 20;
|
|
@@ -450,13 +450,8 @@ class GoRenderer {
|
|
|
450
450
|
// Renders a README the way pkg.go.dev shows it, minus the sentence the page
|
|
451
451
|
// shows as its description (see takeLeadingSentence).
|
|
452
452
|
private readme(markdown: string, synopsis?: string): { intro: string; description?: string } {
|
|
453
|
-
if (
|
|
454
|
-
const tree =
|
|
455
|
-
const first = tree.children[0];
|
|
456
|
-
if (first?.type === 'heading' && first.depth === 1) tree.children.shift();
|
|
457
|
-
visit(tree, 'heading', (node: Heading) => {
|
|
458
|
-
node.depth = Math.max(node.depth, MIN_README_HEADING_DEPTH) as Heading['depth'];
|
|
459
|
-
});
|
|
453
|
+
if (unparseableMarkdown(markdown)) return { intro: codeBlock(markdown, 'text') };
|
|
454
|
+
const tree = parseSdkReadme(markdown);
|
|
460
455
|
const imageDefinitions = new Set<string>();
|
|
461
456
|
const linkDefinitions = new Set<string>();
|
|
462
457
|
visit(tree, 'imageReference', (node: ImageReference) => {
|
|
@@ -777,7 +772,7 @@ class GoRenderer {
|
|
|
777
772
|
}
|
|
778
773
|
|
|
779
774
|
private parseDoc(markdown: string): GoDoc {
|
|
780
|
-
if (
|
|
775
|
+
if (unparseableMarkdown(markdown)) {
|
|
781
776
|
return {
|
|
782
777
|
tree: { type: 'root', children: [{ type: 'code', lang: 'text', value: markdown }] },
|
|
783
778
|
};
|
|
@@ -906,40 +901,6 @@ function dropLeadingSentence(paragraph: Paragraph): void {
|
|
|
906
901
|
paragraph.children = [];
|
|
907
902
|
}
|
|
908
903
|
|
|
909
|
-
// Text past either limit is rendered as a text block instead of Markdown.
|
|
910
|
-
// micromark is quadratic in unclosed brackets (80k `[a](` takes about 45 s)
|
|
911
|
-
// and in balanced link and emphasis markup (40k nested brackets, `[]` pairs,
|
|
912
|
-
// `[a]` references, or `*a` runs take 5 to 36 s), and remark-stringify is
|
|
913
|
-
// quadratic in the characters it escapes (40k `<` or `{` take 0.7 s, minutes
|
|
914
|
-
// at the 1 MiB cap). At 10k either costs well under 2 s. The largest real doc
|
|
915
|
-
// seen (openai-go's README, 60 KB) has about 1,500 such characters and no more
|
|
916
|
-
// than 3 unclosed brackets.
|
|
917
|
-
const MAX_UNCLOSED = 1000;
|
|
918
|
-
const MAX_MARKUP = 10000;
|
|
919
|
-
const MARKUP = new Set(['[', ']', '*', '_', '<', '>', '{', '}', '`', '&']);
|
|
920
|
-
|
|
921
|
-
function unparseable(markdown: string): boolean {
|
|
922
|
-
let square = 0;
|
|
923
|
-
let round = 0;
|
|
924
|
-
let unclosed = 0;
|
|
925
|
-
let markup = 0;
|
|
926
|
-
for (let i = 0; i < markdown.length; i += 1) {
|
|
927
|
-
const char = markdown[i];
|
|
928
|
-
if (char !== undefined && MARKUP.has(char)) markup += 1;
|
|
929
|
-
if (char === '\\') i += 1;
|
|
930
|
-
else if (char === '[') square += 1;
|
|
931
|
-
else if (char === '(') round += 1;
|
|
932
|
-
else if (char === ']') {
|
|
933
|
-
if (square > 0) square -= 1;
|
|
934
|
-
else unclosed += 1;
|
|
935
|
-
} else if (char === ')') {
|
|
936
|
-
if (round > 0) round -= 1;
|
|
937
|
-
else unclosed += 1;
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
return square + round + unclosed > MAX_UNCLOSED || markup > MAX_MARKUP;
|
|
941
|
-
}
|
|
942
|
-
|
|
943
904
|
const MAX_DESCRIPTION = 160;
|
|
944
905
|
|
|
945
906
|
// The text up to and including the first full stop that ends a sentence, or
|
|
@@ -2,8 +2,9 @@ import type { SdkSymbolKind } from '@mintlify/models';
|
|
|
2
2
|
import fse from 'fs-extra';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
|
|
5
|
-
import { escapeMdxText, markdownToMdx } from '../convert.js';
|
|
6
|
-
import { inlineCode, linkedCode, responseField, truncate } from '../mdx.js';
|
|
5
|
+
import { escapeMdxText, markdownToMdx, rewriteMdastLinks, stringifyMdast } from '../convert.js';
|
|
6
|
+
import { inlineCode, linkedCode, responseField, safeHref, truncate } from '../mdx.js';
|
|
7
|
+
import { parseSdkReadme } from '../readme.js';
|
|
7
8
|
import type { SdkNavGroup, SdkPage, SdkPageTarget, SdkReference } from '../types.js';
|
|
8
9
|
|
|
9
10
|
const KIND = {
|
|
@@ -60,6 +61,7 @@ type TypedocNode = {
|
|
|
60
61
|
name: string;
|
|
61
62
|
kind: number;
|
|
62
63
|
comment?: Comment;
|
|
64
|
+
readme?: CommentPart[];
|
|
63
65
|
children?: TypedocNode[];
|
|
64
66
|
groups?: TypedocGroup[];
|
|
65
67
|
categories?: TypedocCategory[];
|
|
@@ -197,7 +199,48 @@ export async function convertTypedoc(sourcePath: string): Promise<SdkReference>
|
|
|
197
199
|
groups.push(...moduleGroups.map((group) => ({ ...group, group: `${prefix}${group.group}` })));
|
|
198
200
|
}
|
|
199
201
|
|
|
200
|
-
|
|
202
|
+
const overviewPages: SdkPage[] = [];
|
|
203
|
+
const overviewGroups: SdkNavGroup[] = [];
|
|
204
|
+
const reservedSlugs = new Set(pages.map((page) => page.slug.toLowerCase()));
|
|
205
|
+
const containers = [project, ...modules.filter((module) => module !== project)];
|
|
206
|
+
const overviews: { node: TypedocNode; slug: string }[] = [];
|
|
207
|
+
for (const container of containers) {
|
|
208
|
+
if (!container.readme?.some((part) => part.text.trim())) continue;
|
|
209
|
+
const isProject = container === project;
|
|
210
|
+
const prefix = isProject ? '' : slugify(container.name).replace(/^\.+$/, 'item');
|
|
211
|
+
const base = path.posix.join(prefix, 'package-overview');
|
|
212
|
+
let slug = base;
|
|
213
|
+
let suffix = 2;
|
|
214
|
+
while (reservedSlugs.has(slug.toLowerCase())) {
|
|
215
|
+
slug = `${base}-${suffix}`;
|
|
216
|
+
suffix += 1;
|
|
217
|
+
}
|
|
218
|
+
reservedSlugs.add(slug.toLowerCase());
|
|
219
|
+
slugById.set(container.id, slug);
|
|
220
|
+
overviews.push({ node: container, slug });
|
|
221
|
+
}
|
|
222
|
+
for (const { node: container, slug } of overviews) {
|
|
223
|
+
const isProject = container === project;
|
|
224
|
+
const declarationSlugs = new Set(
|
|
225
|
+
collectDeclarations(container).map(({ node }) => slugById.get(node.id))
|
|
226
|
+
);
|
|
227
|
+
const indexPages = pages.filter(
|
|
228
|
+
(page) => !page.target?.parent && (isProject || declarationSlugs.has(page.slug))
|
|
229
|
+
);
|
|
230
|
+
const overview = renderer.renderOverview(
|
|
231
|
+
container,
|
|
232
|
+
slug,
|
|
233
|
+
indexPages,
|
|
234
|
+
isProject ? undefined : project.name
|
|
235
|
+
);
|
|
236
|
+
overviewPages.push(overview);
|
|
237
|
+
overviewGroups.push({
|
|
238
|
+
group: isProject ? 'Overview' : `${container.name} Overview`,
|
|
239
|
+
pages: [slug],
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return { pages: [...overviewPages, ...pages], groups: [...overviewGroups, ...groups] };
|
|
201
244
|
}
|
|
202
245
|
|
|
203
246
|
function hasCustomLayout(container: TypedocNode): boolean {
|
|
@@ -296,6 +339,31 @@ function slugify(name: string): string {
|
|
|
296
339
|
class TypedocRenderer {
|
|
297
340
|
constructor(private slugById: Map<number, string>) {}
|
|
298
341
|
|
|
342
|
+
renderOverview(node: TypedocNode, slug: string, pages: SdkPage[], namespace?: string): SdkPage {
|
|
343
|
+
const tree = parseSdkReadme(this.partsMarkdown(node.readme ?? []));
|
|
344
|
+
rewriteMdastLinks(tree, (link) => safeHref(link.url));
|
|
345
|
+
const lines: string[] = [];
|
|
346
|
+
for (const section of SECTIONS) {
|
|
347
|
+
const entries = pages.filter((page) => page.target?.kind === section.symbolKind);
|
|
348
|
+
if (entries.length === 0) continue;
|
|
349
|
+
lines.push(
|
|
350
|
+
`## ${section.group}`,
|
|
351
|
+
entries.map((page) => `- [${inlineCode(page.title)}](/${page.slug})`).join('\n')
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
slug,
|
|
356
|
+
title: node.name,
|
|
357
|
+
intro: stringifyMdast(tree),
|
|
358
|
+
content: lines.join('\n\n'),
|
|
359
|
+
target: {
|
|
360
|
+
kind: 'package',
|
|
361
|
+
name: node.name,
|
|
362
|
+
...(namespace ? { namespace } : {}),
|
|
363
|
+
},
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
299
367
|
renderPage(
|
|
300
368
|
node: TypedocNode,
|
|
301
369
|
section: { dir: string; tag: string; symbolKind: SdkSymbolKind },
|
|
@@ -570,7 +638,11 @@ class TypedocRenderer {
|
|
|
570
638
|
}
|
|
571
639
|
|
|
572
640
|
private parts(parts: CommentPart[]): string {
|
|
573
|
-
|
|
641
|
+
return markdownToMdx(this.partsMarkdown(parts));
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
private partsMarkdown(parts: CommentPart[]): string {
|
|
645
|
+
return parts
|
|
574
646
|
.map((part) => {
|
|
575
647
|
if (part.kind === 'inline-tag') {
|
|
576
648
|
if (typeof part.target === 'number' && this.slugById.has(part.target)) {
|
|
@@ -582,7 +654,6 @@ class TypedocRenderer {
|
|
|
582
654
|
return part.text;
|
|
583
655
|
})
|
|
584
656
|
.join('');
|
|
585
|
-
return markdownToMdx(markdown);
|
|
586
657
|
}
|
|
587
658
|
|
|
588
659
|
private firstSentence(comment?: Comment): string | undefined {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Root as MdastRoot } from 'mdast';
|
|
2
|
+
import { visit } from 'unist-util-visit';
|
|
3
|
+
|
|
4
|
+
import { parseMarkdown } from './convert.js';
|
|
5
|
+
|
|
6
|
+
export function parseSdkReadme(markdown: string): MdastRoot {
|
|
7
|
+
if (unparseableMarkdown(markdown)) {
|
|
8
|
+
return { type: 'root', children: [{ type: 'code', lang: 'text', value: markdown }] };
|
|
9
|
+
}
|
|
10
|
+
const tree = parseMarkdown(markdown);
|
|
11
|
+
const first = tree.children[0];
|
|
12
|
+
if (first?.type === 'heading' && first.depth === 1) tree.children.shift();
|
|
13
|
+
visit(tree, 'heading', (node) => {
|
|
14
|
+
if (node.depth < 2) node.depth = 2;
|
|
15
|
+
});
|
|
16
|
+
return tree;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Text past either limit is rendered as a text block instead of Markdown.
|
|
20
|
+
// micromark is quadratic in unclosed brackets (80k `[a](` takes about 45 s)
|
|
21
|
+
// and in balanced link and emphasis markup (40k nested brackets, `[]` pairs,
|
|
22
|
+
// `[a]` references, or `*a` runs take 5 to 36 s), and remark-stringify is
|
|
23
|
+
// quadratic in the characters it escapes (40k `<` or `{` take 0.7 s, minutes
|
|
24
|
+
// at the 1 MiB cap). At 10k either costs well under 2 s. The largest real doc
|
|
25
|
+
// seen (openai-go's README, 60 KB) has about 1,500 such characters and no more
|
|
26
|
+
// than 3 unclosed brackets.
|
|
27
|
+
const MAX_UNCLOSED = 1000;
|
|
28
|
+
const MAX_MARKUP = 10000;
|
|
29
|
+
const MARKUP = new Set(['[', ']', '*', '_', '<', '>', '{', '}', '`', '&']);
|
|
30
|
+
|
|
31
|
+
export function unparseableMarkdown(markdown: string): boolean {
|
|
32
|
+
let square = 0;
|
|
33
|
+
let round = 0;
|
|
34
|
+
let unclosed = 0;
|
|
35
|
+
let markup = 0;
|
|
36
|
+
for (let i = 0; i < markdown.length; i += 1) {
|
|
37
|
+
const char = markdown[i];
|
|
38
|
+
if (char !== undefined && MARKUP.has(char)) markup += 1;
|
|
39
|
+
if (char === '\\') i += 1;
|
|
40
|
+
else if (char === '[') square += 1;
|
|
41
|
+
else if (char === '(') round += 1;
|
|
42
|
+
else if (char === ']') {
|
|
43
|
+
if (square > 0) square -= 1;
|
|
44
|
+
else unclosed += 1;
|
|
45
|
+
} else if (char === ')') {
|
|
46
|
+
if (round > 0) round -= 1;
|
|
47
|
+
else unclosed += 1;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return square + round + unclosed > MAX_UNCLOSED || markup > MAX_MARKUP;
|
|
51
|
+
}
|