@cosmocoder/mcp-web-docs 2.0.13 → 2.0.14
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/build/config.d.ts +0 -1
- package/build/config.js +0 -5
- package/build/config.js.map +1 -1
- package/build/config.test.js +1 -25
- package/build/config.test.js.map +1 -1
- package/build/crawler/base.test.js +1 -0
- package/build/crawler/base.test.js.map +1 -1
- package/build/crawler/content-extractor-types.d.ts +3 -0
- package/build/crawler/crawlee-crawler.js +14 -9
- package/build/crawler/crawlee-crawler.js.map +1 -1
- package/build/crawler/crawlee-crawler.test.js +58 -7
- package/build/crawler/crawlee-crawler.test.js.map +1 -1
- package/build/crawler/default-extractor.js +1 -0
- package/build/crawler/default-extractor.js.map +1 -1
- package/build/crawler/default-extractor.test.js +1 -0
- package/build/crawler/default-extractor.test.js.map +1 -1
- package/build/crawler/docs-crawler.test.js +18 -10
- package/build/crawler/docs-crawler.test.js.map +1 -1
- package/build/crawler/github-pages-extractor.js +2 -0
- package/build/crawler/github-pages-extractor.js.map +1 -1
- package/build/crawler/github-pages-extractor.test.js +2 -0
- package/build/crawler/github-pages-extractor.test.js.map +1 -1
- package/build/crawler/github.js +1 -0
- package/build/crawler/github.js.map +1 -1
- package/build/crawler/github.test.js +1 -0
- package/build/crawler/github.test.js.map +1 -1
- package/build/crawler/queue-manager.test.js +7 -0
- package/build/crawler/queue-manager.test.js.map +1 -1
- package/build/crawler/storybook-extractor.js +3 -0
- package/build/crawler/storybook-extractor.js.map +1 -1
- package/build/crawler/storybook-extractor.test.js +2 -0
- package/build/crawler/storybook-extractor.test.js.map +1 -1
- package/build/index.test.js +2 -2
- package/build/index.test.js.map +1 -1
- package/build/processor/markdown.d.ts +15 -6
- package/build/processor/markdown.js +7 -30
- package/build/processor/markdown.js.map +1 -1
- package/build/processor/markdown.test.js +39 -6
- package/build/processor/markdown.test.js.map +1 -1
- package/build/processor/processor.js +8 -26
- package/build/processor/processor.js.map +1 -1
- package/build/processor/processor.test.js +118 -172
- package/build/processor/processor.test.js.map +1 -1
- package/build/types.d.ts +2 -0
- package/package.json +2 -3
- package/build/processor/content.d.ts +0 -16
- package/build/processor/content.js +0 -287
- package/build/processor/content.js.map +0 -1
- package/build/processor/content.test.d.ts +0 -1
- package/build/processor/content.test.js +0 -369
- package/build/processor/content.test.js.map +0 -1
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
import { Readability } from '@mozilla/readability';
|
|
2
|
-
import { JSDOM } from 'jsdom';
|
|
3
|
-
import { logger } from '../util/logger.js';
|
|
4
|
-
function cleanText(text) {
|
|
5
|
-
return text
|
|
6
|
-
.replace(/\s+/g, ' ')
|
|
7
|
-
.replace(/\n\s*\n/g, '\n\n')
|
|
8
|
-
.trim();
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Extract text content from HTML while preserving code blocks with markdown fences.
|
|
12
|
-
* This ensures code examples aren't lost when using Readability fallback.
|
|
13
|
-
*/
|
|
14
|
-
function extractTextWithCodeBlocks(html) {
|
|
15
|
-
const dom = new JSDOM(html);
|
|
16
|
-
const doc = dom.window.document;
|
|
17
|
-
// First, wrap all code blocks with markdown fences
|
|
18
|
-
const codeElements = doc.querySelectorAll('pre, code');
|
|
19
|
-
codeElements.forEach((el) => {
|
|
20
|
-
// Skip inline <code> inside <pre> (already handled by parent)
|
|
21
|
-
if (el.tagName === 'CODE' && el.parentElement?.tagName === 'PRE') {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
const code = el.textContent?.trim();
|
|
25
|
-
if (code && code.length > 0) {
|
|
26
|
-
// Replace the element's content with fenced code
|
|
27
|
-
const isBlock = el.tagName === 'PRE' || code.includes('\n');
|
|
28
|
-
if (isBlock) {
|
|
29
|
-
el.textContent = `\n\`\`\`\n${code}\n\`\`\`\n`;
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
el.textContent = `\`${code}\``;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
// Now get the text content which includes our fenced markers
|
|
37
|
-
return doc.body?.textContent || '';
|
|
38
|
-
}
|
|
39
|
-
function findMainContent(doc) {
|
|
40
|
-
const selectors = [
|
|
41
|
-
// Storybook specific selectors
|
|
42
|
-
'[class*="story-content"]',
|
|
43
|
-
'[class*="storybook-"]',
|
|
44
|
-
'[class*="docs-content"]',
|
|
45
|
-
'[class*="sbdocs-"]',
|
|
46
|
-
'[class*="docblock-"]',
|
|
47
|
-
// Jimdo UI specific selectors
|
|
48
|
-
'[class*="docs-"]',
|
|
49
|
-
'[class*="documentation"]',
|
|
50
|
-
'[class*="content"]',
|
|
51
|
-
'[class*="main"]',
|
|
52
|
-
// Common documentation selectors
|
|
53
|
-
'main',
|
|
54
|
-
'[role="main"]',
|
|
55
|
-
'#root',
|
|
56
|
-
'#app',
|
|
57
|
-
'#__next',
|
|
58
|
-
'#storybook-root',
|
|
59
|
-
'.documentation',
|
|
60
|
-
'.docs-content',
|
|
61
|
-
'.markdown-body',
|
|
62
|
-
'article',
|
|
63
|
-
'.article',
|
|
64
|
-
'.content',
|
|
65
|
-
'.page-content',
|
|
66
|
-
'.docusaurus-content',
|
|
67
|
-
'.vuepress-content',
|
|
68
|
-
'.gatsby-content',
|
|
69
|
-
'.mdx-content',
|
|
70
|
-
'.nextra-content',
|
|
71
|
-
'.nuxt-content',
|
|
72
|
-
];
|
|
73
|
-
// Try each selector
|
|
74
|
-
for (const selector of selectors) {
|
|
75
|
-
const elements = Array.from(doc.querySelectorAll(selector));
|
|
76
|
-
if (elements.length > 0) {
|
|
77
|
-
// If multiple elements found, return the one with most content
|
|
78
|
-
return elements.reduce((best, current) => {
|
|
79
|
-
const bestLength = best.textContent?.length || 0;
|
|
80
|
-
const currentLength = current.textContent?.length || 0;
|
|
81
|
-
return currentLength > bestLength ? current : best;
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
// Fallback: try to find the element with the most content
|
|
86
|
-
const candidates = Array.from(doc.body.children);
|
|
87
|
-
if (candidates.length === 0) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
return candidates.reduce((best, current) => {
|
|
91
|
-
const bestLength = best.textContent?.length || 0;
|
|
92
|
-
const currentLength = current.textContent?.length || 0;
|
|
93
|
-
return currentLength > bestLength ? current : best;
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
function getAllTextContent(element) {
|
|
97
|
-
let content = '';
|
|
98
|
-
// Handle code blocks specially
|
|
99
|
-
if (element.tagName === 'PRE' || element.classList.contains('code')) {
|
|
100
|
-
const code = element.textContent?.trim();
|
|
101
|
-
if (code) {
|
|
102
|
-
return '\n```\n' + code + '\n```\n';
|
|
103
|
-
}
|
|
104
|
-
return '';
|
|
105
|
-
}
|
|
106
|
-
// Handle lists specially
|
|
107
|
-
if (element.tagName === 'UL' || element.tagName === 'OL') {
|
|
108
|
-
const items = Array.from(element.querySelectorAll('li'))
|
|
109
|
-
.map((li) => '- ' + li.textContent?.trim())
|
|
110
|
-
.filter(Boolean)
|
|
111
|
-
.join('\n');
|
|
112
|
-
if (items) {
|
|
113
|
-
return '\n' + items + '\n';
|
|
114
|
-
}
|
|
115
|
-
return '';
|
|
116
|
-
}
|
|
117
|
-
// Handle tables specially
|
|
118
|
-
if (element.tagName === 'TABLE') {
|
|
119
|
-
const rows = Array.from(element.querySelectorAll('tr'))
|
|
120
|
-
.map((tr) => Array.from(tr.querySelectorAll('td, th'))
|
|
121
|
-
.map((cell) => cell.textContent?.trim() || '')
|
|
122
|
-
.join(' | '))
|
|
123
|
-
.filter(Boolean)
|
|
124
|
-
.join('\n');
|
|
125
|
-
if (rows) {
|
|
126
|
-
return '\n' + rows + '\n';
|
|
127
|
-
}
|
|
128
|
-
return '';
|
|
129
|
-
}
|
|
130
|
-
// Skip unwanted elements
|
|
131
|
-
if (['SCRIPT', 'STYLE', 'NAV', 'HEADER', 'FOOTER'].includes(element.tagName)) {
|
|
132
|
-
return '';
|
|
133
|
-
}
|
|
134
|
-
// Get text content of this element
|
|
135
|
-
const text = element.textContent?.trim();
|
|
136
|
-
if (text) {
|
|
137
|
-
content += text + '\n';
|
|
138
|
-
}
|
|
139
|
-
return content;
|
|
140
|
-
}
|
|
141
|
-
function extractContentBetweenElements(start, end) {
|
|
142
|
-
let content = '';
|
|
143
|
-
let current = start;
|
|
144
|
-
// Process all elements between start and end
|
|
145
|
-
while (current && current !== end) {
|
|
146
|
-
content += getAllTextContent(current);
|
|
147
|
-
// Check children first (depth-first)
|
|
148
|
-
if (current.firstElementChild && current !== start) {
|
|
149
|
-
current = current.firstElementChild;
|
|
150
|
-
}
|
|
151
|
-
// Then try next sibling
|
|
152
|
-
else if (current.nextElementSibling) {
|
|
153
|
-
current = current.nextElementSibling;
|
|
154
|
-
}
|
|
155
|
-
// Finally try parent's next sibling
|
|
156
|
-
else {
|
|
157
|
-
let parent = current.parentElement;
|
|
158
|
-
while (parent && parent !== end && !parent.nextElementSibling) {
|
|
159
|
-
parent = parent.parentElement;
|
|
160
|
-
}
|
|
161
|
-
if (parent && parent !== end) {
|
|
162
|
-
current = parent.nextElementSibling;
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return cleanText(content);
|
|
170
|
-
}
|
|
171
|
-
function extractSections(mainContent) {
|
|
172
|
-
const headerSelectors = [
|
|
173
|
-
'h1',
|
|
174
|
-
'h2',
|
|
175
|
-
'h3',
|
|
176
|
-
'h4',
|
|
177
|
-
'[class*="heading"]',
|
|
178
|
-
'[class*="title"]',
|
|
179
|
-
'[class*="sbdocs-h"]',
|
|
180
|
-
'[class*="story-title"]',
|
|
181
|
-
'[class*="docblock-title"]',
|
|
182
|
-
'[class*="docs-title"]',
|
|
183
|
-
];
|
|
184
|
-
const headers = Array.from(mainContent.querySelectorAll(headerSelectors.join(','))).filter((header) => {
|
|
185
|
-
const text = header.textContent?.trim();
|
|
186
|
-
return text && text.length > 0;
|
|
187
|
-
});
|
|
188
|
-
if (headers.length === 0) {
|
|
189
|
-
// No headers found, treat entire content as one section
|
|
190
|
-
const title = mainContent.querySelector('h1, [class*="title"]')?.textContent?.trim() || 'Content';
|
|
191
|
-
const body = getAllTextContent(mainContent);
|
|
192
|
-
if (body.length > 0) {
|
|
193
|
-
return [{ title, body: cleanText(body) }];
|
|
194
|
-
}
|
|
195
|
-
return [];
|
|
196
|
-
}
|
|
197
|
-
const components = [];
|
|
198
|
-
// Process content before first header
|
|
199
|
-
if (headers[0].previousElementSibling) {
|
|
200
|
-
const introContent = extractContentBetweenElements(mainContent.firstElementChild, headers[0]);
|
|
201
|
-
if (introContent.length > 0) {
|
|
202
|
-
components.push({
|
|
203
|
-
title: 'Introduction',
|
|
204
|
-
body: introContent,
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
// Process sections between headers
|
|
209
|
-
headers.forEach((header, index) => {
|
|
210
|
-
const nextHeader = headers[index + 1];
|
|
211
|
-
const title = header.textContent?.trim() || '';
|
|
212
|
-
const body = extractContentBetweenElements(header, nextHeader);
|
|
213
|
-
if (body.length > 0) {
|
|
214
|
-
components.push({ title, body });
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
// Filter out empty components and normalize
|
|
218
|
-
return components
|
|
219
|
-
.filter((comp) => comp.body.length > 0)
|
|
220
|
-
.map((comp) => ({
|
|
221
|
-
title: comp.title,
|
|
222
|
-
body: cleanText(comp.body),
|
|
223
|
-
}));
|
|
224
|
-
}
|
|
225
|
-
export async function processHtmlContent(page) {
|
|
226
|
-
try {
|
|
227
|
-
logger.debug(`[ContentProcessor] Processing content for ${page.url}`);
|
|
228
|
-
const dom = new JSDOM(page.content);
|
|
229
|
-
const doc = dom.window.document;
|
|
230
|
-
// Try to find main content first
|
|
231
|
-
const mainContent = findMainContent(doc);
|
|
232
|
-
// If no main content found, use Readability
|
|
233
|
-
if (!mainContent) {
|
|
234
|
-
logger.debug('[ContentProcessor] No main content found, trying Readability');
|
|
235
|
-
const reader = new Readability(doc);
|
|
236
|
-
const readability = reader.parse();
|
|
237
|
-
if (!readability) {
|
|
238
|
-
logger.debug(`[ContentProcessor] No content could be extracted from ${page.url}`);
|
|
239
|
-
return undefined;
|
|
240
|
-
}
|
|
241
|
-
// Use HTML content to preserve code blocks with markdown fences
|
|
242
|
-
// This prevents code examples from triggering false positive security detections
|
|
243
|
-
const contentWithCodeBlocks = readability.content ? extractTextWithCodeBlocks(readability.content) : readability.textContent || '';
|
|
244
|
-
return {
|
|
245
|
-
article: {
|
|
246
|
-
url: page.url,
|
|
247
|
-
path: page.path,
|
|
248
|
-
title: readability.title || page.path,
|
|
249
|
-
components: [
|
|
250
|
-
{
|
|
251
|
-
title: readability.title || 'Content',
|
|
252
|
-
body: cleanText(contentWithCodeBlocks),
|
|
253
|
-
},
|
|
254
|
-
],
|
|
255
|
-
},
|
|
256
|
-
content: cleanText(contentWithCodeBlocks),
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
logger.debug('[ContentProcessor] Found main content, extracting sections');
|
|
260
|
-
// Extract sections from main content
|
|
261
|
-
const components = extractSections(mainContent);
|
|
262
|
-
if (components.length === 0) {
|
|
263
|
-
logger.debug(`[ContentProcessor] No valid content sections found in ${page.url}`);
|
|
264
|
-
return undefined;
|
|
265
|
-
}
|
|
266
|
-
logger.debug(`[ContentProcessor] Extracted ${components.length} sections`);
|
|
267
|
-
const article = {
|
|
268
|
-
url: page.url,
|
|
269
|
-
path: page.path,
|
|
270
|
-
title: page.title || components[0].title,
|
|
271
|
-
components,
|
|
272
|
-
};
|
|
273
|
-
return {
|
|
274
|
-
article,
|
|
275
|
-
content: components
|
|
276
|
-
.map((comp) => `${comp.title}\n\n${comp.body}`)
|
|
277
|
-
.join('\n\n')
|
|
278
|
-
.trim(),
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
catch (error) {
|
|
282
|
-
logger.debug('[ContentProcessor] Error processing HTML content:', error);
|
|
283
|
-
logger.debug('[ContentProcessor] Error details:', error instanceof Error ? error.stack : error);
|
|
284
|
-
return undefined;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
//# sourceMappingURL=content.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/processor/content.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
import { processHtmlContent } from './content.js';
|
|
2
|
-
describe('HTML Content Processor', () => {
|
|
3
|
-
describe('processHtmlContent', () => {
|
|
4
|
-
it('should process simple HTML content', async () => {
|
|
5
|
-
const page = {
|
|
6
|
-
url: 'https://example.com/docs/page',
|
|
7
|
-
path: '/docs/page',
|
|
8
|
-
title: 'Documentation Page',
|
|
9
|
-
content: `
|
|
10
|
-
<!DOCTYPE html>
|
|
11
|
-
<html>
|
|
12
|
-
<head><title>Page Title</title></head>
|
|
13
|
-
<body>
|
|
14
|
-
<main>
|
|
15
|
-
<h1>Welcome to the Documentation</h1>
|
|
16
|
-
<p>This is the introduction paragraph.</p>
|
|
17
|
-
<h2>Getting Started</h2>
|
|
18
|
-
<p>Here's how to get started.</p>
|
|
19
|
-
</main>
|
|
20
|
-
</body>
|
|
21
|
-
</html>
|
|
22
|
-
`,
|
|
23
|
-
};
|
|
24
|
-
const result = await processHtmlContent(page);
|
|
25
|
-
expect(result).toBeDefined();
|
|
26
|
-
expect(result?.article.url).toBe(page.url);
|
|
27
|
-
expect(result?.article.components.length).toBeGreaterThan(0);
|
|
28
|
-
});
|
|
29
|
-
it('should extract content from article tags', async () => {
|
|
30
|
-
const page = {
|
|
31
|
-
url: 'https://example.com/blog/post',
|
|
32
|
-
path: '/blog/post',
|
|
33
|
-
title: 'Blog Post',
|
|
34
|
-
content: `
|
|
35
|
-
<html>
|
|
36
|
-
<body>
|
|
37
|
-
<nav>Navigation items</nav>
|
|
38
|
-
<article>
|
|
39
|
-
<h1>Article Title</h1>
|
|
40
|
-
<p>Article content goes here.</p>
|
|
41
|
-
<h2>Section One</h2>
|
|
42
|
-
<p>More content.</p>
|
|
43
|
-
</article>
|
|
44
|
-
<footer>Footer content</footer>
|
|
45
|
-
</body>
|
|
46
|
-
</html>
|
|
47
|
-
`,
|
|
48
|
-
};
|
|
49
|
-
const result = await processHtmlContent(page);
|
|
50
|
-
expect(result).toBeDefined();
|
|
51
|
-
expect(result?.content).toContain('Article content');
|
|
52
|
-
// Should not include nav/footer content
|
|
53
|
-
expect(result?.content).not.toContain('Navigation items');
|
|
54
|
-
});
|
|
55
|
-
it('should handle documentation-specific selectors', async () => {
|
|
56
|
-
const page = {
|
|
57
|
-
url: 'https://example.com/docs',
|
|
58
|
-
path: '/docs',
|
|
59
|
-
title: 'Docs',
|
|
60
|
-
content: `
|
|
61
|
-
<html>
|
|
62
|
-
<body>
|
|
63
|
-
<div class="sidebar">Sidebar</div>
|
|
64
|
-
<div class="markdown-body">
|
|
65
|
-
<h1>Documentation</h1>
|
|
66
|
-
<p>Main documentation content.</p>
|
|
67
|
-
</div>
|
|
68
|
-
</body>
|
|
69
|
-
</html>
|
|
70
|
-
`,
|
|
71
|
-
};
|
|
72
|
-
const result = await processHtmlContent(page);
|
|
73
|
-
expect(result).toBeDefined();
|
|
74
|
-
expect(result?.content).toContain('Main documentation content');
|
|
75
|
-
});
|
|
76
|
-
it('should preserve code blocks', async () => {
|
|
77
|
-
const page = {
|
|
78
|
-
url: 'https://example.com/code',
|
|
79
|
-
path: '/code',
|
|
80
|
-
title: 'Code',
|
|
81
|
-
content: `
|
|
82
|
-
<html>
|
|
83
|
-
<body>
|
|
84
|
-
<main>
|
|
85
|
-
<h1>Code Examples</h1>
|
|
86
|
-
<pre><code>function example() {
|
|
87
|
-
return "Hello";
|
|
88
|
-
}</code></pre>
|
|
89
|
-
</main>
|
|
90
|
-
</body>
|
|
91
|
-
</html>
|
|
92
|
-
`,
|
|
93
|
-
};
|
|
94
|
-
const result = await processHtmlContent(page);
|
|
95
|
-
expect(result).toBeDefined();
|
|
96
|
-
expect(result?.content).toContain('function example');
|
|
97
|
-
});
|
|
98
|
-
it('should handle lists properly', async () => {
|
|
99
|
-
const page = {
|
|
100
|
-
url: 'https://example.com/list',
|
|
101
|
-
path: '/list',
|
|
102
|
-
title: 'List',
|
|
103
|
-
content: `
|
|
104
|
-
<html>
|
|
105
|
-
<body>
|
|
106
|
-
<main>
|
|
107
|
-
<h1>Features</h1>
|
|
108
|
-
<ul>
|
|
109
|
-
<li>Feature one</li>
|
|
110
|
-
<li>Feature two</li>
|
|
111
|
-
<li>Feature three</li>
|
|
112
|
-
</ul>
|
|
113
|
-
</main>
|
|
114
|
-
</body>
|
|
115
|
-
</html>
|
|
116
|
-
`,
|
|
117
|
-
};
|
|
118
|
-
const result = await processHtmlContent(page);
|
|
119
|
-
expect(result).toBeDefined();
|
|
120
|
-
expect(result?.content).toContain('Feature one');
|
|
121
|
-
expect(result?.content).toContain('Feature two');
|
|
122
|
-
});
|
|
123
|
-
it('should handle tables', async () => {
|
|
124
|
-
const page = {
|
|
125
|
-
url: 'https://example.com/table',
|
|
126
|
-
path: '/table',
|
|
127
|
-
title: 'Table',
|
|
128
|
-
content: `
|
|
129
|
-
<html>
|
|
130
|
-
<body>
|
|
131
|
-
<main>
|
|
132
|
-
<h1>API Reference</h1>
|
|
133
|
-
<table>
|
|
134
|
-
<tr><th>Method</th><th>Description</th></tr>
|
|
135
|
-
<tr><td>GET</td><td>Retrieve data</td></tr>
|
|
136
|
-
<tr><td>POST</td><td>Create data</td></tr>
|
|
137
|
-
</table>
|
|
138
|
-
</main>
|
|
139
|
-
</body>
|
|
140
|
-
</html>
|
|
141
|
-
`,
|
|
142
|
-
};
|
|
143
|
-
const result = await processHtmlContent(page);
|
|
144
|
-
expect(result).toBeDefined();
|
|
145
|
-
expect(result?.content).toContain('GET');
|
|
146
|
-
expect(result?.content).toContain('POST');
|
|
147
|
-
});
|
|
148
|
-
it('should skip script and style tags', async () => {
|
|
149
|
-
const page = {
|
|
150
|
-
url: 'https://example.com/scripts',
|
|
151
|
-
path: '/scripts',
|
|
152
|
-
title: 'Scripts',
|
|
153
|
-
content: `
|
|
154
|
-
<html>
|
|
155
|
-
<head>
|
|
156
|
-
<style>.class { color: red; }</style>
|
|
157
|
-
</head>
|
|
158
|
-
<body>
|
|
159
|
-
<script>alert('hello');</script>
|
|
160
|
-
<main>
|
|
161
|
-
<h1>Content</h1>
|
|
162
|
-
<p>Actual content here.</p>
|
|
163
|
-
</main>
|
|
164
|
-
<script>console.log('end');</script>
|
|
165
|
-
</body>
|
|
166
|
-
</html>
|
|
167
|
-
`,
|
|
168
|
-
};
|
|
169
|
-
const result = await processHtmlContent(page);
|
|
170
|
-
expect(result).toBeDefined();
|
|
171
|
-
expect(result?.content).not.toContain('alert');
|
|
172
|
-
expect(result?.content).not.toContain('color: red');
|
|
173
|
-
expect(result?.content).toContain('Actual content');
|
|
174
|
-
});
|
|
175
|
-
it('should use Readability as fallback', async () => {
|
|
176
|
-
const page = {
|
|
177
|
-
url: 'https://example.com/blog',
|
|
178
|
-
path: '/blog',
|
|
179
|
-
title: 'Blog',
|
|
180
|
-
content: `
|
|
181
|
-
<html>
|
|
182
|
-
<head><title>Blog Post</title></head>
|
|
183
|
-
<body>
|
|
184
|
-
<div>
|
|
185
|
-
<p>This is a paragraph of content.</p>
|
|
186
|
-
<p>Another paragraph with more information.</p>
|
|
187
|
-
<p>Yet another paragraph to provide enough content for Readability.</p>
|
|
188
|
-
<p>Additional paragraph for proper content extraction.</p>
|
|
189
|
-
<p>Final paragraph of meaningful content.</p>
|
|
190
|
-
</div>
|
|
191
|
-
</body>
|
|
192
|
-
</html>
|
|
193
|
-
`,
|
|
194
|
-
};
|
|
195
|
-
const result = await processHtmlContent(page);
|
|
196
|
-
// Should still extract something even without clear main content
|
|
197
|
-
expect(result).toBeDefined();
|
|
198
|
-
});
|
|
199
|
-
it('should return undefined for pages without extractable content', async () => {
|
|
200
|
-
const page = {
|
|
201
|
-
url: 'https://example.com/empty',
|
|
202
|
-
path: '/empty',
|
|
203
|
-
title: 'Empty',
|
|
204
|
-
content: `
|
|
205
|
-
<html>
|
|
206
|
-
<body>
|
|
207
|
-
<nav>Just navigation</nav>
|
|
208
|
-
</body>
|
|
209
|
-
</html>
|
|
210
|
-
`,
|
|
211
|
-
};
|
|
212
|
-
const result = await processHtmlContent(page);
|
|
213
|
-
// May return undefined or minimal content depending on parser
|
|
214
|
-
if (result) {
|
|
215
|
-
expect(result.content.length).toBeLessThan(100);
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
it('should handle Storybook-specific classes', async () => {
|
|
219
|
-
const page = {
|
|
220
|
-
url: 'https://storybook.example.com/docs',
|
|
221
|
-
path: '/docs',
|
|
222
|
-
title: 'Storybook',
|
|
223
|
-
content: `
|
|
224
|
-
<html>
|
|
225
|
-
<body>
|
|
226
|
-
<div class="sbdocs-wrapper">
|
|
227
|
-
<div class="sbdocs-content">
|
|
228
|
-
<h1 class="sbdocs-h1">Component Documentation</h1>
|
|
229
|
-
<div class="docblock-description">
|
|
230
|
-
<p>Description of the component.</p>
|
|
231
|
-
</div>
|
|
232
|
-
</div>
|
|
233
|
-
</div>
|
|
234
|
-
</body>
|
|
235
|
-
</html>
|
|
236
|
-
`,
|
|
237
|
-
};
|
|
238
|
-
const result = await processHtmlContent(page);
|
|
239
|
-
expect(result).toBeDefined();
|
|
240
|
-
expect(result?.content).toContain('Component Documentation');
|
|
241
|
-
});
|
|
242
|
-
it('should handle React app root containers', async () => {
|
|
243
|
-
const page = {
|
|
244
|
-
url: 'https://example.com/react-app',
|
|
245
|
-
path: '/react-app',
|
|
246
|
-
title: 'React App',
|
|
247
|
-
content: `
|
|
248
|
-
<html>
|
|
249
|
-
<body>
|
|
250
|
-
<div id="root">
|
|
251
|
-
<div>
|
|
252
|
-
<h1>React App Content</h1>
|
|
253
|
-
<p>This is rendered by React.</p>
|
|
254
|
-
</div>
|
|
255
|
-
</div>
|
|
256
|
-
</body>
|
|
257
|
-
</html>
|
|
258
|
-
`,
|
|
259
|
-
};
|
|
260
|
-
const result = await processHtmlContent(page);
|
|
261
|
-
expect(result).toBeDefined();
|
|
262
|
-
expect(result?.content).toContain('React App Content');
|
|
263
|
-
});
|
|
264
|
-
it('should handle Next.js containers', async () => {
|
|
265
|
-
const page = {
|
|
266
|
-
url: 'https://example.com/nextjs',
|
|
267
|
-
path: '/nextjs',
|
|
268
|
-
title: 'Next.js',
|
|
269
|
-
content: `
|
|
270
|
-
<html>
|
|
271
|
-
<body>
|
|
272
|
-
<div id="__next">
|
|
273
|
-
<main>
|
|
274
|
-
<h1>Next.js Page</h1>
|
|
275
|
-
<p>Content from Next.js.</p>
|
|
276
|
-
</main>
|
|
277
|
-
</div>
|
|
278
|
-
</body>
|
|
279
|
-
</html>
|
|
280
|
-
`,
|
|
281
|
-
};
|
|
282
|
-
const result = await processHtmlContent(page);
|
|
283
|
-
expect(result).toBeDefined();
|
|
284
|
-
expect(result?.content).toContain('Next.js Page');
|
|
285
|
-
});
|
|
286
|
-
it('should handle multiple heading levels for section extraction', async () => {
|
|
287
|
-
const page = {
|
|
288
|
-
url: 'https://example.com/headings',
|
|
289
|
-
path: '/headings',
|
|
290
|
-
title: 'Headings',
|
|
291
|
-
content: `
|
|
292
|
-
<html>
|
|
293
|
-
<body>
|
|
294
|
-
<main>
|
|
295
|
-
<h1>Main Title</h1>
|
|
296
|
-
<p>Intro text.</p>
|
|
297
|
-
<h2>Section 1</h2>
|
|
298
|
-
<p>Section 1 content.</p>
|
|
299
|
-
<h3>Subsection 1.1</h3>
|
|
300
|
-
<p>Subsection content.</p>
|
|
301
|
-
<h2>Section 2</h2>
|
|
302
|
-
<p>Section 2 content.</p>
|
|
303
|
-
<h4>Deep Section</h4>
|
|
304
|
-
<p>Deep content.</p>
|
|
305
|
-
</main>
|
|
306
|
-
</body>
|
|
307
|
-
</html>
|
|
308
|
-
`,
|
|
309
|
-
};
|
|
310
|
-
const result = await processHtmlContent(page);
|
|
311
|
-
expect(result).toBeDefined();
|
|
312
|
-
expect(result?.article.components.length).toBeGreaterThan(3);
|
|
313
|
-
});
|
|
314
|
-
it('should handle malformed HTML gracefully', async () => {
|
|
315
|
-
const page = {
|
|
316
|
-
url: 'https://example.com/malformed',
|
|
317
|
-
path: '/malformed',
|
|
318
|
-
title: 'Malformed',
|
|
319
|
-
content: `
|
|
320
|
-
<html>
|
|
321
|
-
<body>
|
|
322
|
-
<div>
|
|
323
|
-
<p>Unclosed paragraph
|
|
324
|
-
<p>Another unclosed
|
|
325
|
-
<span>Nested <b>incorrectly</span></b>
|
|
326
|
-
<main>
|
|
327
|
-
<h1>Still Works</h1>
|
|
328
|
-
<p>Content here.</p>
|
|
329
|
-
</main>
|
|
330
|
-
</div>
|
|
331
|
-
</body>
|
|
332
|
-
</html>
|
|
333
|
-
`,
|
|
334
|
-
};
|
|
335
|
-
// Should not throw
|
|
336
|
-
const result = await processHtmlContent(page);
|
|
337
|
-
expect(result).toBeDefined();
|
|
338
|
-
});
|
|
339
|
-
it('should clean text and remove extra whitespace', async () => {
|
|
340
|
-
const page = {
|
|
341
|
-
url: 'https://example.com/whitespace',
|
|
342
|
-
path: '/whitespace',
|
|
343
|
-
title: 'Whitespace',
|
|
344
|
-
content: `
|
|
345
|
-
<html>
|
|
346
|
-
<body>
|
|
347
|
-
<main>
|
|
348
|
-
<h1>Title</h1>
|
|
349
|
-
<p>Text with extra spaces.</p>
|
|
350
|
-
<p>
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
Multiple newlines here.
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
</p>
|
|
357
|
-
</main>
|
|
358
|
-
</body>
|
|
359
|
-
</html>
|
|
360
|
-
`,
|
|
361
|
-
};
|
|
362
|
-
const result = await processHtmlContent(page);
|
|
363
|
-
expect(result).toBeDefined();
|
|
364
|
-
// Should normalize whitespace
|
|
365
|
-
expect(result?.content).not.toMatch(/\s{3,}/);
|
|
366
|
-
});
|
|
367
|
-
});
|
|
368
|
-
});
|
|
369
|
-
//# sourceMappingURL=content.test.js.map
|