webforai 2.1.0 → 3.0.0
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/README.md +63 -1
- package/dist/adapters/index.js +58 -0
- package/dist/adapters/registry.js +149 -0
- package/dist/adapters/sites/declarative.js +178 -0
- package/dist/adapters/sites/hackernews.js +94 -0
- package/dist/adapters/sites/youtube.js +165 -0
- package/dist/adapters/types.js +0 -0
- package/dist/bin.js +3199 -399
- package/dist/cjs/adapters/index.js +86 -0
- package/dist/cjs/adapters/registry.js +178 -0
- package/dist/cjs/adapters/sites/declarative.js +202 -0
- package/dist/cjs/adapters/sites/hackernews.js +117 -0
- package/dist/cjs/adapters/sites/youtube.js +191 -0
- package/dist/cjs/adapters/types.js +16 -0
- package/dist/cjs/constants.js +2 -2
- package/dist/cjs/extractors/index.js +8 -0
- package/dist/cjs/extractors/lib/constants.js +186 -0
- package/dist/cjs/extractors/lib/sanitize.js +425 -0
- package/dist/cjs/extractors/lib/score.js +264 -0
- package/dist/cjs/extractors/lib/terminators.js +131 -0
- package/dist/cjs/extractors/lib/ui-chrome.js +119 -0
- package/dist/cjs/extractors/pipeExtractors.js +12 -9
- package/dist/cjs/extractors/presets/auto.js +44 -0
- package/dist/cjs/extractors/presets/minimal-filter.js +27 -48
- package/dist/cjs/extractors/presets/takumi.js +84 -122
- package/dist/cjs/html-to-markdown.js +44 -7
- package/dist/cjs/html-to-mdast.js +9 -1
- package/dist/cjs/index.js +33 -2
- package/dist/cjs/mdast-handlers/custom-a-handler.js +25 -9
- package/dist/cjs/mdast-handlers/custom-img-handler.js +23 -1
- package/dist/cjs/mdast-handlers/custom-table-handler.js +16 -0
- package/dist/cjs/mdast-handlers/definition-list-handler.js +99 -0
- package/dist/cjs/mdast-handlers/math-handler.js +12 -3
- package/dist/cjs/mdast-handlers/sup-sub-handler.js +39 -0
- package/dist/cjs/metadata/index.js +223 -0
- package/dist/cjs/normalize/index.js +168 -0
- package/dist/cjs/platform/client.js +147 -0
- package/dist/cjs/platform/error.js +36 -0
- package/dist/cjs/platform/fetch.js +37 -0
- package/dist/cjs/platform/index.js +40 -0
- package/dist/cjs/platform/types.js +34 -0
- package/dist/cjs/utils/hast-fast.js +284 -0
- package/dist/constants.js +2 -2
- package/dist/extractors/index.js +5 -0
- package/dist/extractors/lib/constants.js +150 -0
- package/dist/extractors/lib/sanitize.js +415 -0
- package/dist/extractors/lib/score.js +242 -0
- package/dist/extractors/lib/terminators.js +107 -0
- package/dist/extractors/lib/ui-chrome.js +96 -0
- package/dist/extractors/pipeExtractors.js +12 -9
- package/dist/extractors/presets/auto.js +20 -0
- package/dist/extractors/presets/minimal-filter.js +35 -47
- package/dist/extractors/presets/takumi.js +91 -122
- package/dist/html-to-markdown.js +42 -6
- package/dist/html-to-mdast.js +9 -1
- package/dist/index.js +34 -3
- package/dist/mdast-handlers/custom-a-handler.js +25 -9
- package/dist/mdast-handlers/custom-img-handler.js +23 -1
- package/dist/mdast-handlers/custom-table-handler.js +16 -0
- package/dist/mdast-handlers/definition-list-handler.js +76 -0
- package/dist/mdast-handlers/math-handler.js +12 -3
- package/dist/mdast-handlers/sup-sub-handler.js +16 -0
- package/dist/metadata/index.js +198 -0
- package/dist/normalize/index.js +149 -0
- package/dist/platform/client.js +125 -0
- package/dist/platform/error.js +13 -0
- package/dist/platform/fetch.js +14 -0
- package/dist/platform/index.js +19 -0
- package/dist/platform/types.js +9 -0
- package/dist/types/adapters/index.d.ts +24 -0
- package/dist/types/adapters/index.js.map +1 -0
- package/dist/types/adapters/registry.d.ts +35 -0
- package/dist/types/adapters/registry.js.map +1 -0
- package/dist/types/adapters/sites/declarative.d.ts +13 -0
- package/dist/types/adapters/sites/declarative.js.map +1 -0
- package/dist/types/adapters/sites/hackernews.d.ts +13 -0
- package/dist/types/adapters/sites/hackernews.js.map +1 -0
- package/dist/types/adapters/sites/youtube.d.ts +31 -0
- package/dist/types/adapters/sites/youtube.js.map +1 -0
- package/dist/types/adapters/types.d.ts +99 -0
- package/dist/types/adapters/types.js.map +1 -0
- package/dist/types/cli/bin.js.map +1 -1
- package/dist/types/cli/commands/skill/content.d.ts +9 -0
- package/dist/types/cli/commands/skill/content.js.map +1 -0
- package/dist/types/cli/commands/skill/index.d.ts +19 -0
- package/dist/types/cli/commands/skill/index.js.map +1 -0
- package/dist/types/cli/commands/webforai/interactive.d.ts +7 -0
- package/dist/types/cli/commands/webforai/interactive.js.map +1 -0
- package/dist/types/cli/commands/webforai/loadHtml.js.map +1 -1
- package/dist/types/cli/commands/webforai/options.d.ts +55 -0
- package/dist/types/cli/commands/webforai/options.js.map +1 -0
- package/dist/types/cli/commands/webforai/run.d.ts +10 -0
- package/dist/types/cli/commands/webforai/run.js.map +1 -0
- package/dist/types/cli/constants.d.ts +14 -4
- package/dist/types/cli/constants.js.map +1 -1
- package/dist/types/cli/helpers/selectExtractMode.js.map +1 -1
- package/dist/types/cli/helpers/selectLoader.js.map +1 -1
- package/dist/types/constants.d.ts +7 -1
- package/dist/types/constants.js.map +1 -1
- package/dist/types/extractors/index.d.ts +2 -0
- package/dist/types/extractors/index.js.map +1 -1
- package/dist/types/extractors/lib/constants.d.ts +81 -0
- package/dist/types/extractors/lib/constants.js.map +1 -0
- package/dist/types/extractors/lib/sanitize.d.ts +66 -0
- package/dist/types/extractors/lib/sanitize.js.map +1 -0
- package/dist/types/extractors/lib/score.d.ts +85 -0
- package/dist/types/extractors/lib/score.js.map +1 -0
- package/dist/types/extractors/lib/terminators.d.ts +26 -0
- package/dist/types/extractors/lib/terminators.js.map +1 -0
- package/dist/types/extractors/lib/ui-chrome.d.ts +18 -0
- package/dist/types/extractors/lib/ui-chrome.js.map +1 -0
- package/dist/types/extractors/pipeExtractors.d.ts +5 -0
- package/dist/types/extractors/pipeExtractors.js.map +1 -1
- package/dist/types/extractors/presets/auto.d.ts +30 -0
- package/dist/types/extractors/presets/auto.js.map +1 -0
- package/dist/types/extractors/presets/minimal-filter.d.ts +10 -2
- package/dist/types/extractors/presets/minimal-filter.js.map +1 -1
- package/dist/types/extractors/presets/takumi.d.ts +19 -3
- package/dist/types/extractors/presets/takumi.js.map +1 -1
- package/dist/types/extractors/types.d.ts +8 -0
- package/dist/types/html-to-markdown.d.ts +38 -0
- package/dist/types/html-to-markdown.js.map +1 -1
- package/dist/types/html-to-mdast.d.ts +11 -0
- package/dist/types/html-to-mdast.js.map +1 -1
- package/dist/types/index.d.ts +6 -3
- package/dist/types/index.js.map +1 -1
- package/dist/types/mdast-handlers/custom-a-handler.js.map +1 -1
- package/dist/types/mdast-handlers/custom-img-handler.js.map +1 -1
- package/dist/types/mdast-handlers/custom-table-handler.js.map +1 -1
- package/dist/types/mdast-handlers/definition-list-handler.d.ts +13 -0
- package/dist/types/mdast-handlers/definition-list-handler.js.map +1 -0
- package/dist/types/mdast-handlers/math-handler.js.map +1 -1
- package/dist/types/mdast-handlers/sup-sub-handler.d.ts +10 -0
- package/dist/types/mdast-handlers/sup-sub-handler.js.map +1 -0
- package/dist/types/metadata/index.d.ts +47 -0
- package/dist/types/metadata/index.js.map +1 -0
- package/dist/types/normalize/index.d.ts +53 -0
- package/dist/types/normalize/index.js.map +1 -0
- package/dist/types/platform/client.d.ts +54 -0
- package/dist/types/platform/client.js.map +1 -0
- package/dist/types/platform/error.d.ts +20 -0
- package/dist/types/platform/error.js.map +1 -0
- package/dist/types/platform/fetch.d.ts +34 -0
- package/dist/types/platform/fetch.js.map +1 -0
- package/dist/types/platform/index.d.ts +4 -0
- package/dist/types/platform/index.js.map +1 -0
- package/dist/types/platform/types.d.ts +135 -0
- package/dist/types/platform/types.js.map +1 -0
- package/dist/types/utils/hast-fast.d.ts +101 -0
- package/dist/types/utils/hast-fast.js.map +1 -0
- package/dist/utils/hast-fast.js +246 -0
- package/package.json +10 -3
- package/dist/types/cli/commands/webforai/index.d.ts +0 -6
- package/dist/types/cli/commands/webforai/index.js.map +0 -1
package/dist/html-to-mdast.js
CHANGED
|
@@ -8,18 +8,26 @@ import { customCodeHandler } from "./mdast-handlers/custom-code-handler.js";
|
|
|
8
8
|
import { customDivHandler } from "./mdast-handlers/custom-div-handler.js";
|
|
9
9
|
import { customImgHandler } from "./mdast-handlers/custom-img-handler.js";
|
|
10
10
|
import { customTableHandler } from "./mdast-handlers/custom-table-handler.js";
|
|
11
|
+
import { definitionListHandler } from "./mdast-handlers/definition-list-handler.js";
|
|
11
12
|
import { mathHandler } from "./mdast-handlers/math-handler.js";
|
|
13
|
+
import { supSubHandler } from "./mdast-handlers/sup-sub-handler.js";
|
|
14
|
+
import { normalizeHast } from "./normalize/index.js";
|
|
12
15
|
import { getLangFromHast, getLangFromStr, getUrlFromHast } from "./utils/hast-utils.js";
|
|
13
16
|
var htmlToMdast = (htmlOrHast, options) => {
|
|
14
17
|
const { extractors, url: defaultUrl, lang: defaultLang } = options || {};
|
|
15
18
|
const [lang, hast] = typeof htmlOrHast === "string" ? [defaultLang || getLangFromStr(htmlOrHast), fromHtml(htmlOrHast, { fragment: true })] : [defaultLang || getLangFromHast(htmlOrHast), htmlOrHast];
|
|
19
|
+
const isOwned = typeof htmlOrHast === "string" || (options?.owned ?? false);
|
|
16
20
|
const url = defaultUrl || getUrlFromHast(hast);
|
|
17
|
-
const extractedHast = pipeExtractors({ hast, lang, url }, extractors);
|
|
21
|
+
const extractedHast = pipeExtractors({ hast, lang, url, owned: isOwned }, extractors);
|
|
22
|
+
normalizeHast(extractedHast, options?.normalize);
|
|
18
23
|
const mdast = toMdast(extractedHast, {
|
|
19
24
|
handlers: {
|
|
20
25
|
math: mathHandler,
|
|
21
26
|
div: customDivHandler,
|
|
22
27
|
pre: customCodeHandler,
|
|
28
|
+
dl: definitionListHandler,
|
|
29
|
+
sup: supSubHandler("sup"),
|
|
30
|
+
sub: supSubHandler("sub"),
|
|
23
31
|
a: customAHandler({ asText: options?.linkAsText }),
|
|
24
32
|
img: customImgHandler({ hideImage: options?.hideImage }),
|
|
25
33
|
table: customTableHandler({ asText: options?.tableAsText })
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,48 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
htmlToMarkdown,
|
|
4
|
+
htmlToMarkdownWithMetadata
|
|
5
|
+
} from "./html-to-markdown.js";
|
|
3
6
|
import { mdastSplitter } from "./md-splitter.js";
|
|
4
7
|
import { htmlToMdast } from "./html-to-mdast.js";
|
|
5
8
|
import { mdastToMarkdown } from "./mdast-to-markdown.js";
|
|
6
9
|
import {
|
|
7
10
|
pipeExtractors,
|
|
8
|
-
|
|
11
|
+
autoExtractor,
|
|
12
|
+
createAutoExtractor,
|
|
13
|
+
takumiExtractor,
|
|
14
|
+
minimalFilter
|
|
9
15
|
} from "./extractors/index.js";
|
|
16
|
+
import {
|
|
17
|
+
BUILTIN_ADAPTERS,
|
|
18
|
+
adapterById,
|
|
19
|
+
buildPageSignature,
|
|
20
|
+
defineSelectorAdapter,
|
|
21
|
+
resolveAdapter,
|
|
22
|
+
runAdapters,
|
|
23
|
+
youtubeCaptionTracks
|
|
24
|
+
} from "./adapters/index.js";
|
|
25
|
+
import { extractMetadata, toFrontmatter } from "./metadata/index.js";
|
|
26
|
+
import { normalizeHast } from "./normalize/index.js";
|
|
10
27
|
export {
|
|
28
|
+
BUILTIN_ADAPTERS,
|
|
29
|
+
adapterById,
|
|
30
|
+
autoExtractor,
|
|
31
|
+
buildPageSignature,
|
|
32
|
+
createAutoExtractor,
|
|
33
|
+
defineSelectorAdapter,
|
|
34
|
+
extractMetadata,
|
|
11
35
|
htmlToMarkdown,
|
|
36
|
+
htmlToMarkdownWithMetadata,
|
|
12
37
|
htmlToMdast,
|
|
13
38
|
mdastSplitter,
|
|
14
39
|
mdastToMarkdown,
|
|
40
|
+
minimalFilter,
|
|
41
|
+
normalizeHast,
|
|
15
42
|
pipeExtractors,
|
|
16
|
-
|
|
43
|
+
resolveAdapter,
|
|
44
|
+
runAdapters,
|
|
45
|
+
takumiExtractor,
|
|
46
|
+
toFrontmatter,
|
|
47
|
+
youtubeCaptionTracks
|
|
17
48
|
};
|
|
@@ -1,21 +1,37 @@
|
|
|
1
1
|
// src/mdast-handlers/custom-a-handler.ts
|
|
2
2
|
import { defaultHandlers } from "hast-util-to-mdast";
|
|
3
3
|
import { toString as hastToString } from "hast-util-to-string";
|
|
4
|
+
import { stringProperty } from "../utils/hast-fast.js";
|
|
5
|
+
var ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:", "ftp:", "ftps:"]);
|
|
6
|
+
var isFollowableHref = (href) => {
|
|
7
|
+
const trimmed = href?.trim();
|
|
8
|
+
if (!trimmed) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const scheme = trimmed.match(/^[a-z][a-z0-9+.-]*:/i)?.[0];
|
|
12
|
+
if (!scheme) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
return ALLOWED_SCHEMES.has(scheme.toLowerCase());
|
|
16
|
+
};
|
|
4
17
|
var customAHandler = (options) => (state, node) => {
|
|
5
|
-
|
|
18
|
+
const asPlainText = () => {
|
|
6
19
|
const text = hastToString(node);
|
|
7
|
-
if (
|
|
20
|
+
if (text.length === 0) {
|
|
8
21
|
return void 0;
|
|
9
22
|
}
|
|
10
|
-
const
|
|
11
|
-
state.patch(node,
|
|
12
|
-
return
|
|
23
|
+
const result = { type: "text", value: text };
|
|
24
|
+
state.patch(node, result);
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
if (options?.asText) {
|
|
28
|
+
return hastToString(node).length <= 3 ? void 0 : asPlainText();
|
|
13
29
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return link;
|
|
30
|
+
if (!isFollowableHref(stringProperty(node, "href"))) {
|
|
31
|
+
return asPlainText();
|
|
17
32
|
}
|
|
18
|
-
|
|
33
|
+
const link = defaultHandlers.a(state, node);
|
|
34
|
+
return link.children.length > 0 ? link : void 0;
|
|
19
35
|
};
|
|
20
36
|
export {
|
|
21
37
|
customAHandler
|
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
// src/mdast-handlers/custom-img-handler.ts
|
|
2
2
|
import { defaultHandlers } from "hast-util-to-mdast";
|
|
3
|
+
import { stringProperty } from "../utils/hast-fast.js";
|
|
4
|
+
var MAX_DATA_URL_LENGTH = 256;
|
|
5
|
+
var hasNoUsableSource = (node) => {
|
|
6
|
+
const src = stringProperty(node, "src")?.trim();
|
|
7
|
+
if (!src) {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
if (src.startsWith("data:")) {
|
|
11
|
+
return src.length > MAX_DATA_URL_LENGTH;
|
|
12
|
+
}
|
|
13
|
+
return false;
|
|
14
|
+
};
|
|
3
15
|
var customImgHandler = (options) => (state, node) => {
|
|
4
16
|
if (options?.hideImage) {
|
|
5
17
|
return void 0;
|
|
6
18
|
}
|
|
7
|
-
|
|
19
|
+
if (hasNoUsableSource(node)) {
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
const image = defaultHandlers.image(state, node);
|
|
23
|
+
if (!image.alt) {
|
|
24
|
+
const title = stringProperty(node, "title");
|
|
25
|
+
if (title) {
|
|
26
|
+
image.alt = title;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return image;
|
|
8
30
|
};
|
|
9
31
|
export {
|
|
10
32
|
customImgHandler
|
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
// src/mdast-handlers/custom-table-handler.ts
|
|
2
2
|
import { defaultHandlers } from "hast-util-to-mdast";
|
|
3
3
|
import { toText } from "hast-util-to-text";
|
|
4
|
+
import { isElement, numericProperty, walk } from "../utils/hast-fast.js";
|
|
5
|
+
var MAX_SPAN = 1e3;
|
|
6
|
+
var clampSpans = (node) => {
|
|
7
|
+
walk(node, (candidate) => {
|
|
8
|
+
if (!isElement(candidate) || candidate.tagName !== "td" && candidate.tagName !== "th") {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
for (const attribute of ["colSpan", "rowSpan"]) {
|
|
12
|
+
const span = numericProperty(candidate, attribute);
|
|
13
|
+
if (span !== void 0 && span > MAX_SPAN) {
|
|
14
|
+
candidate.properties[attribute] = MAX_SPAN;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
};
|
|
4
19
|
var customTableHandler = (options) => (state, node) => {
|
|
5
20
|
if (options?.asText) {
|
|
6
21
|
const paragraph = { type: "paragraph", children: [{ type: "text", value: toText(node) }] };
|
|
7
22
|
state.patch(node, paragraph);
|
|
8
23
|
return paragraph;
|
|
9
24
|
}
|
|
25
|
+
clampSpans(node);
|
|
10
26
|
return defaultHandlers.table(state, node);
|
|
11
27
|
};
|
|
12
28
|
export {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// src/mdast-handlers/definition-list-handler.ts
|
|
2
|
+
var hoistBoundaryWhitespace = (nodes) => {
|
|
3
|
+
const result = [];
|
|
4
|
+
for (const node of nodes) {
|
|
5
|
+
if (node.type !== "emphasis" && node.type !== "strong") {
|
|
6
|
+
result.push(node);
|
|
7
|
+
continue;
|
|
8
|
+
}
|
|
9
|
+
node.children = hoistBoundaryWhitespace(node.children);
|
|
10
|
+
const first = node.children[0];
|
|
11
|
+
if (first?.type === "text" && /^\s/.test(first.value)) {
|
|
12
|
+
result.push({ type: "text", value: " " });
|
|
13
|
+
first.value = first.value.replace(/^\s+/, "");
|
|
14
|
+
}
|
|
15
|
+
let trailing = false;
|
|
16
|
+
const last = node.children[node.children.length - 1];
|
|
17
|
+
if (last?.type === "text" && /\s$/.test(last.value)) {
|
|
18
|
+
last.value = last.value.replace(/\s+$/, "");
|
|
19
|
+
trailing = true;
|
|
20
|
+
}
|
|
21
|
+
node.children = node.children.filter((child) => !(child.type === "text" && child.value.length === 0));
|
|
22
|
+
if (node.children.length > 0) {
|
|
23
|
+
result.push(node);
|
|
24
|
+
}
|
|
25
|
+
if (trailing) {
|
|
26
|
+
result.push({ type: "text", value: " " });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
};
|
|
31
|
+
var definitionListHandler = (state, node) => {
|
|
32
|
+
const items = [];
|
|
33
|
+
let current;
|
|
34
|
+
const flush = () => {
|
|
35
|
+
if (!current) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const heading = {
|
|
39
|
+
type: "paragraph",
|
|
40
|
+
children: current.term.length > 0 ? [{ type: "strong", children: current.term }] : []
|
|
41
|
+
};
|
|
42
|
+
items.push({ type: "listItem", spread: false, children: [heading, ...current.definitions] });
|
|
43
|
+
current = void 0;
|
|
44
|
+
};
|
|
45
|
+
for (const child of node.children) {
|
|
46
|
+
if (child.type !== "element") {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const element = child;
|
|
50
|
+
if (element.tagName === "dt") {
|
|
51
|
+
if (current && current.definitions.length > 0) {
|
|
52
|
+
flush();
|
|
53
|
+
}
|
|
54
|
+
const phrasing = hoistBoundaryWhitespace(state.all(element));
|
|
55
|
+
current = current ? { term: [...current.term, { type: "text", value: ", " }, ...phrasing], definitions: [] } : { term: phrasing, definitions: [] };
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (element.tagName === "dd") {
|
|
59
|
+
if (!current) {
|
|
60
|
+
current = { term: [], definitions: [] };
|
|
61
|
+
}
|
|
62
|
+
const content = state.toFlow(state.all(element));
|
|
63
|
+
current.definitions.push(...content);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
flush();
|
|
67
|
+
if (items.length === 0) {
|
|
68
|
+
return void 0;
|
|
69
|
+
}
|
|
70
|
+
const result = { type: "list", ordered: false, spread: false, children: items };
|
|
71
|
+
state.patch(node, result);
|
|
72
|
+
return result;
|
|
73
|
+
};
|
|
74
|
+
export {
|
|
75
|
+
definitionListHandler
|
|
76
|
+
};
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
// src/mdast-handlers/math-handler.ts
|
|
2
2
|
import { toHtml } from "hast-util-to-html";
|
|
3
3
|
import { MathMLToLaTeX } from "mathml-to-latex";
|
|
4
|
+
import { stringProperty } from "../utils/hast-fast.js";
|
|
5
|
+
var readAnnotation = (node) => {
|
|
6
|
+
const alttext = stringProperty(node, "alttext");
|
|
7
|
+
if (!alttext) {
|
|
8
|
+
return void 0;
|
|
9
|
+
}
|
|
10
|
+
const unwrapped = alttext.trim().replace(/^\{\\displaystyle\s*([\s\S]*)\}$/, "$1").trim();
|
|
11
|
+
return unwrapped.length > 0 ? unwrapped : void 0;
|
|
12
|
+
};
|
|
13
|
+
var isDisplayMath = (node) => stringProperty(node, "display") === "block";
|
|
4
14
|
var mathHandler = (state, node) => {
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const result = { type: "inlineMath", value: latex };
|
|
15
|
+
const latex = readAnnotation(node) ?? MathMLToLaTeX.convert(toHtml(node));
|
|
16
|
+
const result = isDisplayMath(node) ? { type: "math", value: latex, meta: null } : { type: "inlineMath", value: latex };
|
|
8
17
|
state.patch(node, result);
|
|
9
18
|
return result;
|
|
10
19
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/mdast-handlers/sup-sub-handler.ts
|
|
2
|
+
import { toString as hastToString } from "hast-util-to-string";
|
|
3
|
+
var MAX_ANNOTATION_LENGTH = 32;
|
|
4
|
+
var supSubHandler = (tagName) => (state, node) => {
|
|
5
|
+
const inner = hastToString(node).trim();
|
|
6
|
+
if (inner.length === 0 || inner.length > MAX_ANNOTATION_LENGTH) {
|
|
7
|
+
return state.all(node);
|
|
8
|
+
}
|
|
9
|
+
const escaped = inner.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
10
|
+
const result = { type: "html", value: `<${tagName}>${escaped}</${tagName}>` };
|
|
11
|
+
state.patch(node, result);
|
|
12
|
+
return result;
|
|
13
|
+
};
|
|
14
|
+
export {
|
|
15
|
+
supSubHandler
|
|
16
|
+
};
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// src/metadata/index.ts
|
|
2
|
+
import { collectElements, findElement, isElement, stringProperty, walk } from "../utils/hast-fast.js";
|
|
3
|
+
var readMetaTags = (tree) => {
|
|
4
|
+
const tags = /* @__PURE__ */ new Map();
|
|
5
|
+
for (const meta of collectElements(tree, (element) => element.tagName === "meta")) {
|
|
6
|
+
const key = stringProperty(meta, "property") ?? stringProperty(meta, "name") ?? stringProperty(meta, "itemprop");
|
|
7
|
+
const content = stringProperty(meta, "content");
|
|
8
|
+
if (!(key && content)) {
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
const normalized = key.toLowerCase();
|
|
12
|
+
if (!tags.has(normalized)) {
|
|
13
|
+
tags.set(normalized, content);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return tags;
|
|
17
|
+
};
|
|
18
|
+
var readJsonLd = (tree) => {
|
|
19
|
+
const entries = [];
|
|
20
|
+
for (const script of collectElements(tree, (element) => element.tagName === "script")) {
|
|
21
|
+
const type = stringProperty(script, "type");
|
|
22
|
+
if (type?.toLowerCase() !== "application/ld+json") {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const source = script.children.map((child) => child.type === "text" ? child.value : "").join("").trim();
|
|
26
|
+
if (source.length === 0) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
let parsed;
|
|
30
|
+
try {
|
|
31
|
+
parsed = JSON.parse(source);
|
|
32
|
+
} catch {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
for (const entry of flattenJsonLd(parsed)) {
|
|
36
|
+
entries.push(entry);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return entries;
|
|
40
|
+
};
|
|
41
|
+
var flattenJsonLd = (value) => {
|
|
42
|
+
if (Array.isArray(value)) {
|
|
43
|
+
return value.flatMap(flattenJsonLd);
|
|
44
|
+
}
|
|
45
|
+
if (typeof value !== "object" || value === null) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
const record = value;
|
|
49
|
+
const graph = record["@graph"];
|
|
50
|
+
if (graph) {
|
|
51
|
+
return flattenJsonLd(graph);
|
|
52
|
+
}
|
|
53
|
+
return [record];
|
|
54
|
+
};
|
|
55
|
+
var ARTICLE_TYPES = /* @__PURE__ */ new Set([
|
|
56
|
+
"article",
|
|
57
|
+
"newsarticle",
|
|
58
|
+
"blogposting",
|
|
59
|
+
"techarticle",
|
|
60
|
+
"scholarlyarticle",
|
|
61
|
+
"report",
|
|
62
|
+
"webpage"
|
|
63
|
+
]);
|
|
64
|
+
var jsonLdType = (entry) => {
|
|
65
|
+
const type = entry["@type"];
|
|
66
|
+
if (typeof type === "string") {
|
|
67
|
+
return [type.toLowerCase()];
|
|
68
|
+
}
|
|
69
|
+
if (Array.isArray(type)) {
|
|
70
|
+
return type.filter((value) => typeof value === "string").map((value) => value.toLowerCase());
|
|
71
|
+
}
|
|
72
|
+
return [];
|
|
73
|
+
};
|
|
74
|
+
var readName = (value) => {
|
|
75
|
+
if (typeof value === "string") {
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
if (Array.isArray(value)) {
|
|
79
|
+
for (const entry of value) {
|
|
80
|
+
const name = readName(entry);
|
|
81
|
+
if (name) {
|
|
82
|
+
return name;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return void 0;
|
|
86
|
+
}
|
|
87
|
+
if (typeof value === "object" && value !== null) {
|
|
88
|
+
const name = value.name;
|
|
89
|
+
return typeof name === "string" ? name : void 0;
|
|
90
|
+
}
|
|
91
|
+
return void 0;
|
|
92
|
+
};
|
|
93
|
+
var asString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
|
|
94
|
+
var MAX_TITLE_LENGTH = 160;
|
|
95
|
+
var asTitle = (value) => {
|
|
96
|
+
const cleaned = clean(value);
|
|
97
|
+
return cleaned && cleaned.length <= MAX_TITLE_LENGTH ? cleaned : void 0;
|
|
98
|
+
};
|
|
99
|
+
var clean = (value) => {
|
|
100
|
+
if (value === void 0) {
|
|
101
|
+
return void 0;
|
|
102
|
+
}
|
|
103
|
+
const trimmed = value.replace(/\s+/g, " ").trim();
|
|
104
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
105
|
+
};
|
|
106
|
+
var documentTitle = (tree) => {
|
|
107
|
+
const title = findElement(tree, (element) => element.tagName === "title");
|
|
108
|
+
if (!title) {
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
return title.children.map((child) => child.type === "text" ? child.value : "").join("");
|
|
112
|
+
};
|
|
113
|
+
var firstHeading = (tree) => {
|
|
114
|
+
const heading = findElement(tree, (element) => element.tagName === "h1");
|
|
115
|
+
if (!heading) {
|
|
116
|
+
return void 0;
|
|
117
|
+
}
|
|
118
|
+
let text = "";
|
|
119
|
+
walk(heading, (node) => {
|
|
120
|
+
if (node.type === "text") {
|
|
121
|
+
text += node.value;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
return text;
|
|
125
|
+
};
|
|
126
|
+
var linkHref = (tree, rel) => {
|
|
127
|
+
const link = findElement(
|
|
128
|
+
tree,
|
|
129
|
+
(element) => element.tagName === "link" && (stringProperty(element, "rel") ?? "").toLowerCase().includes(rel)
|
|
130
|
+
);
|
|
131
|
+
return link ? stringProperty(link, "href") : void 0;
|
|
132
|
+
};
|
|
133
|
+
var documentLang = (tree) => {
|
|
134
|
+
const html = findElement(tree, (element) => element.tagName === "html");
|
|
135
|
+
if (html) {
|
|
136
|
+
const declared = stringProperty(html, "lang") ?? stringProperty(html, "xml:lang");
|
|
137
|
+
if (declared) {
|
|
138
|
+
return declared;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (isElement(tree)) {
|
|
142
|
+
const own = stringProperty(tree, "lang");
|
|
143
|
+
if (own) {
|
|
144
|
+
return own;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const tagged = findElement(tree, (element) => Boolean(stringProperty(element, "lang")));
|
|
148
|
+
return tagged ? stringProperty(tagged, "lang") : void 0;
|
|
149
|
+
};
|
|
150
|
+
var extractMetadata = (tree) => {
|
|
151
|
+
const meta = readMetaTags(tree);
|
|
152
|
+
const jsonLd = readJsonLd(tree);
|
|
153
|
+
const article = jsonLd.find((entry) => jsonLdType(entry).some((type) => ARTICLE_TYPES.has(type))) ?? jsonLd[0];
|
|
154
|
+
const get = (...keys) => {
|
|
155
|
+
for (const key of keys) {
|
|
156
|
+
const value = meta.get(key);
|
|
157
|
+
if (value) {
|
|
158
|
+
return value;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return void 0;
|
|
162
|
+
};
|
|
163
|
+
const title = asTitle(get("og:title", "twitter:title")) ?? asTitle(asString(article?.headline)) ?? asTitle(asString(article?.name)) ?? asTitle(documentTitle(tree)) ?? asTitle(firstHeading(tree));
|
|
164
|
+
return {
|
|
165
|
+
title,
|
|
166
|
+
description: clean(get("og:description", "twitter:description", "description")) ?? clean(asString(article?.description)),
|
|
167
|
+
author: clean(readName(article?.author)) ?? clean(get("author", "article:author", "twitter:creator")),
|
|
168
|
+
published: clean(asString(article?.datePublished)) ?? clean(get("article:published_time", "date", "pubdate")),
|
|
169
|
+
modified: clean(asString(article?.dateModified)) ?? clean(get("article:modified_time", "og:updated_time")),
|
|
170
|
+
siteName: clean(get("og:site_name", "application-name")) ?? clean(readName(article?.publisher)),
|
|
171
|
+
canonicalUrl: clean(linkHref(tree, "canonical")) ?? clean(get("og:url", "twitter:url")),
|
|
172
|
+
lang: clean(documentLang(tree)),
|
|
173
|
+
image: clean(get("og:image", "twitter:image", "twitter:image:src")),
|
|
174
|
+
type: clean(get("og:type"))
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
var yamlString = (value) => `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
178
|
+
var toFrontmatter = (metadata) => {
|
|
179
|
+
const lines = [];
|
|
180
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
181
|
+
if (typeof value === "string" && value.length > 0) {
|
|
182
|
+
lines.push(`${key}: ${yamlString(value)}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (lines.length === 0) {
|
|
186
|
+
return "";
|
|
187
|
+
}
|
|
188
|
+
return `---
|
|
189
|
+
${lines.join("\n")}
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
`;
|
|
193
|
+
};
|
|
194
|
+
export {
|
|
195
|
+
extractMetadata,
|
|
196
|
+
readJsonLd,
|
|
197
|
+
toFrontmatter
|
|
198
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// src/normalize/index.ts
|
|
2
|
+
import {
|
|
3
|
+
classList,
|
|
4
|
+
isElement,
|
|
5
|
+
numericProperty,
|
|
6
|
+
property,
|
|
7
|
+
pruneInPlace,
|
|
8
|
+
stringProperty,
|
|
9
|
+
walk
|
|
10
|
+
} from "../utils/hast-fast.js";
|
|
11
|
+
var LAZY_SRC_ATTRIBUTES = [
|
|
12
|
+
"data-src",
|
|
13
|
+
"data-original",
|
|
14
|
+
"data-lazy-src",
|
|
15
|
+
"data-original-src",
|
|
16
|
+
"data-actualsrc",
|
|
17
|
+
"data-hi-res-src",
|
|
18
|
+
"data-echo"
|
|
19
|
+
];
|
|
20
|
+
var PLACEHOLDER_SRC = /^$|^data:image\/|^about:blank$|spacer\.gif$|blank\.(gif|png)$/i;
|
|
21
|
+
var readSrcset = (element, attribute) => {
|
|
22
|
+
const raw = property(element, attribute);
|
|
23
|
+
if (typeof raw === "string") {
|
|
24
|
+
return raw;
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(raw)) {
|
|
27
|
+
return raw.join(", ");
|
|
28
|
+
}
|
|
29
|
+
return void 0;
|
|
30
|
+
};
|
|
31
|
+
var bestFromSrcset = (srcset) => {
|
|
32
|
+
let best;
|
|
33
|
+
for (const entry of srcset.split(",")) {
|
|
34
|
+
const parts = entry.trim().split(/\s+/);
|
|
35
|
+
const url = parts[0];
|
|
36
|
+
if (!url) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const descriptor = parts[1];
|
|
40
|
+
let weight = 1;
|
|
41
|
+
if (descriptor?.endsWith("w")) {
|
|
42
|
+
weight = Number.parseFloat(descriptor) || 1;
|
|
43
|
+
} else if (descriptor?.endsWith("x")) {
|
|
44
|
+
weight = (Number.parseFloat(descriptor) || 1) * 1e3;
|
|
45
|
+
}
|
|
46
|
+
if (!best || weight > best.weight) {
|
|
47
|
+
best = { url, weight };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return best?.url;
|
|
51
|
+
};
|
|
52
|
+
var resolveImageSources = (tree) => {
|
|
53
|
+
walk(tree, (node) => {
|
|
54
|
+
if (!isElement(node) || node.tagName !== "img") {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const current = stringProperty(node, "src");
|
|
58
|
+
if (current && !PLACEHOLDER_SRC.test(current)) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
for (const attribute of LAZY_SRC_ATTRIBUTES) {
|
|
62
|
+
const candidate = stringProperty(node, attribute);
|
|
63
|
+
if (candidate && !PLACEHOLDER_SRC.test(candidate)) {
|
|
64
|
+
node.properties.src = candidate;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
for (const attribute of ["srcSet", "srcset", "data-srcset"]) {
|
|
69
|
+
const srcset = readSrcset(node, attribute);
|
|
70
|
+
const best = srcset ? bestFromSrcset(srcset) : void 0;
|
|
71
|
+
if (best) {
|
|
72
|
+
node.properties.src = best;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
var katexDuplicates = (node) => {
|
|
79
|
+
const classes = classList(node);
|
|
80
|
+
if (!(classes.includes("katex") || classes.includes("katex-display"))) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
const hasMathml = node.children.some((child) => isElement(child) && classList(child).includes("katex-mathml"));
|
|
84
|
+
if (!hasMathml) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
return node.children.filter((child) => isElement(child) && classList(child).includes("katex-html"));
|
|
88
|
+
};
|
|
89
|
+
var mediawikiDuplicates = (node) => {
|
|
90
|
+
if (!classList(node).includes("mwe-math-element")) {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
return node.children.filter(
|
|
94
|
+
(child) => isElement(child) && classList(child).some((name) => name.startsWith("mwe-math-fallback-image"))
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
var mathjaxDuplicates = (node) => {
|
|
98
|
+
if (node.tagName !== "mjx-container") {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
return node.children.filter((child) => isElement(child) && child.tagName !== "mjx-assistive-mml");
|
|
102
|
+
};
|
|
103
|
+
var dedupeRenderedMath = (tree) => {
|
|
104
|
+
const doomed = /* @__PURE__ */ new Set();
|
|
105
|
+
walk(tree, (node) => {
|
|
106
|
+
if (!isElement(node)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
for (const duplicate of [...katexDuplicates(node), ...mathjaxDuplicates(node), ...mediawikiDuplicates(node)]) {
|
|
110
|
+
doomed.add(duplicate);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
if (doomed.size > 0) {
|
|
114
|
+
pruneInPlace(tree, (node) => !(isElement(node) && doomed.has(node)));
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
var promoteAriaHeadings = (tree) => {
|
|
118
|
+
walk(tree, (node) => {
|
|
119
|
+
if (!isElement(node) || stringProperty(node, "role") !== "heading") {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (/^h[1-6]$/.test(node.tagName)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const level = numericProperty(node, "aria-level") ?? 2;
|
|
126
|
+
const clamped = Math.min(6, Math.max(1, Math.round(level)));
|
|
127
|
+
node.tagName = `h${clamped}`;
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
var normalizeHast = (tree, options = {}) => {
|
|
131
|
+
const { images = true, math = true, ariaHeadings = true } = options;
|
|
132
|
+
if (images) {
|
|
133
|
+
resolveImageSources(tree);
|
|
134
|
+
}
|
|
135
|
+
if (math) {
|
|
136
|
+
dedupeRenderedMath(tree);
|
|
137
|
+
}
|
|
138
|
+
if (ariaHeadings) {
|
|
139
|
+
promoteAriaHeadings(tree);
|
|
140
|
+
}
|
|
141
|
+
return tree;
|
|
142
|
+
};
|
|
143
|
+
export {
|
|
144
|
+
bestFromSrcset,
|
|
145
|
+
dedupeRenderedMath,
|
|
146
|
+
normalizeHast,
|
|
147
|
+
promoteAriaHeadings,
|
|
148
|
+
resolveImageSources
|
|
149
|
+
};
|