webforai 4.2.0 → 4.3.1
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/THIRD_PARTY_NOTICES.md +42 -0
- package/dist/adapters/sites/hackernews.js +3 -3
- package/dist/adapters/sites/youtube.js +4 -2
- package/dist/bin.js +738 -474
- package/dist/cjs/adapters/sites/hackernews.js +3 -3
- package/dist/cjs/adapters/sites/youtube.js +5 -2
- package/dist/cjs/detect-client-shell.js +2 -0
- package/dist/cjs/extractors/lib/layout-tables.js +48 -9
- package/dist/cjs/extractors/lib/sanitize.js +38 -2
- package/dist/cjs/extractors/lib/terminators.js +2 -0
- package/dist/cjs/index.js +3 -0
- package/dist/cjs/mdast-handlers/custom-code-handler.js +31 -2
- package/dist/cjs/normalize/index.js +97 -4
- package/dist/cjs/utils/detect-code-lang.js +4 -5
- package/dist/cjs/utils/parse-html.js +73 -5
- package/dist/detect-client-shell.js +1 -0
- package/dist/extractors/lib/layout-tables.js +48 -9
- package/dist/extractors/lib/sanitize.js +38 -2
- package/dist/extractors/lib/terminators.js +2 -0
- package/dist/index.js +2 -0
- package/dist/mdast-handlers/custom-code-handler.js +31 -2
- package/dist/normalize/index.js +94 -3
- package/dist/types/adapters/sites/hackernews.js.map +1 -1
- package/dist/types/adapters/sites/youtube.d.ts +2 -0
- package/dist/types/adapters/sites/youtube.js.map +1 -1
- package/dist/types/detect-client-shell.d.ts +2 -0
- package/dist/types/detect-client-shell.js.map +1 -1
- package/dist/types/extractors/lib/layout-tables.d.ts +9 -2
- package/dist/types/extractors/lib/layout-tables.js.map +1 -1
- package/dist/types/extractors/lib/sanitize.js.map +1 -1
- package/dist/types/extractors/lib/terminators.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/mdast-handlers/custom-code-handler.d.ts +5 -0
- package/dist/types/mdast-handlers/custom-code-handler.js.map +1 -1
- package/dist/types/normalize/index.d.ts +21 -0
- package/dist/types/normalize/index.js.map +1 -1
- package/dist/types/utils/detect-code-lang.d.ts +7 -1
- package/dist/types/utils/detect-code-lang.js.map +1 -1
- package/dist/types/utils/parse-html.d.ts +12 -6
- package/dist/types/utils/parse-html.js.map +1 -1
- package/dist/utils/detect-code-lang.js +4 -5
- package/dist/utils/parse-html.js +73 -5
- package/package.json +3 -2
package/dist/bin.js
CHANGED
|
@@ -6,7 +6,7 @@ import { CommanderError, program } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "webforai",
|
|
9
|
-
version: "4.
|
|
9
|
+
version: "4.3.1",
|
|
10
10
|
description: "Convert web pages and local HTML to clean, LLM-ready Markdown: library, CLI (npx webforai) and hosted-API client.",
|
|
11
11
|
author: "inaridiy",
|
|
12
12
|
license: "Apache-2.0",
|
|
@@ -45,7 +45,8 @@ var package_default = {
|
|
|
45
45
|
},
|
|
46
46
|
files: [
|
|
47
47
|
"dist",
|
|
48
|
-
"!dist/types/**/*.js"
|
|
48
|
+
"!dist/types/**/*.js",
|
|
49
|
+
"THIRD_PARTY_NOTICES.md"
|
|
49
50
|
],
|
|
50
51
|
main: "dist/cjs/index.js",
|
|
51
52
|
type: "module",
|
|
@@ -1360,8 +1361,440 @@ import path5 from "node:path";
|
|
|
1360
1361
|
// src/utils/parse-html.ts
|
|
1361
1362
|
import { fromParse5 } from "hast-util-from-parse5";
|
|
1362
1363
|
import { parse, parseFragment } from "parse5";
|
|
1364
|
+
|
|
1365
|
+
// src/utils/hast-fast.ts
|
|
1366
|
+
var EMPTY_METRICS = { text: 0, link: 0, commas: 0, paragraphs: 0, elements: 0 };
|
|
1367
|
+
var COMMA_REGEX = /[,、,،]/g;
|
|
1368
|
+
var PARAGRAPH_LIKE = /* @__PURE__ */ new Set(["p", "pre", "li", "blockquote", "figcaption", "dd", "dt"]);
|
|
1369
|
+
var NON_PROSE_TAGS = /* @__PURE__ */ new Set(["script", "style", "noscript", "template", "svg", "math", "iframe", "object"]);
|
|
1370
|
+
var isElement = (node) => typeof node === "object" && node !== null && node.type === "element";
|
|
1371
|
+
var isParent = (node) => typeof node === "object" && node !== null && Array.isArray(node.children);
|
|
1372
|
+
var classList = (element4) => {
|
|
1373
|
+
const className = element4.properties?.className;
|
|
1374
|
+
if (Array.isArray(className)) {
|
|
1375
|
+
return className;
|
|
1376
|
+
}
|
|
1377
|
+
if (typeof className === "string") {
|
|
1378
|
+
return className.split(/\s+/).filter(Boolean);
|
|
1379
|
+
}
|
|
1380
|
+
return [];
|
|
1381
|
+
};
|
|
1382
|
+
var matchString = (element4) => {
|
|
1383
|
+
const id = element4.properties?.id;
|
|
1384
|
+
const classes = classList(element4);
|
|
1385
|
+
if (!id && classes.length === 0) {
|
|
1386
|
+
return element4.tagName;
|
|
1387
|
+
}
|
|
1388
|
+
return `${element4.tagName} ${typeof id === "string" ? id : ""} ${classes.join(" ")}`;
|
|
1389
|
+
};
|
|
1390
|
+
var MetricsCollector = class {
|
|
1391
|
+
#cache = /* @__PURE__ */ new WeakMap();
|
|
1392
|
+
metrics(node) {
|
|
1393
|
+
if (node.type === "text") {
|
|
1394
|
+
const value = node.value;
|
|
1395
|
+
if (value.trim().length === 0) {
|
|
1396
|
+
return EMPTY_METRICS;
|
|
1397
|
+
}
|
|
1398
|
+
return {
|
|
1399
|
+
text: value.length,
|
|
1400
|
+
link: 0,
|
|
1401
|
+
commas: (value.match(COMMA_REGEX) ?? []).length,
|
|
1402
|
+
paragraphs: 0,
|
|
1403
|
+
elements: 0
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
if (!isParent(node)) {
|
|
1407
|
+
return EMPTY_METRICS;
|
|
1408
|
+
}
|
|
1409
|
+
const cached = this.#cache.get(node);
|
|
1410
|
+
if (cached) {
|
|
1411
|
+
return cached;
|
|
1412
|
+
}
|
|
1413
|
+
if (isElement(node) && NON_PROSE_TAGS.has(node.tagName)) {
|
|
1414
|
+
this.#cache.set(node, EMPTY_METRICS);
|
|
1415
|
+
return EMPTY_METRICS;
|
|
1416
|
+
}
|
|
1417
|
+
let text7 = 0;
|
|
1418
|
+
let link = 0;
|
|
1419
|
+
let commas = 0;
|
|
1420
|
+
let paragraphs = 0;
|
|
1421
|
+
let elements = 0;
|
|
1422
|
+
for (const child of node.children) {
|
|
1423
|
+
const childMetrics = this.metrics(child);
|
|
1424
|
+
text7 += childMetrics.text;
|
|
1425
|
+
link += childMetrics.link;
|
|
1426
|
+
commas += childMetrics.commas;
|
|
1427
|
+
paragraphs += childMetrics.paragraphs;
|
|
1428
|
+
elements += childMetrics.elements;
|
|
1429
|
+
if (isElement(child)) {
|
|
1430
|
+
elements += 1;
|
|
1431
|
+
if (PARAGRAPH_LIKE.has(child.tagName)) {
|
|
1432
|
+
paragraphs += 1;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
if (isElement(node) && node.tagName === "a") {
|
|
1437
|
+
link = text7;
|
|
1438
|
+
}
|
|
1439
|
+
const metrics = { text: text7, link, commas, paragraphs, elements };
|
|
1440
|
+
this.#cache.set(node, metrics);
|
|
1441
|
+
return metrics;
|
|
1442
|
+
}
|
|
1443
|
+
textLength(node) {
|
|
1444
|
+
return this.metrics(node).text;
|
|
1445
|
+
}
|
|
1446
|
+
/** Fraction of a subtree's text that is link anchor text. `0` for empty subtrees. */
|
|
1447
|
+
linkDensity(node) {
|
|
1448
|
+
const { text: text7, link } = this.metrics(node);
|
|
1449
|
+
if (text7 === 0) {
|
|
1450
|
+
return 0;
|
|
1451
|
+
}
|
|
1452
|
+
return Math.min(1, link / text7);
|
|
1453
|
+
}
|
|
1454
|
+
};
|
|
1455
|
+
var pruneInPlace = (tree, keep) => {
|
|
1456
|
+
if (!isParent(tree)) {
|
|
1457
|
+
return tree;
|
|
1458
|
+
}
|
|
1459
|
+
const stack = [tree];
|
|
1460
|
+
while (stack.length > 0) {
|
|
1461
|
+
const parent = stack.pop();
|
|
1462
|
+
const children = parent.children;
|
|
1463
|
+
let write = 0;
|
|
1464
|
+
for (let read = 0; read < children.length; read++) {
|
|
1465
|
+
const child = children[read];
|
|
1466
|
+
if (!keep(child, parent)) {
|
|
1467
|
+
continue;
|
|
1468
|
+
}
|
|
1469
|
+
children[write++] = child;
|
|
1470
|
+
if (isParent(child)) {
|
|
1471
|
+
stack.push(child);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
children.length = write;
|
|
1475
|
+
}
|
|
1476
|
+
return tree;
|
|
1477
|
+
};
|
|
1478
|
+
var walk = (tree, visit) => {
|
|
1479
|
+
const stack = [{ node: tree, parent: void 0 }];
|
|
1480
|
+
while (stack.length > 0) {
|
|
1481
|
+
const { node, parent } = stack.pop();
|
|
1482
|
+
if (visit(node, parent) === false) {
|
|
1483
|
+
continue;
|
|
1484
|
+
}
|
|
1485
|
+
if (isParent(node)) {
|
|
1486
|
+
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
1487
|
+
stack.push({ node: node.children[i], parent: node });
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
var collectElements = (tree, predicate) => {
|
|
1493
|
+
const found = [];
|
|
1494
|
+
walk(tree, (node) => {
|
|
1495
|
+
if (isElement(node) && predicate(node)) {
|
|
1496
|
+
found.push(node);
|
|
1497
|
+
}
|
|
1498
|
+
});
|
|
1499
|
+
return found;
|
|
1500
|
+
};
|
|
1501
|
+
var findElement = (tree, predicate) => {
|
|
1502
|
+
let found;
|
|
1503
|
+
walk(tree, (node) => {
|
|
1504
|
+
if (found) {
|
|
1505
|
+
return false;
|
|
1506
|
+
}
|
|
1507
|
+
if (isElement(node) && predicate(node)) {
|
|
1508
|
+
found = node;
|
|
1509
|
+
return false;
|
|
1510
|
+
}
|
|
1511
|
+
});
|
|
1512
|
+
return found;
|
|
1513
|
+
};
|
|
1514
|
+
var cloneHast = (tree) => {
|
|
1515
|
+
try {
|
|
1516
|
+
return structuredClone(tree);
|
|
1517
|
+
} catch {
|
|
1518
|
+
return manualClone(tree);
|
|
1519
|
+
}
|
|
1520
|
+
};
|
|
1521
|
+
var manualClone = (node) => {
|
|
1522
|
+
if (Array.isArray(node)) {
|
|
1523
|
+
return node.map(manualClone);
|
|
1524
|
+
}
|
|
1525
|
+
if (typeof node !== "object" || node === null) {
|
|
1526
|
+
return node;
|
|
1527
|
+
}
|
|
1528
|
+
const copy = {};
|
|
1529
|
+
for (const [key, value] of Object.entries(node)) {
|
|
1530
|
+
copy[key] = manualClone(value);
|
|
1531
|
+
}
|
|
1532
|
+
return copy;
|
|
1533
|
+
};
|
|
1534
|
+
var property = (element4, key) => {
|
|
1535
|
+
const properties = element4.properties;
|
|
1536
|
+
if (!properties) {
|
|
1537
|
+
return void 0;
|
|
1538
|
+
}
|
|
1539
|
+
const direct = properties[key];
|
|
1540
|
+
if (direct !== void 0) {
|
|
1541
|
+
return direct;
|
|
1542
|
+
}
|
|
1543
|
+
if (!(key.includes("-") || key.includes(":"))) {
|
|
1544
|
+
return void 0;
|
|
1545
|
+
}
|
|
1546
|
+
return properties[camelCaseAttribute(key)];
|
|
1547
|
+
};
|
|
1548
|
+
var camelCaseAttribute = (key) => key.replace(/[-:]([a-z])/g, (_, character) => character.toUpperCase());
|
|
1549
|
+
var numericProperty = (element4, key) => {
|
|
1550
|
+
const raw = property(element4, key);
|
|
1551
|
+
if (typeof raw === "number") {
|
|
1552
|
+
return Number.isFinite(raw) ? raw : void 0;
|
|
1553
|
+
}
|
|
1554
|
+
if (typeof raw === "string" && raw.length > 0) {
|
|
1555
|
+
const parsed = Number.parseFloat(raw);
|
|
1556
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
1557
|
+
}
|
|
1558
|
+
return void 0;
|
|
1559
|
+
};
|
|
1560
|
+
var stringProperty = (element4, key) => {
|
|
1561
|
+
const raw = property(element4, key);
|
|
1562
|
+
if (typeof raw === "string") {
|
|
1563
|
+
return raw;
|
|
1564
|
+
}
|
|
1565
|
+
if (Array.isArray(raw)) {
|
|
1566
|
+
return raw.join(" ");
|
|
1567
|
+
}
|
|
1568
|
+
if (raw === true) {
|
|
1569
|
+
return "";
|
|
1570
|
+
}
|
|
1571
|
+
return void 0;
|
|
1572
|
+
};
|
|
1573
|
+
var isTruthyAttribute = (element4, key) => {
|
|
1574
|
+
const raw = property(element4, key);
|
|
1575
|
+
if (raw === void 0 || raw === null || raw === false) {
|
|
1576
|
+
return false;
|
|
1577
|
+
}
|
|
1578
|
+
if (raw === true) {
|
|
1579
|
+
return true;
|
|
1580
|
+
}
|
|
1581
|
+
if (typeof raw === "string") {
|
|
1582
|
+
return raw.toLowerCase() !== "false";
|
|
1583
|
+
}
|
|
1584
|
+
return true;
|
|
1585
|
+
};
|
|
1586
|
+
|
|
1587
|
+
// src/adapters/sites/youtube.ts
|
|
1588
|
+
var findObjectEnd = (source, start) => {
|
|
1589
|
+
let depth = 0;
|
|
1590
|
+
let inString = false;
|
|
1591
|
+
let escaped = false;
|
|
1592
|
+
for (let index = start; index < source.length; index++) {
|
|
1593
|
+
const character = source[index];
|
|
1594
|
+
if (escaped) {
|
|
1595
|
+
escaped = false;
|
|
1596
|
+
} else if (character === "\\") {
|
|
1597
|
+
escaped = true;
|
|
1598
|
+
} else if (character === '"') {
|
|
1599
|
+
inString = !inString;
|
|
1600
|
+
} else if (!inString) {
|
|
1601
|
+
if (character === "{") {
|
|
1602
|
+
depth += 1;
|
|
1603
|
+
} else if (character === "}" && --depth === 0) {
|
|
1604
|
+
return index + 1;
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
return -1;
|
|
1609
|
+
};
|
|
1610
|
+
var extractAssignedJson = (source, name) => {
|
|
1611
|
+
const markerIndex = source.indexOf(`${name} =`);
|
|
1612
|
+
if (markerIndex === -1) {
|
|
1613
|
+
return void 0;
|
|
1614
|
+
}
|
|
1615
|
+
const start = source.indexOf("{", markerIndex);
|
|
1616
|
+
if (start === -1) {
|
|
1617
|
+
return void 0;
|
|
1618
|
+
}
|
|
1619
|
+
const end = findObjectEnd(source, start);
|
|
1620
|
+
if (end === -1) {
|
|
1621
|
+
return void 0;
|
|
1622
|
+
}
|
|
1623
|
+
try {
|
|
1624
|
+
return JSON.parse(source.slice(start, end));
|
|
1625
|
+
} catch {
|
|
1626
|
+
return void 0;
|
|
1627
|
+
}
|
|
1628
|
+
};
|
|
1629
|
+
var scriptText = (root) => collectElements(root, (element4) => element4.tagName === "script").map(
|
|
1630
|
+
(script) => script.children.map((child) => child.type === "text" ? child.value : "").join("")
|
|
1631
|
+
);
|
|
1632
|
+
var PLAYER_RESPONSE_VARIABLE = "ytInitialPlayerResponse";
|
|
1633
|
+
var findPlayerResponse = (root) => {
|
|
1634
|
+
for (const source of scriptText(root)) {
|
|
1635
|
+
if (!source.includes(PLAYER_RESPONSE_VARIABLE)) {
|
|
1636
|
+
continue;
|
|
1637
|
+
}
|
|
1638
|
+
const parsed = extractAssignedJson(source, PLAYER_RESPONSE_VARIABLE);
|
|
1639
|
+
if (parsed && typeof parsed === "object") {
|
|
1640
|
+
return parsed;
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
return void 0;
|
|
1644
|
+
};
|
|
1645
|
+
var youtubeCaptionTracks = (root) => {
|
|
1646
|
+
const player = findPlayerResponse(root);
|
|
1647
|
+
const renderer = player?.captions?.playerCaptionsTracklistRenderer;
|
|
1648
|
+
const tracks = renderer?.captionTracks;
|
|
1649
|
+
if (!Array.isArray(tracks)) {
|
|
1650
|
+
return [];
|
|
1651
|
+
}
|
|
1652
|
+
return tracks.flatMap((track) => {
|
|
1653
|
+
const url = typeof track.baseUrl === "string" ? track.baseUrl : void 0;
|
|
1654
|
+
const languageCode = typeof track.languageCode === "string" ? track.languageCode : void 0;
|
|
1655
|
+
if (!(url && languageCode)) {
|
|
1656
|
+
return [];
|
|
1657
|
+
}
|
|
1658
|
+
const name = track.name?.simpleText;
|
|
1659
|
+
return [
|
|
1660
|
+
{
|
|
1661
|
+
url,
|
|
1662
|
+
languageCode,
|
|
1663
|
+
name: typeof name === "string" ? name : void 0,
|
|
1664
|
+
isAutomatic: track.kind === "asr"
|
|
1665
|
+
}
|
|
1666
|
+
];
|
|
1667
|
+
});
|
|
1668
|
+
};
|
|
1669
|
+
var text3 = (value) => ({ type: "text", value });
|
|
1670
|
+
var element = (tagName, children) => ({
|
|
1671
|
+
type: "element",
|
|
1672
|
+
tagName,
|
|
1673
|
+
properties: {},
|
|
1674
|
+
children
|
|
1675
|
+
});
|
|
1676
|
+
var heading = (depth, value) => element(`h${depth}`, [text3(value)]);
|
|
1677
|
+
var paragraph = (value) => element("p", [text3(value)]);
|
|
1678
|
+
var descriptionNodes = (description) => description.split(/\n{2,}/).map((block) => block.trim()).filter((block) => block.length > 0).map(
|
|
1679
|
+
(block) => element(
|
|
1680
|
+
"p",
|
|
1681
|
+
block.split("\n").flatMap((line, index) => index === 0 ? [text3(line)] : [element("br", []), text3(line)])
|
|
1682
|
+
)
|
|
1683
|
+
);
|
|
1684
|
+
var formatDuration = (seconds) => {
|
|
1685
|
+
const hours = Math.floor(seconds / 3600);
|
|
1686
|
+
const minutes = Math.floor(seconds % 3600 / 60);
|
|
1687
|
+
const remainder = seconds % 60;
|
|
1688
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
1689
|
+
return hours > 0 ? `${hours}:${pad(minutes)}:${pad(remainder)}` : `${minutes}:${pad(remainder)}`;
|
|
1690
|
+
};
|
|
1691
|
+
var metadataList = (details) => {
|
|
1692
|
+
const items = [];
|
|
1693
|
+
if (details.author) {
|
|
1694
|
+
items.push(element("li", [text3(`Channel: ${details.author}`)]));
|
|
1695
|
+
}
|
|
1696
|
+
const seconds = Number(details.lengthSeconds);
|
|
1697
|
+
if (Number.isFinite(seconds) && seconds > 0) {
|
|
1698
|
+
items.push(element("li", [text3(`Duration: ${formatDuration(seconds)}`)]));
|
|
1699
|
+
}
|
|
1700
|
+
const views = Number(details.viewCount);
|
|
1701
|
+
if (Number.isFinite(views) && views > 0) {
|
|
1702
|
+
items.push(element("li", [text3(`Views: ${views.toLocaleString("en-US")}`)]));
|
|
1703
|
+
}
|
|
1704
|
+
return items.length > 0 ? element("ul", items) : void 0;
|
|
1705
|
+
};
|
|
1706
|
+
var youtubeAdapter = {
|
|
1707
|
+
id: "youtube",
|
|
1708
|
+
matches: ({ url }) => {
|
|
1709
|
+
if (!url) {
|
|
1710
|
+
return false;
|
|
1711
|
+
}
|
|
1712
|
+
try {
|
|
1713
|
+
return /(^|\.)(youtube\.com|youtu\.be|youtube-nocookie\.com)$/.test(new URL(url).hostname);
|
|
1714
|
+
} catch {
|
|
1715
|
+
return false;
|
|
1716
|
+
}
|
|
1717
|
+
},
|
|
1718
|
+
// Scanning every script body for the JSON blob would cost a full traversal on every page, so
|
|
1719
|
+
// the fallback keys off markers the signature already collected.
|
|
1720
|
+
matchesFingerprint: ({ signature }) => Boolean(signature?.ids.has("movie_player") || signature?.classes.has("ytd-app")),
|
|
1721
|
+
extract: ({ hast }) => {
|
|
1722
|
+
const player = findPlayerResponse(hast);
|
|
1723
|
+
const details = player?.videoDetails;
|
|
1724
|
+
if (!details?.title) {
|
|
1725
|
+
return void 0;
|
|
1726
|
+
}
|
|
1727
|
+
const children = [heading(1, details.title)];
|
|
1728
|
+
const metadata = metadataList(details);
|
|
1729
|
+
if (metadata) {
|
|
1730
|
+
children.push(metadata);
|
|
1731
|
+
}
|
|
1732
|
+
const description = details.shortDescription?.trim();
|
|
1733
|
+
if (description) {
|
|
1734
|
+
children.push(heading(2, "Description"));
|
|
1735
|
+
children.push(...descriptionNodes(description));
|
|
1736
|
+
}
|
|
1737
|
+
const tracks = youtubeCaptionTracks(hast);
|
|
1738
|
+
if (tracks.length > 0) {
|
|
1739
|
+
const languages2 = [...new Set(tracks.map((track) => track.languageCode))].sort();
|
|
1740
|
+
children.push(paragraph(`Captions available: ${languages2.join(", ")}`));
|
|
1741
|
+
}
|
|
1742
|
+
return { type: "root", children };
|
|
1743
|
+
}
|
|
1744
|
+
};
|
|
1745
|
+
|
|
1746
|
+
// src/detect-client-shell.ts
|
|
1747
|
+
var SHELL_SUSPICION_THRESHOLD = 300;
|
|
1748
|
+
var SHELL_EMPTY_THRESHOLD = 50;
|
|
1749
|
+
var BODY_PATTERN = /<body[^>]*>([\s\S]*?)<\/body>/i;
|
|
1750
|
+
var HEAD_PATTERN = /<head[^>]*>[\s\S]*?<\/head>/gi;
|
|
1751
|
+
var NOSCRIPT_PATTERN = /<noscript[^>]*>([\s\S]*?)<\/noscript>/gi;
|
|
1752
|
+
var INVISIBLE_ELEMENT_PATTERN = /<(script|style|template|svg|noscript)[^>]*>[\s\S]*?<\/\1>/gi;
|
|
1753
|
+
var COMMENT_PATTERN = /<!--[\s\S]*?-->/g;
|
|
1754
|
+
var TAG_PATTERN = /<[^>]+>/g;
|
|
1755
|
+
var MOUNT_POINT_PATTERN = /<[a-z][^>]*\sid=["'](?:root|app|__next|___gatsby|__nuxt|q-app|svelte|react-root)["']|<app-root[\s>]|\sdata-reactroot[\s>=]/i;
|
|
1756
|
+
var CHALLENGE_PATTERN = /cf-chl|challenge-platform|cf_chl_opt|just a moment|attention required|checking your browser|_incapsula_|px-captcha|datadome/i;
|
|
1757
|
+
var ENTITIES = {
|
|
1758
|
+
" ": " ",
|
|
1759
|
+
"&": "&",
|
|
1760
|
+
"<": "<",
|
|
1761
|
+
">": ">",
|
|
1762
|
+
""": '"',
|
|
1763
|
+
"'": "'"
|
|
1764
|
+
};
|
|
1765
|
+
var visibleTextOf = (markup) => markup.replace(COMMENT_PATTERN, " ").replace(INVISIBLE_ELEMENT_PATTERN, " ").replace(TAG_PATTERN, " ").replace(/&(?:nbsp|amp|lt|gt|quot|#39);/g, (entity) => ENTITIES[entity] ?? " ").replace(/\s+/g, " ").trim();
|
|
1766
|
+
var noscriptMentionsJavascript = (html) => {
|
|
1767
|
+
for (const match of html.matchAll(NOSCRIPT_PATTERN)) {
|
|
1768
|
+
if (/javascript/i.test(match[1] ?? "")) {
|
|
1769
|
+
return true;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
return false;
|
|
1773
|
+
};
|
|
1774
|
+
var detectClientShell = (html) => {
|
|
1775
|
+
const body = BODY_PATTERN.exec(html)?.[1] ?? html.replace(HEAD_PATTERN, " ");
|
|
1776
|
+
const visibleTextLength = visibleTextOf(body).length;
|
|
1777
|
+
if (visibleTextLength >= SHELL_SUSPICION_THRESHOLD) {
|
|
1778
|
+
return { isShell: false, visibleTextLength };
|
|
1779
|
+
}
|
|
1780
|
+
if (CHALLENGE_PATTERN.test(html)) {
|
|
1781
|
+
return { isShell: true, reason: "anti-bot-challenge", visibleTextLength };
|
|
1782
|
+
}
|
|
1783
|
+
if (MOUNT_POINT_PATTERN.test(body)) {
|
|
1784
|
+
return { isShell: true, reason: "spa-shell", visibleTextLength };
|
|
1785
|
+
}
|
|
1786
|
+
if (noscriptMentionsJavascript(html)) {
|
|
1787
|
+
return { isShell: true, reason: "noscript-only", visibleTextLength };
|
|
1788
|
+
}
|
|
1789
|
+
if (visibleTextLength < SHELL_EMPTY_THRESHOLD) {
|
|
1790
|
+
return { isShell: true, reason: "empty-body", visibleTextLength };
|
|
1791
|
+
}
|
|
1792
|
+
return { isShell: false, visibleTextLength };
|
|
1793
|
+
};
|
|
1794
|
+
|
|
1795
|
+
// src/utils/parse-html.ts
|
|
1363
1796
|
var MAX_ELEMENT_DEPTH = 256;
|
|
1364
|
-
var
|
|
1797
|
+
var isParent2 = (node) => "childNodes" in node;
|
|
1365
1798
|
var LINE_ELEMENTS = /* @__PURE__ */ new Set([
|
|
1366
1799
|
"p",
|
|
1367
1800
|
"div",
|
|
@@ -1402,7 +1835,7 @@ var flattenDeep = (root, maxDepth) => {
|
|
|
1402
1835
|
let unwrapped = false;
|
|
1403
1836
|
for (let index = 0; index < children.length; index++) {
|
|
1404
1837
|
const child = children[index];
|
|
1405
|
-
if (!
|
|
1838
|
+
if (!isParent2(child)) {
|
|
1406
1839
|
continue;
|
|
1407
1840
|
}
|
|
1408
1841
|
if (depth + 1 > maxDepth && "tagName" in child) {
|
|
@@ -1426,12 +1859,78 @@ var flattenDeep = (root, maxDepth) => {
|
|
|
1426
1859
|
}
|
|
1427
1860
|
}
|
|
1428
1861
|
};
|
|
1429
|
-
var
|
|
1862
|
+
var SCRIPT_OR_STYLE_START = /<!--|<(script|style)(?=[\s/>])/gi;
|
|
1863
|
+
var CLOSING_TAG = {
|
|
1864
|
+
script: /<\/script[\s/>]/gi,
|
|
1865
|
+
style: /<\/style[\s/>]/gi
|
|
1866
|
+
};
|
|
1867
|
+
var startTagEnd = (html, from) => {
|
|
1868
|
+
let index = from;
|
|
1869
|
+
while (index < html.length) {
|
|
1870
|
+
const char = html[index];
|
|
1871
|
+
if (char === ">") {
|
|
1872
|
+
return index + 1;
|
|
1873
|
+
}
|
|
1874
|
+
if (char === "=") {
|
|
1875
|
+
let value = index + 1;
|
|
1876
|
+
while (value < html.length && /\s/.test(html[value] ?? "")) {
|
|
1877
|
+
value++;
|
|
1878
|
+
}
|
|
1879
|
+
const quote = html[value];
|
|
1880
|
+
if (quote === '"' || quote === "'") {
|
|
1881
|
+
const close = html.indexOf(quote, value + 1);
|
|
1882
|
+
if (close < 0) {
|
|
1883
|
+
return -1;
|
|
1884
|
+
}
|
|
1885
|
+
index = close + 1;
|
|
1886
|
+
continue;
|
|
1887
|
+
}
|
|
1888
|
+
index = value;
|
|
1889
|
+
continue;
|
|
1890
|
+
}
|
|
1891
|
+
index++;
|
|
1892
|
+
}
|
|
1893
|
+
return -1;
|
|
1894
|
+
};
|
|
1895
|
+
var isReadScript = (attributes, element4) => /application\/ld\+json/i.test(attributes) || element4.includes(PLAYER_RESPONSE_VARIABLE) || CHALLENGE_PATTERN.test(element4);
|
|
1430
1896
|
var LARGE_DOCUMENT_CHARS = 2e6;
|
|
1431
|
-
var stripScriptBodies = (html) =>
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
);
|
|
1897
|
+
var stripScriptBodies = (html) => {
|
|
1898
|
+
const out = [];
|
|
1899
|
+
let copied = 0;
|
|
1900
|
+
const start = new RegExp(SCRIPT_OR_STYLE_START);
|
|
1901
|
+
for (let match = start.exec(html); match; match = start.exec(html)) {
|
|
1902
|
+
if (match[1] === void 0) {
|
|
1903
|
+
const commentEnd = html.indexOf("-->", match.index + 4);
|
|
1904
|
+
if (commentEnd < 0) {
|
|
1905
|
+
break;
|
|
1906
|
+
}
|
|
1907
|
+
start.lastIndex = commentEnd + 3;
|
|
1908
|
+
continue;
|
|
1909
|
+
}
|
|
1910
|
+
const tag = match[1].toLowerCase();
|
|
1911
|
+
const bodyStart = startTagEnd(html, match.index + match[0].length);
|
|
1912
|
+
if (bodyStart < 0) {
|
|
1913
|
+
break;
|
|
1914
|
+
}
|
|
1915
|
+
const closing = CLOSING_TAG[tag];
|
|
1916
|
+
closing.lastIndex = bodyStart;
|
|
1917
|
+
const close = closing.exec(html);
|
|
1918
|
+
if (!close) {
|
|
1919
|
+
break;
|
|
1920
|
+
}
|
|
1921
|
+
const tagEnd = html.indexOf(">", close.index);
|
|
1922
|
+
const elementEnd = tagEnd < 0 ? html.length : tagEnd + 1;
|
|
1923
|
+
const element4 = html.slice(match.index, elementEnd);
|
|
1924
|
+
const openingTag = html.slice(match.index, bodyStart);
|
|
1925
|
+
if (!(tag === "script" && isReadScript(openingTag, element4))) {
|
|
1926
|
+
out.push(html.slice(copied, bodyStart), html.slice(close.index, elementEnd));
|
|
1927
|
+
copied = elementEnd;
|
|
1928
|
+
}
|
|
1929
|
+
start.lastIndex = elementEnd;
|
|
1930
|
+
}
|
|
1931
|
+
out.push(html.slice(copied));
|
|
1932
|
+
return out.join("");
|
|
1933
|
+
};
|
|
1435
1934
|
var parseHtml = (html, options = {}) => {
|
|
1436
1935
|
const source = html.length > LARGE_DOCUMENT_CHARS ? stripScriptBodies(html) : html;
|
|
1437
1936
|
const document2 = options.fragment ? parseFragment(source, { scriptingEnabled: false }) : parse(source, { scriptingEnabled: false });
|
|
@@ -1637,228 +2136,6 @@ var extractMdast = (node) => {
|
|
|
1637
2136
|
return extracted;
|
|
1638
2137
|
};
|
|
1639
2138
|
|
|
1640
|
-
// src/utils/hast-fast.ts
|
|
1641
|
-
var EMPTY_METRICS = { text: 0, link: 0, commas: 0, paragraphs: 0, elements: 0 };
|
|
1642
|
-
var COMMA_REGEX = /[,、,،]/g;
|
|
1643
|
-
var PARAGRAPH_LIKE = /* @__PURE__ */ new Set(["p", "pre", "li", "blockquote", "figcaption", "dd", "dt"]);
|
|
1644
|
-
var NON_PROSE_TAGS = /* @__PURE__ */ new Set(["script", "style", "noscript", "template", "svg", "math", "iframe", "object"]);
|
|
1645
|
-
var isElement = (node) => typeof node === "object" && node !== null && node.type === "element";
|
|
1646
|
-
var isParent2 = (node) => typeof node === "object" && node !== null && Array.isArray(node.children);
|
|
1647
|
-
var classList = (element4) => {
|
|
1648
|
-
const className = element4.properties?.className;
|
|
1649
|
-
if (Array.isArray(className)) {
|
|
1650
|
-
return className;
|
|
1651
|
-
}
|
|
1652
|
-
if (typeof className === "string") {
|
|
1653
|
-
return className.split(/\s+/).filter(Boolean);
|
|
1654
|
-
}
|
|
1655
|
-
return [];
|
|
1656
|
-
};
|
|
1657
|
-
var matchString = (element4) => {
|
|
1658
|
-
const id = element4.properties?.id;
|
|
1659
|
-
const classes = classList(element4);
|
|
1660
|
-
if (!id && classes.length === 0) {
|
|
1661
|
-
return element4.tagName;
|
|
1662
|
-
}
|
|
1663
|
-
return `${element4.tagName} ${typeof id === "string" ? id : ""} ${classes.join(" ")}`;
|
|
1664
|
-
};
|
|
1665
|
-
var MetricsCollector = class {
|
|
1666
|
-
#cache = /* @__PURE__ */ new WeakMap();
|
|
1667
|
-
metrics(node) {
|
|
1668
|
-
if (node.type === "text") {
|
|
1669
|
-
const value = node.value;
|
|
1670
|
-
if (value.trim().length === 0) {
|
|
1671
|
-
return EMPTY_METRICS;
|
|
1672
|
-
}
|
|
1673
|
-
return {
|
|
1674
|
-
text: value.length,
|
|
1675
|
-
link: 0,
|
|
1676
|
-
commas: (value.match(COMMA_REGEX) ?? []).length,
|
|
1677
|
-
paragraphs: 0,
|
|
1678
|
-
elements: 0
|
|
1679
|
-
};
|
|
1680
|
-
}
|
|
1681
|
-
if (!isParent2(node)) {
|
|
1682
|
-
return EMPTY_METRICS;
|
|
1683
|
-
}
|
|
1684
|
-
const cached = this.#cache.get(node);
|
|
1685
|
-
if (cached) {
|
|
1686
|
-
return cached;
|
|
1687
|
-
}
|
|
1688
|
-
if (isElement(node) && NON_PROSE_TAGS.has(node.tagName)) {
|
|
1689
|
-
this.#cache.set(node, EMPTY_METRICS);
|
|
1690
|
-
return EMPTY_METRICS;
|
|
1691
|
-
}
|
|
1692
|
-
let text7 = 0;
|
|
1693
|
-
let link = 0;
|
|
1694
|
-
let commas = 0;
|
|
1695
|
-
let paragraphs = 0;
|
|
1696
|
-
let elements = 0;
|
|
1697
|
-
for (const child of node.children) {
|
|
1698
|
-
const childMetrics = this.metrics(child);
|
|
1699
|
-
text7 += childMetrics.text;
|
|
1700
|
-
link += childMetrics.link;
|
|
1701
|
-
commas += childMetrics.commas;
|
|
1702
|
-
paragraphs += childMetrics.paragraphs;
|
|
1703
|
-
elements += childMetrics.elements;
|
|
1704
|
-
if (isElement(child)) {
|
|
1705
|
-
elements += 1;
|
|
1706
|
-
if (PARAGRAPH_LIKE.has(child.tagName)) {
|
|
1707
|
-
paragraphs += 1;
|
|
1708
|
-
}
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
if (isElement(node) && node.tagName === "a") {
|
|
1712
|
-
link = text7;
|
|
1713
|
-
}
|
|
1714
|
-
const metrics = { text: text7, link, commas, paragraphs, elements };
|
|
1715
|
-
this.#cache.set(node, metrics);
|
|
1716
|
-
return metrics;
|
|
1717
|
-
}
|
|
1718
|
-
textLength(node) {
|
|
1719
|
-
return this.metrics(node).text;
|
|
1720
|
-
}
|
|
1721
|
-
/** Fraction of a subtree's text that is link anchor text. `0` for empty subtrees. */
|
|
1722
|
-
linkDensity(node) {
|
|
1723
|
-
const { text: text7, link } = this.metrics(node);
|
|
1724
|
-
if (text7 === 0) {
|
|
1725
|
-
return 0;
|
|
1726
|
-
}
|
|
1727
|
-
return Math.min(1, link / text7);
|
|
1728
|
-
}
|
|
1729
|
-
};
|
|
1730
|
-
var pruneInPlace = (tree, keep) => {
|
|
1731
|
-
if (!isParent2(tree)) {
|
|
1732
|
-
return tree;
|
|
1733
|
-
}
|
|
1734
|
-
const stack = [tree];
|
|
1735
|
-
while (stack.length > 0) {
|
|
1736
|
-
const parent = stack.pop();
|
|
1737
|
-
const children = parent.children;
|
|
1738
|
-
let write = 0;
|
|
1739
|
-
for (let read = 0; read < children.length; read++) {
|
|
1740
|
-
const child = children[read];
|
|
1741
|
-
if (!keep(child, parent)) {
|
|
1742
|
-
continue;
|
|
1743
|
-
}
|
|
1744
|
-
children[write++] = child;
|
|
1745
|
-
if (isParent2(child)) {
|
|
1746
|
-
stack.push(child);
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
children.length = write;
|
|
1750
|
-
}
|
|
1751
|
-
return tree;
|
|
1752
|
-
};
|
|
1753
|
-
var walk = (tree, visit) => {
|
|
1754
|
-
const stack = [{ node: tree, parent: void 0 }];
|
|
1755
|
-
while (stack.length > 0) {
|
|
1756
|
-
const { node, parent } = stack.pop();
|
|
1757
|
-
if (visit(node, parent) === false) {
|
|
1758
|
-
continue;
|
|
1759
|
-
}
|
|
1760
|
-
if (isParent2(node)) {
|
|
1761
|
-
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
1762
|
-
stack.push({ node: node.children[i], parent: node });
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
};
|
|
1767
|
-
var collectElements = (tree, predicate) => {
|
|
1768
|
-
const found = [];
|
|
1769
|
-
walk(tree, (node) => {
|
|
1770
|
-
if (isElement(node) && predicate(node)) {
|
|
1771
|
-
found.push(node);
|
|
1772
|
-
}
|
|
1773
|
-
});
|
|
1774
|
-
return found;
|
|
1775
|
-
};
|
|
1776
|
-
var findElement = (tree, predicate) => {
|
|
1777
|
-
let found;
|
|
1778
|
-
walk(tree, (node) => {
|
|
1779
|
-
if (found) {
|
|
1780
|
-
return false;
|
|
1781
|
-
}
|
|
1782
|
-
if (isElement(node) && predicate(node)) {
|
|
1783
|
-
found = node;
|
|
1784
|
-
return false;
|
|
1785
|
-
}
|
|
1786
|
-
});
|
|
1787
|
-
return found;
|
|
1788
|
-
};
|
|
1789
|
-
var cloneHast = (tree) => {
|
|
1790
|
-
try {
|
|
1791
|
-
return structuredClone(tree);
|
|
1792
|
-
} catch {
|
|
1793
|
-
return manualClone(tree);
|
|
1794
|
-
}
|
|
1795
|
-
};
|
|
1796
|
-
var manualClone = (node) => {
|
|
1797
|
-
if (Array.isArray(node)) {
|
|
1798
|
-
return node.map(manualClone);
|
|
1799
|
-
}
|
|
1800
|
-
if (typeof node !== "object" || node === null) {
|
|
1801
|
-
return node;
|
|
1802
|
-
}
|
|
1803
|
-
const copy = {};
|
|
1804
|
-
for (const [key, value] of Object.entries(node)) {
|
|
1805
|
-
copy[key] = manualClone(value);
|
|
1806
|
-
}
|
|
1807
|
-
return copy;
|
|
1808
|
-
};
|
|
1809
|
-
var property = (element4, key) => {
|
|
1810
|
-
const properties = element4.properties;
|
|
1811
|
-
if (!properties) {
|
|
1812
|
-
return void 0;
|
|
1813
|
-
}
|
|
1814
|
-
const direct = properties[key];
|
|
1815
|
-
if (direct !== void 0) {
|
|
1816
|
-
return direct;
|
|
1817
|
-
}
|
|
1818
|
-
if (!(key.includes("-") || key.includes(":"))) {
|
|
1819
|
-
return void 0;
|
|
1820
|
-
}
|
|
1821
|
-
return properties[camelCaseAttribute(key)];
|
|
1822
|
-
};
|
|
1823
|
-
var camelCaseAttribute = (key) => key.replace(/[-:]([a-z])/g, (_, character) => character.toUpperCase());
|
|
1824
|
-
var numericProperty = (element4, key) => {
|
|
1825
|
-
const raw = property(element4, key);
|
|
1826
|
-
if (typeof raw === "number") {
|
|
1827
|
-
return Number.isFinite(raw) ? raw : void 0;
|
|
1828
|
-
}
|
|
1829
|
-
if (typeof raw === "string" && raw.length > 0) {
|
|
1830
|
-
const parsed = Number.parseFloat(raw);
|
|
1831
|
-
return Number.isFinite(parsed) ? parsed : void 0;
|
|
1832
|
-
}
|
|
1833
|
-
return void 0;
|
|
1834
|
-
};
|
|
1835
|
-
var stringProperty = (element4, key) => {
|
|
1836
|
-
const raw = property(element4, key);
|
|
1837
|
-
if (typeof raw === "string") {
|
|
1838
|
-
return raw;
|
|
1839
|
-
}
|
|
1840
|
-
if (Array.isArray(raw)) {
|
|
1841
|
-
return raw.join(" ");
|
|
1842
|
-
}
|
|
1843
|
-
if (raw === true) {
|
|
1844
|
-
return "";
|
|
1845
|
-
}
|
|
1846
|
-
return void 0;
|
|
1847
|
-
};
|
|
1848
|
-
var isTruthyAttribute = (element4, key) => {
|
|
1849
|
-
const raw = property(element4, key);
|
|
1850
|
-
if (raw === void 0 || raw === null || raw === false) {
|
|
1851
|
-
return false;
|
|
1852
|
-
}
|
|
1853
|
-
if (raw === true) {
|
|
1854
|
-
return true;
|
|
1855
|
-
}
|
|
1856
|
-
if (typeof raw === "string") {
|
|
1857
|
-
return raw.toLowerCase() !== "false";
|
|
1858
|
-
}
|
|
1859
|
-
return true;
|
|
1860
|
-
};
|
|
1861
|
-
|
|
1862
2139
|
// src/extractors/lib/code-tabs.ts
|
|
1863
2140
|
var MAX_LABEL_LENGTH = 80;
|
|
1864
2141
|
var CodeTabCollector = class {
|
|
@@ -2410,18 +2687,54 @@ var containsMath = (element4) => {
|
|
|
2410
2687
|
return true;
|
|
2411
2688
|
}
|
|
2412
2689
|
}
|
|
2413
|
-
return false;
|
|
2690
|
+
return false;
|
|
2691
|
+
};
|
|
2692
|
+
var collapsedText = (element4) => {
|
|
2693
|
+
let text7 = "";
|
|
2694
|
+
walk(element4, (node) => {
|
|
2695
|
+
if (node.type === "text") {
|
|
2696
|
+
text7 += node.value;
|
|
2697
|
+
}
|
|
2698
|
+
});
|
|
2699
|
+
return text7.replace(/\s+/g, " ").trim();
|
|
2700
|
+
};
|
|
2701
|
+
var visibleSiblingText = (parent, cache) => {
|
|
2702
|
+
let texts = cache.get(parent);
|
|
2703
|
+
if (!texts) {
|
|
2704
|
+
texts = /* @__PURE__ */ new Set();
|
|
2705
|
+
for (const child of parent.children) {
|
|
2706
|
+
if (isElement(child) && !isTruthyAttribute(child, "aria-hidden") && !looksHidden(child)) {
|
|
2707
|
+
texts.add(collapsedText(child));
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
cache.set(parent, texts);
|
|
2711
|
+
}
|
|
2712
|
+
return texts;
|
|
2713
|
+
};
|
|
2714
|
+
var isAriaHiddenDuplicate = (element4, parent, cache) => {
|
|
2715
|
+
if (!isTruthyAttribute(element4, "aria-hidden") || containsMath(element4)) {
|
|
2716
|
+
return false;
|
|
2717
|
+
}
|
|
2718
|
+
const text7 = collapsedText(element4);
|
|
2719
|
+
return text7.length > 0 && visibleSiblingText(parent, cache).has(text7);
|
|
2414
2720
|
};
|
|
2415
2721
|
var stripNonContent = (tree) => {
|
|
2416
2722
|
const tabs = new CodeTabCollector((element4) => looksHidden(element4) || NON_CONTENT_TAGS.has(element4.tagName));
|
|
2417
|
-
|
|
2723
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
2724
|
+
const siblingText = /* @__PURE__ */ new WeakMap();
|
|
2725
|
+
hoistNoscriptImages(tree, (element4, depth, parent) => {
|
|
2726
|
+
tabs.visit(element4, depth, parent);
|
|
2727
|
+
if (isAriaHiddenDuplicate(element4, parent, siblingText)) {
|
|
2728
|
+
duplicates.add(element4);
|
|
2729
|
+
}
|
|
2730
|
+
});
|
|
2418
2731
|
tabs.apply();
|
|
2419
2732
|
const siblingCode = /* @__PURE__ */ new WeakMap();
|
|
2420
2733
|
return pruneInPlace(tree, (node, parent) => {
|
|
2421
2734
|
if (isNonContent(node)) {
|
|
2422
2735
|
return false;
|
|
2423
2736
|
}
|
|
2424
|
-
if (isElement(node) && isHidden(node, parent, siblingCode)) {
|
|
2737
|
+
if (isElement(node) && (duplicates.has(node) || isHidden(node, parent, siblingCode))) {
|
|
2425
2738
|
return false;
|
|
2426
2739
|
}
|
|
2427
2740
|
return true;
|
|
@@ -3057,8 +3370,8 @@ var DECLARATIVE_ADAPTERS = SPECS.map(defineSelectorAdapter);
|
|
|
3057
3370
|
// src/adapters/sites/hackernews.ts
|
|
3058
3371
|
import { select as select4, selectAll as selectAll2 } from "hast-util-select";
|
|
3059
3372
|
var MAX_QUOTE_DEPTH = 6;
|
|
3060
|
-
var
|
|
3061
|
-
var
|
|
3373
|
+
var text4 = (value) => ({ type: "text", value });
|
|
3374
|
+
var element2 = (tagName, children) => ({
|
|
3062
3375
|
type: "element",
|
|
3063
3376
|
tagName,
|
|
3064
3377
|
properties: {},
|
|
@@ -3078,25 +3391,25 @@ var commentDepth = (row) => {
|
|
|
3078
3391
|
return width === void 0 ? 0 : Math.round(width / 40);
|
|
3079
3392
|
};
|
|
3080
3393
|
var nest = (content, depth) => {
|
|
3081
|
-
let current = content;
|
|
3394
|
+
let current = element2("div", content);
|
|
3082
3395
|
for (let level = 0; level < Math.min(depth, MAX_QUOTE_DEPTH); level++) {
|
|
3083
|
-
current =
|
|
3396
|
+
current = element2("blockquote", [current]);
|
|
3084
3397
|
}
|
|
3085
|
-
return current
|
|
3398
|
+
return current;
|
|
3086
3399
|
};
|
|
3087
3400
|
var storyNodes = (root) => {
|
|
3088
3401
|
const nodes = [];
|
|
3089
3402
|
const titleLink = select4(".titleline > a", root);
|
|
3090
3403
|
if (titleLink) {
|
|
3091
|
-
nodes.push(
|
|
3404
|
+
nodes.push(element2("h1", [...titleLink.children]));
|
|
3092
3405
|
const href = stringProperty(titleLink, "href");
|
|
3093
3406
|
if (href && /^https?:/.test(href)) {
|
|
3094
|
-
nodes.push(
|
|
3407
|
+
nodes.push(element2("p", [{ ...titleLink, children: [text4(href)] }]));
|
|
3095
3408
|
}
|
|
3096
3409
|
}
|
|
3097
3410
|
const topText = select4(".toptext", root);
|
|
3098
3411
|
if (topText) {
|
|
3099
|
-
nodes.push(
|
|
3412
|
+
nodes.push(element2("div", [...topText.children]));
|
|
3100
3413
|
}
|
|
3101
3414
|
return nodes;
|
|
3102
3415
|
};
|
|
@@ -3108,10 +3421,10 @@ var commentNodes = (root) => {
|
|
|
3108
3421
|
continue;
|
|
3109
3422
|
}
|
|
3110
3423
|
const author = select4(".hnuser", row);
|
|
3111
|
-
const header = author ? [
|
|
3424
|
+
const header = author ? [element2("strong", [...author.children])] : [];
|
|
3112
3425
|
const content = [
|
|
3113
|
-
...header.length > 0 ? [
|
|
3114
|
-
|
|
3426
|
+
...header.length > 0 ? [element2("p", header)] : [],
|
|
3427
|
+
element2("div", [...body.children])
|
|
3115
3428
|
];
|
|
3116
3429
|
nodes.push(nest(content, commentDepth(row)));
|
|
3117
3430
|
}
|
|
@@ -3138,171 +3451,13 @@ var hackernewsAdapter = {
|
|
|
3138
3451
|
}
|
|
3139
3452
|
const children = [...story];
|
|
3140
3453
|
if (comments.length > 0) {
|
|
3141
|
-
children.push(
|
|
3454
|
+
children.push(element2("h2", [text4("Comments")]));
|
|
3142
3455
|
children.push(...comments);
|
|
3143
3456
|
}
|
|
3144
3457
|
return { type: "root", children };
|
|
3145
3458
|
}
|
|
3146
3459
|
};
|
|
3147
3460
|
|
|
3148
|
-
// src/adapters/sites/youtube.ts
|
|
3149
|
-
var findObjectEnd = (source, start) => {
|
|
3150
|
-
let depth = 0;
|
|
3151
|
-
let inString = false;
|
|
3152
|
-
let escaped = false;
|
|
3153
|
-
for (let index = start; index < source.length; index++) {
|
|
3154
|
-
const character = source[index];
|
|
3155
|
-
if (escaped) {
|
|
3156
|
-
escaped = false;
|
|
3157
|
-
} else if (character === "\\") {
|
|
3158
|
-
escaped = true;
|
|
3159
|
-
} else if (character === '"') {
|
|
3160
|
-
inString = !inString;
|
|
3161
|
-
} else if (!inString) {
|
|
3162
|
-
if (character === "{") {
|
|
3163
|
-
depth += 1;
|
|
3164
|
-
} else if (character === "}" && --depth === 0) {
|
|
3165
|
-
return index + 1;
|
|
3166
|
-
}
|
|
3167
|
-
}
|
|
3168
|
-
}
|
|
3169
|
-
return -1;
|
|
3170
|
-
};
|
|
3171
|
-
var extractAssignedJson = (source, name) => {
|
|
3172
|
-
const markerIndex = source.indexOf(`${name} =`);
|
|
3173
|
-
if (markerIndex === -1) {
|
|
3174
|
-
return void 0;
|
|
3175
|
-
}
|
|
3176
|
-
const start = source.indexOf("{", markerIndex);
|
|
3177
|
-
if (start === -1) {
|
|
3178
|
-
return void 0;
|
|
3179
|
-
}
|
|
3180
|
-
const end = findObjectEnd(source, start);
|
|
3181
|
-
if (end === -1) {
|
|
3182
|
-
return void 0;
|
|
3183
|
-
}
|
|
3184
|
-
try {
|
|
3185
|
-
return JSON.parse(source.slice(start, end));
|
|
3186
|
-
} catch {
|
|
3187
|
-
return void 0;
|
|
3188
|
-
}
|
|
3189
|
-
};
|
|
3190
|
-
var scriptText = (root) => collectElements(root, (element4) => element4.tagName === "script").map(
|
|
3191
|
-
(script) => script.children.map((child) => child.type === "text" ? child.value : "").join("")
|
|
3192
|
-
);
|
|
3193
|
-
var findPlayerResponse = (root) => {
|
|
3194
|
-
for (const source of scriptText(root)) {
|
|
3195
|
-
if (!source.includes("ytInitialPlayerResponse")) {
|
|
3196
|
-
continue;
|
|
3197
|
-
}
|
|
3198
|
-
const parsed = extractAssignedJson(source, "ytInitialPlayerResponse");
|
|
3199
|
-
if (parsed && typeof parsed === "object") {
|
|
3200
|
-
return parsed;
|
|
3201
|
-
}
|
|
3202
|
-
}
|
|
3203
|
-
return void 0;
|
|
3204
|
-
};
|
|
3205
|
-
var youtubeCaptionTracks = (root) => {
|
|
3206
|
-
const player = findPlayerResponse(root);
|
|
3207
|
-
const renderer = player?.captions?.playerCaptionsTracklistRenderer;
|
|
3208
|
-
const tracks = renderer?.captionTracks;
|
|
3209
|
-
if (!Array.isArray(tracks)) {
|
|
3210
|
-
return [];
|
|
3211
|
-
}
|
|
3212
|
-
return tracks.flatMap((track) => {
|
|
3213
|
-
const url = typeof track.baseUrl === "string" ? track.baseUrl : void 0;
|
|
3214
|
-
const languageCode = typeof track.languageCode === "string" ? track.languageCode : void 0;
|
|
3215
|
-
if (!(url && languageCode)) {
|
|
3216
|
-
return [];
|
|
3217
|
-
}
|
|
3218
|
-
const name = track.name?.simpleText;
|
|
3219
|
-
return [
|
|
3220
|
-
{
|
|
3221
|
-
url,
|
|
3222
|
-
languageCode,
|
|
3223
|
-
name: typeof name === "string" ? name : void 0,
|
|
3224
|
-
isAutomatic: track.kind === "asr"
|
|
3225
|
-
}
|
|
3226
|
-
];
|
|
3227
|
-
});
|
|
3228
|
-
};
|
|
3229
|
-
var text4 = (value) => ({ type: "text", value });
|
|
3230
|
-
var element2 = (tagName, children) => ({
|
|
3231
|
-
type: "element",
|
|
3232
|
-
tagName,
|
|
3233
|
-
properties: {},
|
|
3234
|
-
children
|
|
3235
|
-
});
|
|
3236
|
-
var heading = (depth, value) => element2(`h${depth}`, [text4(value)]);
|
|
3237
|
-
var paragraph = (value) => element2("p", [text4(value)]);
|
|
3238
|
-
var descriptionNodes = (description) => description.split(/\n{2,}/).map((block) => block.trim()).filter((block) => block.length > 0).map(
|
|
3239
|
-
(block) => element2(
|
|
3240
|
-
"p",
|
|
3241
|
-
block.split("\n").flatMap((line, index) => index === 0 ? [text4(line)] : [element2("br", []), text4(line)])
|
|
3242
|
-
)
|
|
3243
|
-
);
|
|
3244
|
-
var formatDuration = (seconds) => {
|
|
3245
|
-
const hours = Math.floor(seconds / 3600);
|
|
3246
|
-
const minutes = Math.floor(seconds % 3600 / 60);
|
|
3247
|
-
const remainder = seconds % 60;
|
|
3248
|
-
const pad = (value) => String(value).padStart(2, "0");
|
|
3249
|
-
return hours > 0 ? `${hours}:${pad(minutes)}:${pad(remainder)}` : `${minutes}:${pad(remainder)}`;
|
|
3250
|
-
};
|
|
3251
|
-
var metadataList = (details) => {
|
|
3252
|
-
const items = [];
|
|
3253
|
-
if (details.author) {
|
|
3254
|
-
items.push(element2("li", [text4(`Channel: ${details.author}`)]));
|
|
3255
|
-
}
|
|
3256
|
-
const seconds = Number(details.lengthSeconds);
|
|
3257
|
-
if (Number.isFinite(seconds) && seconds > 0) {
|
|
3258
|
-
items.push(element2("li", [text4(`Duration: ${formatDuration(seconds)}`)]));
|
|
3259
|
-
}
|
|
3260
|
-
const views = Number(details.viewCount);
|
|
3261
|
-
if (Number.isFinite(views) && views > 0) {
|
|
3262
|
-
items.push(element2("li", [text4(`Views: ${views.toLocaleString("en-US")}`)]));
|
|
3263
|
-
}
|
|
3264
|
-
return items.length > 0 ? element2("ul", items) : void 0;
|
|
3265
|
-
};
|
|
3266
|
-
var youtubeAdapter = {
|
|
3267
|
-
id: "youtube",
|
|
3268
|
-
matches: ({ url }) => {
|
|
3269
|
-
if (!url) {
|
|
3270
|
-
return false;
|
|
3271
|
-
}
|
|
3272
|
-
try {
|
|
3273
|
-
return /(^|\.)(youtube\.com|youtu\.be|youtube-nocookie\.com)$/.test(new URL(url).hostname);
|
|
3274
|
-
} catch {
|
|
3275
|
-
return false;
|
|
3276
|
-
}
|
|
3277
|
-
},
|
|
3278
|
-
// Scanning every script body for the JSON blob would cost a full traversal on every page, so
|
|
3279
|
-
// the fallback keys off markers the signature already collected.
|
|
3280
|
-
matchesFingerprint: ({ signature }) => Boolean(signature?.ids.has("movie_player") || signature?.classes.has("ytd-app")),
|
|
3281
|
-
extract: ({ hast }) => {
|
|
3282
|
-
const player = findPlayerResponse(hast);
|
|
3283
|
-
const details = player?.videoDetails;
|
|
3284
|
-
if (!details?.title) {
|
|
3285
|
-
return void 0;
|
|
3286
|
-
}
|
|
3287
|
-
const children = [heading(1, details.title)];
|
|
3288
|
-
const metadata = metadataList(details);
|
|
3289
|
-
if (metadata) {
|
|
3290
|
-
children.push(metadata);
|
|
3291
|
-
}
|
|
3292
|
-
const description = details.shortDescription?.trim();
|
|
3293
|
-
if (description) {
|
|
3294
|
-
children.push(heading(2, "Description"));
|
|
3295
|
-
children.push(...descriptionNodes(description));
|
|
3296
|
-
}
|
|
3297
|
-
const tracks = youtubeCaptionTracks(hast);
|
|
3298
|
-
if (tracks.length > 0) {
|
|
3299
|
-
const languages2 = [...new Set(tracks.map((track) => track.languageCode))].sort();
|
|
3300
|
-
children.push(paragraph(`Captions available: ${languages2.join(", ")}`));
|
|
3301
|
-
}
|
|
3302
|
-
return { type: "root", children };
|
|
3303
|
-
}
|
|
3304
|
-
};
|
|
3305
|
-
|
|
3306
3461
|
// src/adapters/index.ts
|
|
3307
3462
|
var BUILTIN_ADAPTERS = [youtubeAdapter, hackernewsAdapter, ...DECLARATIVE_ADAPTERS];
|
|
3308
3463
|
var MIN_ADAPTER_TEXT = 40;
|
|
@@ -4478,10 +4633,51 @@ var DATA_MARKERS = /* @__PURE__ */ new Set(["caption", "thead", "tfoot", "th", "
|
|
|
4478
4633
|
var BLOCK_IN_CELL = /* @__PURE__ */ new Set(["h1", "h2", "h3", "h4", "h5", "h6", "table", "form"]);
|
|
4479
4634
|
var TABLE_PARTS = /* @__PURE__ */ new Set(["tbody", "thead", "tfoot", "tr", "td", "th"]);
|
|
4480
4635
|
var TABLE_CONTEXT = /* @__PURE__ */ new Set(["table", "tbody", "thead", "tfoot", "tr"]);
|
|
4636
|
+
var MAX_SMALL_TABLE_CELL = 150;
|
|
4637
|
+
var MAX_SMALL_TABLE_LINK_SHARE = 0.5;
|
|
4638
|
+
var collapsedLength = (text7) => text7.replace(/\s+/g, " ").trim().length;
|
|
4639
|
+
var cellText = (cell) => {
|
|
4640
|
+
let text7 = "";
|
|
4641
|
+
let linkText = "";
|
|
4642
|
+
const visit = (node, inLink) => {
|
|
4643
|
+
for (const child of node.children) {
|
|
4644
|
+
if (child.type === "text") {
|
|
4645
|
+
text7 += child.value;
|
|
4646
|
+
if (inLink) {
|
|
4647
|
+
linkText += child.value;
|
|
4648
|
+
}
|
|
4649
|
+
} else if (isElement(child) && child.tagName !== "table") {
|
|
4650
|
+
visit(child, inLink || child.tagName === "a");
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4653
|
+
};
|
|
4654
|
+
visit(cell, false);
|
|
4655
|
+
return { text: collapsedLength(text7), linkText: collapsedLength(linkText) };
|
|
4656
|
+
};
|
|
4481
4657
|
var cellsOf = (row) => row.children.filter((cell) => isElement(cell) && (cell.tagName === "td" || cell.tagName === "th"));
|
|
4482
4658
|
var HEADING = /^h[1-6]$/;
|
|
4659
|
+
var measureRow = (shape, row) => {
|
|
4660
|
+
const cells = cellsOf(row);
|
|
4661
|
+
shape.rows += 1;
|
|
4662
|
+
shape.columns = Math.max(shape.columns, cells.length);
|
|
4663
|
+
for (const cell of cells) {
|
|
4664
|
+
const { text: text7, linkText } = cellText(cell);
|
|
4665
|
+
shape.longestCell = Math.max(shape.longestCell, text7);
|
|
4666
|
+
shape.text += text7;
|
|
4667
|
+
shape.linkText += linkText;
|
|
4668
|
+
}
|
|
4669
|
+
return cells.length === 1;
|
|
4670
|
+
};
|
|
4483
4671
|
var shapeOf = (table) => {
|
|
4484
|
-
const shape = {
|
|
4672
|
+
const shape = {
|
|
4673
|
+
rows: 0,
|
|
4674
|
+
columns: 0,
|
|
4675
|
+
dataMarker: false,
|
|
4676
|
+
blockInCell: false,
|
|
4677
|
+
longestCell: 0,
|
|
4678
|
+
text: 0,
|
|
4679
|
+
linkText: 0
|
|
4680
|
+
};
|
|
4485
4681
|
let captionHeadings = 0;
|
|
4486
4682
|
const noteBlockInCell = (tag, aloneInRow) => {
|
|
4487
4683
|
if (HEADING.test(tag) && aloneInRow) {
|
|
@@ -4503,13 +4699,7 @@ var shapeOf = (table) => {
|
|
|
4503
4699
|
if (DATA_MARKERS.has(tag)) {
|
|
4504
4700
|
shape.dataMarker = true;
|
|
4505
4701
|
}
|
|
4506
|
-
|
|
4507
|
-
if (tag === "tr") {
|
|
4508
|
-
shape.rows += 1;
|
|
4509
|
-
const cells = cellsOf(child).length;
|
|
4510
|
-
shape.columns = Math.max(shape.columns, cells);
|
|
4511
|
-
alone = cells === 1;
|
|
4512
|
-
}
|
|
4702
|
+
const alone = tag === "tr" ? measureRow(shape, child) : aloneInRow;
|
|
4513
4703
|
if (inCell && BLOCK_IN_CELL.has(tag)) {
|
|
4514
4704
|
noteBlockInCell(tag, aloneInRow);
|
|
4515
4705
|
}
|
|
@@ -4540,7 +4730,11 @@ var isLayoutTable = (table) => {
|
|
|
4540
4730
|
if (shape.rows >= 10 || shape.columns > 4) {
|
|
4541
4731
|
return false;
|
|
4542
4732
|
}
|
|
4543
|
-
|
|
4733
|
+
if (shape.rows * shape.columns > 10) {
|
|
4734
|
+
return false;
|
|
4735
|
+
}
|
|
4736
|
+
const linkShare = shape.text === 0 ? 1 : shape.linkText / shape.text;
|
|
4737
|
+
return shape.longestCell > MAX_SMALL_TABLE_CELL || linkShare > MAX_SMALL_TABLE_LINK_SHARE;
|
|
4544
4738
|
};
|
|
4545
4739
|
var unwrap = (table) => {
|
|
4546
4740
|
const cells = [];
|
|
@@ -4826,6 +5020,8 @@ var NEURAL_MODEL = { "kind": "block-gru", "buckets": 1024, "emb": 16, "hidden":
|
|
|
4826
5020
|
var TERMINATOR_PATTERNS = [
|
|
4827
5021
|
/^was this (page|article|document) helpful/i,
|
|
4828
5022
|
/^thanks for the feedback/i,
|
|
5023
|
+
// Site-generator credit closing the page ("Powered by Docsify.js", "Powered by GitBook").
|
|
5024
|
+
/^powered by\b/i,
|
|
4829
5025
|
/^(this page was )?last (modified|updated) (on|at|:)?\s/i,
|
|
4830
5026
|
/^learn how to contribute/i,
|
|
4831
5027
|
/^help (us )?improve\b/i,
|
|
@@ -4855,7 +5051,7 @@ var textOf3 = (node) => {
|
|
|
4855
5051
|
if (node.type === "text") {
|
|
4856
5052
|
return node.value;
|
|
4857
5053
|
}
|
|
4858
|
-
if (!
|
|
5054
|
+
if (!isParent(node)) {
|
|
4859
5055
|
return "";
|
|
4860
5056
|
}
|
|
4861
5057
|
let text7 = "";
|
|
@@ -4873,7 +5069,7 @@ var truncateTrailingBoilerplate = (tree) => {
|
|
|
4873
5069
|
total += node.value.trim().length;
|
|
4874
5070
|
return;
|
|
4875
5071
|
}
|
|
4876
|
-
if (
|
|
5072
|
+
if (isParent(node)) {
|
|
4877
5073
|
for (const child of node.children) {
|
|
4878
5074
|
measure(child);
|
|
4879
5075
|
}
|
|
@@ -4886,7 +5082,7 @@ var truncateTrailingBoilerplate = (tree) => {
|
|
|
4886
5082
|
const tailStart = total * TAIL_START_FRACTION;
|
|
4887
5083
|
let cutoff = -1;
|
|
4888
5084
|
const find = (node) => {
|
|
4889
|
-
if (cutoff !== -1 || !
|
|
5085
|
+
if (cutoff !== -1 || !isParent(node)) {
|
|
4890
5086
|
return;
|
|
4891
5087
|
}
|
|
4892
5088
|
for (const child of node.children) {
|
|
@@ -5846,11 +6042,10 @@ var languages = [
|
|
|
5846
6042
|
["json", [/\b(true|false|null|\{})\b|\"[^"]+\":/g, 10]],
|
|
5847
6043
|
["yaml", [/^(\s+)?[a-z][a-z0-9]*:/gim, 10]]
|
|
5848
6044
|
];
|
|
5849
|
-
var
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
};
|
|
6045
|
+
var MIN_CONFIDENT_SCORE = 400;
|
|
6046
|
+
var detectLanguage = (code) => languages.map(
|
|
6047
|
+
([lang, ...features]) => [lang, features.reduce((acc, [match, score]) => acc + [...code.matchAll(match)].length * score, 0)]
|
|
6048
|
+
).filter(([_, score]) => score >= MIN_CONFIDENT_SCORE).sort((a, b) => b[1] - a[1])[0]?.[0];
|
|
5854
6049
|
|
|
5855
6050
|
// src/utils/pre-text.ts
|
|
5856
6051
|
var LINE_BLOCK_TAGS = /* @__PURE__ */ new Set([
|
|
@@ -5977,17 +6172,46 @@ var errorLine = (error) => {
|
|
|
5977
6172
|
|
|
5978
6173
|
// src/mdast-handlers/custom-code-handler.ts
|
|
5979
6174
|
var LANGUAGE_MATCH_REGEX = [/^language-(\S+)$/, /^highlight-source-(\S+)$/, /^CodeBlock--language-(\S+)$/];
|
|
6175
|
+
var LANGUAGE_TOKEN = /^[\w+#.-]+$/;
|
|
6176
|
+
var NO_LANGUAGE = /* @__PURE__ */ new Set(["plain", "plaintext", "text", "none", "nohighlight"]);
|
|
5980
6177
|
var LANGUAGE_ATTRIBUTES = ["dataLanguage", "dataLang"];
|
|
5981
6178
|
var attributeLanguage = (element4) => {
|
|
5982
6179
|
for (const name of LANGUAGE_ATTRIBUTES) {
|
|
5983
6180
|
const value = element4.properties?.[name];
|
|
5984
|
-
if (typeof value === "string" &&
|
|
6181
|
+
if (typeof value === "string" && LANGUAGE_TOKEN.test(value.trim()) && value.trim() !== "plaintext") {
|
|
5985
6182
|
return value.trim();
|
|
5986
6183
|
}
|
|
5987
6184
|
}
|
|
5988
6185
|
return void 0;
|
|
5989
6186
|
};
|
|
5990
|
-
var
|
|
6187
|
+
var SANDPACK_LANGUAGES = /* @__PURE__ */ new Set([
|
|
6188
|
+
"javascript",
|
|
6189
|
+
"typescript",
|
|
6190
|
+
"jsx",
|
|
6191
|
+
"tsx",
|
|
6192
|
+
"html",
|
|
6193
|
+
"css",
|
|
6194
|
+
"less",
|
|
6195
|
+
"scss",
|
|
6196
|
+
"sass",
|
|
6197
|
+
"json",
|
|
6198
|
+
"markdown",
|
|
6199
|
+
"python",
|
|
6200
|
+
"vue",
|
|
6201
|
+
"svelte"
|
|
6202
|
+
]);
|
|
6203
|
+
var conventionLanguage = (element4) => {
|
|
6204
|
+
const classes = classList(element4);
|
|
6205
|
+
const brushAt = classes.findIndex((name) => name.startsWith("brush:"));
|
|
6206
|
+
if (brushAt >= 0) {
|
|
6207
|
+
const brush = classes[brushAt] === "brush:" ? classes[brushAt + 1] : classes[brushAt].slice("brush:".length);
|
|
6208
|
+
const lang = brush?.toLowerCase();
|
|
6209
|
+
return lang && LANGUAGE_TOKEN.test(lang) && !NO_LANGUAGE.has(lang) ? lang : void 0;
|
|
6210
|
+
}
|
|
6211
|
+
const sandpack = classes.find((name) => name.startsWith("sp-") && SANDPACK_LANGUAGES.has(name.slice(3)));
|
|
6212
|
+
return sandpack?.slice(3);
|
|
6213
|
+
};
|
|
6214
|
+
var codeLanguage = (...elements) => elements.flatMap(classList).map((className) => LANGUAGE_MATCH_REGEX.map((regex) => className.match(regex)).find(Boolean)?.[1]).find(Boolean) ?? elements.map(attributeLanguage).find(Boolean) ?? elements.map(conventionLanguage).find(Boolean);
|
|
5991
6215
|
var codeMeta = (pre) => {
|
|
5992
6216
|
const title = pre.properties?.dataTitle;
|
|
5993
6217
|
if (typeof title !== "string") {
|
|
@@ -6568,6 +6792,82 @@ var removeIconFonts = (tree) => {
|
|
|
6568
6792
|
pruneInPlace(tree, (node) => !isIcon(node));
|
|
6569
6793
|
}
|
|
6570
6794
|
};
|
|
6795
|
+
var textOfNode = (node) => node.type === "text" ? node.value : "children" in node ? node.children.map(textOfNode).join("") : "";
|
|
6796
|
+
var unwrapHeadingAnchors = (tree) => {
|
|
6797
|
+
walk(tree, (node) => {
|
|
6798
|
+
if (!(isElement(node) && /^h[1-6]$/.test(node.tagName))) {
|
|
6799
|
+
return;
|
|
6800
|
+
}
|
|
6801
|
+
const title = textOfNode(node).trim();
|
|
6802
|
+
if (title === "") {
|
|
6803
|
+
return;
|
|
6804
|
+
}
|
|
6805
|
+
const anchorAt = node.children.findIndex(
|
|
6806
|
+
(child) => isElement(child) && child.tagName === "a" && stringProperty(child, "href")?.startsWith("#") && textOfNode(child).trim() === title
|
|
6807
|
+
);
|
|
6808
|
+
const anchor = node.children[anchorAt];
|
|
6809
|
+
if (isElement(anchor)) {
|
|
6810
|
+
node.children.splice(anchorAt, 1, ...anchor.children);
|
|
6811
|
+
}
|
|
6812
|
+
});
|
|
6813
|
+
};
|
|
6814
|
+
var LABEL_LANGUAGES = /* @__PURE__ */ new Set([
|
|
6815
|
+
"js",
|
|
6816
|
+
"javascript",
|
|
6817
|
+
"ts",
|
|
6818
|
+
"typescript",
|
|
6819
|
+
"jsx",
|
|
6820
|
+
"tsx",
|
|
6821
|
+
"html",
|
|
6822
|
+
"css",
|
|
6823
|
+
"scss",
|
|
6824
|
+
"json",
|
|
6825
|
+
"bash",
|
|
6826
|
+
"sh",
|
|
6827
|
+
"shell",
|
|
6828
|
+
"console",
|
|
6829
|
+
"python",
|
|
6830
|
+
"py",
|
|
6831
|
+
"sql",
|
|
6832
|
+
"xml",
|
|
6833
|
+
"yaml",
|
|
6834
|
+
"toml",
|
|
6835
|
+
"http",
|
|
6836
|
+
"rust",
|
|
6837
|
+
"go",
|
|
6838
|
+
"java",
|
|
6839
|
+
"kotlin",
|
|
6840
|
+
"swift",
|
|
6841
|
+
"c",
|
|
6842
|
+
"cpp",
|
|
6843
|
+
"md",
|
|
6844
|
+
"markdown",
|
|
6845
|
+
"diff"
|
|
6846
|
+
]);
|
|
6847
|
+
var LABEL_NO_LANGUAGE = /* @__PURE__ */ new Set(["plain", "plaintext", "text"]);
|
|
6848
|
+
var foldCodeLabels = (tree) => {
|
|
6849
|
+
walk(tree, (node) => {
|
|
6850
|
+
if (!isElement(node) || node.tagName === "pre") {
|
|
6851
|
+
return;
|
|
6852
|
+
}
|
|
6853
|
+
const elements = node.children.filter(isElement);
|
|
6854
|
+
if (elements.length !== 2 || node.children.some((child) => child.type === "text" && child.value.trim() !== "")) {
|
|
6855
|
+
return;
|
|
6856
|
+
}
|
|
6857
|
+
const [label, pre] = elements;
|
|
6858
|
+
if (pre.tagName !== "pre" || /^(h[1-6]|pre)$/.test(label.tagName) || findElement(label, (el) => el.tagName === "pre")) {
|
|
6859
|
+
return;
|
|
6860
|
+
}
|
|
6861
|
+
const name = textOfNode(label).trim().toLowerCase();
|
|
6862
|
+
if (!(LABEL_LANGUAGES.has(name) || LABEL_NO_LANGUAGE.has(name))) {
|
|
6863
|
+
return;
|
|
6864
|
+
}
|
|
6865
|
+
if (LABEL_LANGUAGES.has(name) && pre.properties.dataLanguage === void 0) {
|
|
6866
|
+
pre.properties.dataLanguage = name;
|
|
6867
|
+
}
|
|
6868
|
+
node.children = node.children.filter((child) => child !== label);
|
|
6869
|
+
});
|
|
6870
|
+
};
|
|
6571
6871
|
var SPACED_INLINE = /* @__PURE__ */ new Set(["span", "time", "label"]);
|
|
6572
6872
|
var SCRIPTS = [
|
|
6573
6873
|
["latin", /\p{Script=Latin}/u],
|
|
@@ -6584,7 +6884,6 @@ var needsSpace = (left, right) => {
|
|
|
6584
6884
|
return a !== void 0 && b !== void 0 && a !== b;
|
|
6585
6885
|
};
|
|
6586
6886
|
var NO_SPACING = /* @__PURE__ */ new Set(["pre", "code", "kbd", "samp", "ruby", "math", "svg"]);
|
|
6587
|
-
var textOfNode = (node) => node.type === "text" ? node.value : "children" in node ? node.children.map(textOfNode).join("") : "";
|
|
6588
6887
|
var separateAdjacentInlines = (tree) => {
|
|
6589
6888
|
const visit = (node) => {
|
|
6590
6889
|
if (!("children" in node) || isElement(node) && NO_SPACING.has(node.tagName)) {
|
|
@@ -6605,7 +6904,15 @@ var separateAdjacentInlines = (tree) => {
|
|
|
6605
6904
|
visit(tree);
|
|
6606
6905
|
};
|
|
6607
6906
|
var normalizeHast = (tree, options = {}) => {
|
|
6608
|
-
const {
|
|
6907
|
+
const {
|
|
6908
|
+
images = true,
|
|
6909
|
+
math = true,
|
|
6910
|
+
ariaHeadings = true,
|
|
6911
|
+
iconFonts = true,
|
|
6912
|
+
headingAnchors = true,
|
|
6913
|
+
codeLabels = true,
|
|
6914
|
+
inlineSpacing = true
|
|
6915
|
+
} = options;
|
|
6609
6916
|
if (images) {
|
|
6610
6917
|
resolveImageSources(tree);
|
|
6611
6918
|
}
|
|
@@ -6618,6 +6925,12 @@ var normalizeHast = (tree, options = {}) => {
|
|
|
6618
6925
|
if (iconFonts) {
|
|
6619
6926
|
removeIconFonts(tree);
|
|
6620
6927
|
}
|
|
6928
|
+
if (headingAnchors) {
|
|
6929
|
+
unwrapHeadingAnchors(tree);
|
|
6930
|
+
}
|
|
6931
|
+
if (codeLabels) {
|
|
6932
|
+
foldCodeLabels(tree);
|
|
6933
|
+
}
|
|
6621
6934
|
if (inlineSpacing) {
|
|
6622
6935
|
separateAdjacentInlines(tree);
|
|
6623
6936
|
}
|
|
@@ -7030,55 +7343,6 @@ var mentionsTitle = (opening, title) => {
|
|
|
7030
7343
|
};
|
|
7031
7344
|
var normalizeForCompare = (value) => value.replace(/\s+/g, " ").trim().toLowerCase();
|
|
7032
7345
|
|
|
7033
|
-
// src/detect-client-shell.ts
|
|
7034
|
-
var SHELL_SUSPICION_THRESHOLD = 300;
|
|
7035
|
-
var SHELL_EMPTY_THRESHOLD = 50;
|
|
7036
|
-
var BODY_PATTERN = /<body[^>]*>([\s\S]*?)<\/body>/i;
|
|
7037
|
-
var HEAD_PATTERN = /<head[^>]*>[\s\S]*?<\/head>/gi;
|
|
7038
|
-
var NOSCRIPT_PATTERN = /<noscript[^>]*>([\s\S]*?)<\/noscript>/gi;
|
|
7039
|
-
var INVISIBLE_ELEMENT_PATTERN = /<(script|style|template|svg|noscript)[^>]*>[\s\S]*?<\/\1>/gi;
|
|
7040
|
-
var COMMENT_PATTERN = /<!--[\s\S]*?-->/g;
|
|
7041
|
-
var TAG_PATTERN = /<[^>]+>/g;
|
|
7042
|
-
var MOUNT_POINT_PATTERN = /<[a-z][^>]*\sid=["'](?:root|app|__next|___gatsby|__nuxt|q-app|svelte|react-root)["']|<app-root[\s>]|\sdata-reactroot[\s>=]/i;
|
|
7043
|
-
var CHALLENGE_PATTERN = /cf-chl|challenge-platform|cf_chl_opt|just a moment|attention required|checking your browser|_incapsula_|px-captcha|datadome/i;
|
|
7044
|
-
var ENTITIES = {
|
|
7045
|
-
" ": " ",
|
|
7046
|
-
"&": "&",
|
|
7047
|
-
"<": "<",
|
|
7048
|
-
">": ">",
|
|
7049
|
-
""": '"',
|
|
7050
|
-
"'": "'"
|
|
7051
|
-
};
|
|
7052
|
-
var visibleTextOf = (markup) => markup.replace(COMMENT_PATTERN, " ").replace(INVISIBLE_ELEMENT_PATTERN, " ").replace(TAG_PATTERN, " ").replace(/&(?:nbsp|amp|lt|gt|quot|#39);/g, (entity) => ENTITIES[entity] ?? " ").replace(/\s+/g, " ").trim();
|
|
7053
|
-
var noscriptMentionsJavascript = (html) => {
|
|
7054
|
-
for (const match of html.matchAll(NOSCRIPT_PATTERN)) {
|
|
7055
|
-
if (/javascript/i.test(match[1] ?? "")) {
|
|
7056
|
-
return true;
|
|
7057
|
-
}
|
|
7058
|
-
}
|
|
7059
|
-
return false;
|
|
7060
|
-
};
|
|
7061
|
-
var detectClientShell = (html) => {
|
|
7062
|
-
const body = BODY_PATTERN.exec(html)?.[1] ?? html.replace(HEAD_PATTERN, " ");
|
|
7063
|
-
const visibleTextLength = visibleTextOf(body).length;
|
|
7064
|
-
if (visibleTextLength >= SHELL_SUSPICION_THRESHOLD) {
|
|
7065
|
-
return { isShell: false, visibleTextLength };
|
|
7066
|
-
}
|
|
7067
|
-
if (CHALLENGE_PATTERN.test(html)) {
|
|
7068
|
-
return { isShell: true, reason: "anti-bot-challenge", visibleTextLength };
|
|
7069
|
-
}
|
|
7070
|
-
if (MOUNT_POINT_PATTERN.test(body)) {
|
|
7071
|
-
return { isShell: true, reason: "spa-shell", visibleTextLength };
|
|
7072
|
-
}
|
|
7073
|
-
if (noscriptMentionsJavascript(html)) {
|
|
7074
|
-
return { isShell: true, reason: "noscript-only", visibleTextLength };
|
|
7075
|
-
}
|
|
7076
|
-
if (visibleTextLength < SHELL_EMPTY_THRESHOLD) {
|
|
7077
|
-
return { isShell: true, reason: "empty-body", visibleTextLength };
|
|
7078
|
-
}
|
|
7079
|
-
return { isShell: false, visibleTextLength };
|
|
7080
|
-
};
|
|
7081
|
-
|
|
7082
7346
|
// src/extract-meta-refresh.ts
|
|
7083
7347
|
var MAX_META_REFRESH_DELAY_SECONDS = 10;
|
|
7084
7348
|
var META_TAG_PATTERN = /<meta\b[^>]*>/gi;
|