@mintlify/scraping 4.0.1160 → 4.0.1162
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__/findSdkPage.test.ts +106 -0
- package/__test__/sdk-go.test.ts +1671 -0
- package/bin/sdk/convert.d.ts +9 -1
- package/bin/sdk/convert.js +173 -8
- package/bin/sdk/convert.js.map +1 -1
- package/bin/sdk/converters/go.d.ts +2 -0
- package/bin/sdk/converters/go.js +828 -0
- package/bin/sdk/converters/go.js.map +1 -0
- package/bin/sdk/converters/typedoc.js +7 -45
- package/bin/sdk/converters/typedoc.js.map +1 -1
- package/bin/sdk/findSdkPage.d.ts +5 -3
- package/bin/sdk/findSdkPage.js +59 -5
- package/bin/sdk/findSdkPage.js.map +1 -1
- package/bin/sdk/generateSdkReference.js +2 -0
- package/bin/sdk/generateSdkReference.js.map +1 -1
- package/bin/sdk/mdx.d.ts +14 -0
- package/bin/sdk/mdx.js +103 -0
- package/bin/sdk/mdx.js.map +1 -0
- package/bin/sdk/types.d.ts +2 -0
- package/bin/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/src/sdk/convert.ts +196 -9
- package/src/sdk/converters/go.ts +925 -0
- package/src/sdk/converters/typedoc.ts +9 -52
- package/src/sdk/findSdkPage.ts +70 -14
- package/src/sdk/generateSdkReference.ts +2 -0
- package/src/sdk/mdx.ts +123 -0
- package/src/sdk/types.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mintlify/scraping",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.1162",
|
|
4
4
|
"description": "Scrape documentation frameworks to Mintlify docs",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=18.0.0"
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"format:check": "oxfmt --check"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@mintlify/common": "1.0.
|
|
46
|
+
"@mintlify/common": "1.0.1289",
|
|
47
47
|
"fast-xml-parser": "5.7.3",
|
|
48
48
|
"fs-extra": "11.1.1",
|
|
49
49
|
"graphql": "16.11.0",
|
|
@@ -62,9 +62,9 @@
|
|
|
62
62
|
"zod": "3.24.0"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@mintlify/models": "0.0.
|
|
65
|
+
"@mintlify/models": "0.0.377",
|
|
66
66
|
"@mintlify/ts-config": "2.0.2",
|
|
67
|
-
"@mintlify/validation": "0.1.
|
|
67
|
+
"@mintlify/validation": "0.1.938",
|
|
68
68
|
"@tsconfig/recommended": "1.0.2",
|
|
69
69
|
"@types/hast": "3.0.4",
|
|
70
70
|
"@types/mdast": "4.0.4",
|
|
@@ -87,5 +87,5 @@
|
|
|
87
87
|
"optional": true
|
|
88
88
|
}
|
|
89
89
|
},
|
|
90
|
-
"gitHead": "
|
|
90
|
+
"gitHead": "3b19a732462d952e4685c6ba0c60c9be1bb550dd"
|
|
91
91
|
}
|
package/src/sdk/convert.ts
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
import type { Root as HastRoot, Element } from 'hast';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
Definition,
|
|
4
|
+
Heading,
|
|
5
|
+
Html,
|
|
6
|
+
Image,
|
|
7
|
+
ImageReference,
|
|
8
|
+
Link,
|
|
9
|
+
LinkReference,
|
|
10
|
+
Paragraph,
|
|
11
|
+
PhrasingContent,
|
|
12
|
+
Root as MdastRoot,
|
|
13
|
+
Text,
|
|
14
|
+
} from 'mdast';
|
|
3
15
|
import remarkGfm from 'remark-gfm';
|
|
4
16
|
import remarkMdx from 'remark-mdx';
|
|
5
17
|
import remarkParse from 'remark-parse';
|
|
6
18
|
import remarkStringify from 'remark-stringify';
|
|
7
19
|
import { unified, type Plugin } from 'unified';
|
|
20
|
+
import type { Node, Parent } from 'unist';
|
|
8
21
|
import { visit } from 'unist-util-visit';
|
|
9
22
|
|
|
10
23
|
import { convertHeaderLinksToText } from '../components/link.js';
|
|
@@ -80,17 +93,191 @@ export function htmlToMdx(html: string, rewriteLink?: RewriteLink): string {
|
|
|
80
93
|
return hastToMdx(hast, rewriteLink);
|
|
81
94
|
}
|
|
82
95
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
96
|
+
// Raw HTML is dropped and its text kept. An HTML block becomes ordinary
|
|
97
|
+
// paragraphs (one per blank-line-separated run, since <pre> and comments may
|
|
98
|
+
// span blank lines), so its lines are subject to the same ESM escaping as any
|
|
99
|
+
// paragraph and keep their block boundaries; inline HTML becomes text within
|
|
100
|
+
// its paragraph, merged with its neighbours so a paragraph that opens with a
|
|
101
|
+
// tag still starts with its first word.
|
|
102
|
+
export function parseMarkdown(markdown: string, minHeadingDepth?: Heading['depth']): MdastRoot {
|
|
103
|
+
// CommonMark treats \r\n and a bare \r as line endings; normalising them up
|
|
104
|
+
// front means every later split and check only has to know about \n.
|
|
105
|
+
const normalized = markdown.replace(/\r\n?/g, '\n');
|
|
106
|
+
const mdastTree = unified().use(remarkParse).use(remarkGfm).parse(normalized) as MdastRoot;
|
|
107
|
+
visit(mdastTree, 'html', (node: Html, index, parent) => {
|
|
108
|
+
const value = stripTags(node.value);
|
|
109
|
+
if (
|
|
110
|
+
!parent ||
|
|
111
|
+
index === undefined ||
|
|
112
|
+
parent.type === 'paragraph' ||
|
|
113
|
+
parent.type === 'heading'
|
|
114
|
+
) {
|
|
115
|
+
Object.assign(node, { type: 'text', value });
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const paragraphs: Paragraph[] = value
|
|
119
|
+
.split(/\n[ \t]*\n/)
|
|
120
|
+
.map((chunk) => chunk.trim())
|
|
121
|
+
.filter(Boolean)
|
|
122
|
+
.map(
|
|
123
|
+
(chunk): Paragraph => ({
|
|
124
|
+
type: 'paragraph',
|
|
125
|
+
children: [{ type: 'text', value: chunk }],
|
|
126
|
+
data: { htmlBlock: true } as Paragraph['data'],
|
|
127
|
+
})
|
|
128
|
+
);
|
|
129
|
+
parent.children.splice(index, 1, ...paragraphs);
|
|
130
|
+
return index + paragraphs.length;
|
|
88
131
|
});
|
|
89
|
-
|
|
132
|
+
visit(mdastTree, 'paragraph', (node: Paragraph) => {
|
|
133
|
+
node.children = mergeText(node.children);
|
|
134
|
+
});
|
|
135
|
+
if (minHeadingDepth) {
|
|
136
|
+
visit(mdastTree, 'heading', (node: Heading) => {
|
|
137
|
+
node.depth = node.depth < minHeadingDepth ? minHeadingDepth : node.depth;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return mdastTree;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Removes every `<...>` span in one pass: from the first unmatched `<` to the
|
|
144
|
+
// next `>`, the same spans as /<[^>]*>/g, without rescanning to the end of the
|
|
145
|
+
// text for each `<` that never closes.
|
|
146
|
+
function stripTags(value: string): string {
|
|
147
|
+
let out = '';
|
|
148
|
+
let last = 0;
|
|
149
|
+
let open = -1;
|
|
150
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
151
|
+
if (value[i] === '<' && open === -1) open = i;
|
|
152
|
+
else if (value[i] === '>' && open !== -1) {
|
|
153
|
+
out += value.slice(last, open);
|
|
154
|
+
last = i + 1;
|
|
155
|
+
open = -1;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return out + value.slice(last);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Paragraphs made from an HTML block, such as a centred <h1> title or a badge
|
|
162
|
+
// row, which a README description should skip.
|
|
163
|
+
export function isHtmlParagraph(node: Paragraph): boolean {
|
|
164
|
+
return (node.data as { htmlBlock?: boolean } | undefined)?.htmlBlock === true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Joins adjacent text nodes and drops empty ones; the whitespace a removed tag
|
|
168
|
+
// leaves at either end of the paragraph is not content either.
|
|
169
|
+
function mergeText(children: PhrasingContent[]): PhrasingContent[] {
|
|
170
|
+
const merged: PhrasingContent[] = [];
|
|
171
|
+
for (const child of children) {
|
|
172
|
+
const previous = merged[merged.length - 1];
|
|
173
|
+
if (child.type === 'text' && previous?.type === 'text') {
|
|
174
|
+
previous.value += child.value;
|
|
175
|
+
} else if (child.type !== 'text' || child.value !== '') {
|
|
176
|
+
merged.push(child);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const first = merged[0];
|
|
180
|
+
if (first?.type === 'text') first.value = first.value.trimStart();
|
|
181
|
+
const last = merged[merged.length - 1];
|
|
182
|
+
if (last?.type === 'text') last.value = last.value.trimEnd();
|
|
183
|
+
return merged.filter((child) => child.type !== 'text' || child.value !== '');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// The text a reader sees for a node: text and code verbatim, an image's alt,
|
|
187
|
+
// a space for a hard line break, nothing for raw HTML.
|
|
188
|
+
export function plainText(node: Node): string {
|
|
189
|
+
if (node.type === 'text' || node.type === 'inlineCode') return (node as Text).value;
|
|
190
|
+
if (node.type === 'break') return ' ';
|
|
191
|
+
if (node.type === 'image' || node.type === 'imageReference') {
|
|
192
|
+
return (node as Image).alt ?? '';
|
|
193
|
+
}
|
|
194
|
+
if ('children' in node) {
|
|
195
|
+
return (node as Parent).children.map(plainText).join('');
|
|
196
|
+
}
|
|
197
|
+
return '';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// A paragraph with words of its own, as opposed to one made only of badges,
|
|
201
|
+
// images, or links, which is how most READMEs open.
|
|
202
|
+
export function isProseParagraph(node: Paragraph): boolean {
|
|
203
|
+
return node.children.some(
|
|
204
|
+
(child) =>
|
|
205
|
+
(child.type === 'text' && child.value.trim() !== '') ||
|
|
206
|
+
child.type === 'inlineCode' ||
|
|
207
|
+
((child.type === 'emphasis' || child.type === 'strong' || child.type === 'delete') &&
|
|
208
|
+
plainText(child).trim() !== '')
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Rewrites every link, image, and definition URL, or drops the node when the
|
|
213
|
+
// callback returns undefined: a link becomes its text, an image its alt, and a
|
|
214
|
+
// definition disappears along with the references that pointed at it.
|
|
215
|
+
export function rewriteMdastLinks(
|
|
216
|
+
tree: MdastRoot,
|
|
217
|
+
rewriteLink: (node: Link | Image | Definition) => string | undefined
|
|
218
|
+
): void {
|
|
219
|
+
const dropped = new Set<string>();
|
|
220
|
+
visit(tree, ['link', 'image', 'definition'], (node, index, parent) => {
|
|
221
|
+
const target = node as Link | Image | Definition;
|
|
222
|
+
const rewritten = rewriteLink(target);
|
|
223
|
+
if (rewritten !== undefined) {
|
|
224
|
+
target.url = rewritten;
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (target.type === 'definition') dropped.add(target.identifier);
|
|
228
|
+
if (parent && index !== undefined) {
|
|
229
|
+
const replacement =
|
|
230
|
+
target.type === 'link'
|
|
231
|
+
? target.children
|
|
232
|
+
: target.type === 'image'
|
|
233
|
+
? [{ type: 'text' as const, value: target.alt ?? '' }]
|
|
234
|
+
: [];
|
|
235
|
+
parent.children.splice(index, 1, ...replacement);
|
|
236
|
+
return index + replacement.length;
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
if (dropped.size === 0) return;
|
|
240
|
+
visit(tree, ['linkReference', 'imageReference'], (node, index, parent) => {
|
|
241
|
+
const reference = node as LinkReference | ImageReference;
|
|
242
|
+
if (!dropped.has(reference.identifier) || !parent || index === undefined) return;
|
|
243
|
+
const replacement: PhrasingContent[] =
|
|
244
|
+
reference.type === 'linkReference'
|
|
245
|
+
? reference.children
|
|
246
|
+
: [{ type: 'text', value: reference.alt ?? '' }];
|
|
247
|
+
(parent as Parent).children.splice(index, 1, ...replacement);
|
|
248
|
+
return index + replacement.length;
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function markdownToMdx(markdown: string, minHeadingDepth?: Heading['depth']): string {
|
|
253
|
+
return stringifyMdast(parseMarkdown(markdown, minHeadingDepth));
|
|
90
254
|
}
|
|
91
255
|
|
|
92
|
-
|
|
93
|
-
|
|
256
|
+
// MDX reads a paragraph that starts with `import` or `export` as ESM. A
|
|
257
|
+
// character reference for the first letter keeps the text while making it
|
|
258
|
+
// impossible for Markdown text to become JavaScript.
|
|
259
|
+
function escapeEsmParagraphs(tree: MdastRoot): void {
|
|
260
|
+
visit(tree, 'paragraph', (node: Paragraph) => {
|
|
261
|
+
const first = node.children[0];
|
|
262
|
+
if (first?.type !== 'text') return;
|
|
263
|
+
const match = /^(import|export)(?=\s|$)/.exec(first.value);
|
|
264
|
+
if (!match) return;
|
|
265
|
+
first.value = first.value.slice(1);
|
|
266
|
+
node.children.unshift({ type: 'html', value: `&#${match[0].charCodeAt(0)};` });
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Serialises a copy, so a cached tree can be stringified more than once and
|
|
271
|
+
// read for its plain text afterwards without carrying the ESM escapes. Text is
|
|
272
|
+
// merged again first: a dropped link or image leaves its text beside the next
|
|
273
|
+
// node, and `[imp](javascript:x)ort` must be seen as one word, `import`.
|
|
274
|
+
export function stringifyMdast(tree: MdastRoot): string {
|
|
275
|
+
const copy = structuredClone(tree);
|
|
276
|
+
visit(copy, 'paragraph', (node: Paragraph) => {
|
|
277
|
+
node.children = mergeText(node.children);
|
|
278
|
+
});
|
|
279
|
+
escapeEsmParagraphs(copy);
|
|
280
|
+
const result = unified().use(remarkMdx).use(remarkGfm).use(remarkStringify).stringify(copy);
|
|
94
281
|
return String(result)
|
|
95
282
|
.replace(/\n{3,}/g, '\n\n')
|
|
96
283
|
.trim();
|