webforai 3.0.0 → 4.1.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.
Files changed (181) hide show
  1. package/README.md +47 -5
  2. package/dist/bin.js +3109 -490
  3. package/dist/cjs/detect-client-shell.js +78 -0
  4. package/dist/cjs/extract-mdast.js +58 -0
  5. package/dist/cjs/extract-meta-refresh.js +71 -0
  6. package/dist/cjs/extractors/index.js +5 -0
  7. package/dist/cjs/extractors/lib/block-features.js +465 -0
  8. package/dist/cjs/extractors/lib/block-model.generated.js +45 -0
  9. package/dist/cjs/extractors/lib/block-model.js +72 -0
  10. package/dist/cjs/extractors/lib/blocks.js +212 -0
  11. package/dist/cjs/extractors/lib/code-tabs.js +191 -0
  12. package/dist/cjs/extractors/lib/constants.js +1 -1
  13. package/dist/cjs/extractors/lib/layout-tables.js +123 -0
  14. package/dist/cjs/extractors/lib/sanitize.js +146 -26
  15. package/dist/cjs/extractors/lib/takumi-signal.js +76 -0
  16. package/dist/cjs/extractors/lib/ui-chrome.js +7 -0
  17. package/dist/cjs/extractors/presets/auto.js +3 -3
  18. package/dist/cjs/extractors/presets/kiwame.js +231 -0
  19. package/dist/cjs/extractors/presets/takumi.js +34 -3
  20. package/dist/cjs/html-to-markdown.js +58 -4
  21. package/dist/cjs/html-to-mdast.js +4 -3
  22. package/dist/cjs/index.js +16 -0
  23. package/dist/cjs/link-replacer.js +19 -13
  24. package/dist/cjs/loaders/cf-puppeteer.js +14 -7
  25. package/dist/cjs/loaders/fetch.js +14 -2
  26. package/dist/cjs/loaders/playwright.js +32 -30
  27. package/dist/cjs/loaders/puppeteer.js +14 -7
  28. package/dist/cjs/md-splitter.js +24 -11
  29. package/dist/cjs/mdast-handlers/custom-code-handler.js +32 -9
  30. package/dist/cjs/mdast-handlers/custom-div-handler.js +14 -35
  31. package/dist/cjs/mdast-handlers/custom-img-handler.js +10 -1
  32. package/dist/cjs/mdast-handlers/custom-table-handler.js +18 -1
  33. package/dist/cjs/mdast-handlers/definition-list-handler.js +17 -6
  34. package/dist/cjs/mdast-handlers/table-cell-flatten.js +195 -0
  35. package/dist/cjs/mdast-handlers/twoslash.js +71 -0
  36. package/dist/cjs/mdast-to-markdown.js +2 -5
  37. package/dist/cjs/metadata/index.js +4 -3
  38. package/dist/cjs/normalize/index.js +11 -2
  39. package/dist/cjs/platform/client.js +73 -16
  40. package/dist/cjs/platform/index.js +4 -0
  41. package/dist/cjs/platform/types.js +7 -1
  42. package/dist/cjs/utils/hast-fast.js +1 -1
  43. package/dist/cjs/utils/parse-html.js +113 -0
  44. package/dist/cjs/utils/pre-text.js +104 -0
  45. package/dist/detect-client-shell.js +53 -0
  46. package/dist/extract-mdast.js +58 -0
  47. package/dist/extract-meta-refresh.js +47 -0
  48. package/dist/extractors/index.js +3 -0
  49. package/dist/extractors/lib/block-features.js +437 -0
  50. package/dist/extractors/lib/block-model.generated.js +21 -0
  51. package/dist/extractors/lib/block-model.js +48 -0
  52. package/dist/extractors/lib/blocks.js +185 -0
  53. package/dist/extractors/lib/code-tabs.js +168 -0
  54. package/dist/extractors/lib/constants.js +1 -1
  55. package/dist/extractors/lib/layout-tables.js +99 -0
  56. package/dist/extractors/lib/sanitize.js +144 -26
  57. package/dist/extractors/lib/takumi-signal.js +53 -0
  58. package/dist/extractors/lib/ui-chrome.js +5 -0
  59. package/dist/extractors/presets/auto.js +3 -3
  60. package/dist/extractors/presets/kiwame.js +206 -0
  61. package/dist/extractors/presets/takumi.js +34 -3
  62. package/dist/html-to-markdown.js +57 -4
  63. package/dist/html-to-mdast.js +4 -3
  64. package/dist/index.js +18 -0
  65. package/dist/link-replacer.js +19 -13
  66. package/dist/loaders/cf-puppeteer.js +15 -8
  67. package/dist/loaders/fetch.js +14 -2
  68. package/dist/loaders/playwright.js +32 -30
  69. package/dist/loaders/puppeteer.js +15 -8
  70. package/dist/md-splitter.js +24 -11
  71. package/dist/mdast-handlers/custom-code-handler.js +30 -9
  72. package/dist/mdast-handlers/custom-div-handler.js +14 -35
  73. package/dist/mdast-handlers/custom-img-handler.js +10 -1
  74. package/dist/mdast-handlers/custom-table-handler.js +19 -2
  75. package/dist/mdast-handlers/definition-list-handler.js +17 -6
  76. package/dist/mdast-handlers/table-cell-flatten.js +170 -0
  77. package/dist/mdast-handlers/twoslash.js +48 -0
  78. package/dist/mdast-to-markdown.js +2 -5
  79. package/dist/metadata/index.js +4 -3
  80. package/dist/normalize/index.js +12 -2
  81. package/dist/platform/client.js +73 -16
  82. package/dist/platform/index.js +4 -0
  83. package/dist/platform/types.js +5 -1
  84. package/dist/types/cli/bin.js.map +1 -1
  85. package/dist/types/cli/commands/jobs/files.d.ts +28 -0
  86. package/dist/types/cli/commands/jobs/files.js.map +1 -0
  87. package/dist/types/cli/commands/jobs/llms-txt.d.ts +33 -0
  88. package/dist/types/cli/commands/jobs/llms-txt.js.map +1 -0
  89. package/dist/types/cli/commands/jobs/options.d.ts +58 -0
  90. package/dist/types/cli/commands/jobs/options.js.map +1 -0
  91. package/dist/types/cli/commands/jobs/run.d.ts +44 -0
  92. package/dist/types/cli/commands/jobs/run.js.map +1 -0
  93. package/dist/types/cli/commands/skill/content.js.map +1 -1
  94. package/dist/types/cli/commands/webforai/interactive.d.ts +1 -1
  95. package/dist/types/cli/commands/webforai/interactive.js.map +1 -1
  96. package/dist/types/cli/commands/webforai/options.d.ts +9 -2
  97. package/dist/types/cli/commands/webforai/options.js.map +1 -1
  98. package/dist/types/cli/commands/webforai/run.js.map +1 -1
  99. package/dist/types/cli/constants.d.ts +3 -1
  100. package/dist/types/cli/constants.js.map +1 -1
  101. package/dist/types/cli/utils.d.ts +8 -1
  102. package/dist/types/cli/utils.js.map +1 -1
  103. package/dist/types/detect-client-shell.d.ts +24 -0
  104. package/dist/types/detect-client-shell.js.map +1 -0
  105. package/dist/types/extract-mdast.js.map +1 -1
  106. package/dist/types/extract-meta-refresh.d.ts +29 -0
  107. package/dist/types/extract-meta-refresh.js.map +1 -0
  108. package/dist/types/extractors/index.d.ts +1 -0
  109. package/dist/types/extractors/index.js.map +1 -1
  110. package/dist/types/extractors/lib/block-features.d.ts +43 -0
  111. package/dist/types/extractors/lib/block-features.js.map +1 -0
  112. package/dist/types/extractors/lib/block-model.d.ts +28 -0
  113. package/dist/types/extractors/lib/block-model.generated.d.ts +5 -0
  114. package/dist/types/extractors/lib/block-model.generated.js.map +1 -0
  115. package/dist/types/extractors/lib/block-model.js.map +1 -0
  116. package/dist/types/extractors/lib/blocks.d.ts +53 -0
  117. package/dist/types/extractors/lib/blocks.js.map +1 -0
  118. package/dist/types/extractors/lib/code-tabs.d.ts +30 -0
  119. package/dist/types/extractors/lib/code-tabs.js.map +1 -0
  120. package/dist/types/extractors/lib/constants.js.map +1 -1
  121. package/dist/types/extractors/lib/layout-tables.d.ts +17 -0
  122. package/dist/types/extractors/lib/layout-tables.js.map +1 -0
  123. package/dist/types/extractors/lib/sanitize.d.ts +33 -3
  124. package/dist/types/extractors/lib/sanitize.js.map +1 -1
  125. package/dist/types/extractors/lib/takumi-signal.d.ts +12 -0
  126. package/dist/types/extractors/lib/takumi-signal.js.map +1 -0
  127. package/dist/types/extractors/lib/ui-chrome.d.ts +8 -0
  128. package/dist/types/extractors/lib/ui-chrome.js.map +1 -1
  129. package/dist/types/extractors/presets/auto.d.ts +6 -1
  130. package/dist/types/extractors/presets/auto.js.map +1 -1
  131. package/dist/types/extractors/presets/kiwame.d.ts +31 -0
  132. package/dist/types/extractors/presets/kiwame.js.map +1 -0
  133. package/dist/types/extractors/presets/takumi.js.map +1 -1
  134. package/dist/types/html-to-markdown.d.ts +9 -0
  135. package/dist/types/html-to-markdown.js.map +1 -1
  136. package/dist/types/html-to-mdast.js.map +1 -1
  137. package/dist/types/index.d.ts +3 -1
  138. package/dist/types/index.js.map +1 -1
  139. package/dist/types/link-replacer.d.ts +3 -1
  140. package/dist/types/link-replacer.js.map +1 -1
  141. package/dist/types/loaders/cf-puppeteer.js.map +1 -1
  142. package/dist/types/loaders/fetch.d.ts +4 -0
  143. package/dist/types/loaders/fetch.js.map +1 -1
  144. package/dist/types/loaders/playwright.js.map +1 -1
  145. package/dist/types/loaders/puppeteer.js.map +1 -1
  146. package/dist/types/md-splitter.d.ts +1 -1
  147. package/dist/types/md-splitter.js.map +1 -1
  148. package/dist/types/mdast-handlers/custom-code-handler.d.ts +9 -0
  149. package/dist/types/mdast-handlers/custom-code-handler.js.map +1 -1
  150. package/dist/types/mdast-handlers/custom-div-handler.js.map +1 -1
  151. package/dist/types/mdast-handlers/custom-img-handler.js.map +1 -1
  152. package/dist/types/mdast-handlers/custom-table-handler.js.map +1 -1
  153. package/dist/types/mdast-handlers/definition-list-handler.d.ts +0 -11
  154. package/dist/types/mdast-handlers/definition-list-handler.js.map +1 -1
  155. package/dist/types/mdast-handlers/table-cell-flatten.d.ts +18 -0
  156. package/dist/types/mdast-handlers/table-cell-flatten.js.map +1 -0
  157. package/dist/types/mdast-handlers/twoslash.d.ts +14 -0
  158. package/dist/types/mdast-handlers/twoslash.js.map +1 -0
  159. package/dist/types/mdast-to-markdown.js.map +1 -1
  160. package/dist/types/metadata/index.js.map +1 -1
  161. package/dist/types/normalize/index.js.map +1 -1
  162. package/dist/types/platform/client.d.ts +2 -1
  163. package/dist/types/platform/client.js.map +1 -1
  164. package/dist/types/platform/error.d.ts +5 -3
  165. package/dist/types/platform/error.js.map +1 -1
  166. package/dist/types/platform/fetch.d.ts +2 -0
  167. package/dist/types/platform/fetch.js.map +1 -1
  168. package/dist/types/platform/index.d.ts +1 -1
  169. package/dist/types/platform/index.js.map +1 -1
  170. package/dist/types/platform/types.d.ts +35 -6
  171. package/dist/types/platform/types.js.map +1 -1
  172. package/dist/types/utils/hast-fast.d.ts +1 -1
  173. package/dist/types/utils/hast-fast.js.map +1 -1
  174. package/dist/types/utils/parse-html.d.ts +33 -0
  175. package/dist/types/utils/parse-html.js.map +1 -0
  176. package/dist/types/utils/pre-text.d.ts +12 -0
  177. package/dist/types/utils/pre-text.js.map +1 -0
  178. package/dist/utils/hast-fast.js +1 -1
  179. package/dist/utils/parse-html.js +87 -0
  180. package/dist/utils/pre-text.js +81 -0
  181. package/package.json +18 -5
@@ -3,15 +3,13 @@ import { mdastToMarkdown } from "./mdast-to-markdown.js";
3
3
  import { chunk } from "./utils/common.js";
4
4
  import { internalType, unwarpRoot, warpRoot } from "./utils/mdast-utils.js";
5
5
  var PRIORITY_SPLITTERS = ["h1", "h2", "h3", "h4", "h5", "h6", "list", "table", "code"];
6
- var getSplitterGenerator = function* () {
7
- for (const splitter of PRIORITY_SPLITTERS) {
8
- yield splitter;
9
- }
10
- };
11
- var _mdastSplitter = async (contents, checker, splitterGenerator) => {
12
- const splitter = splitterGenerator.next().value;
6
+ var _mdastSplitter = async (contents, checker, priority, signal) => {
7
+ signal?.throwIfAborted();
8
+ const splitter = PRIORITY_SPLITTERS[priority];
13
9
  const markdown = mdastToMarkdown(warpRoot(contents));
14
- if (await checker(markdown) || contents.length === 1) {
10
+ const accepted = await checker(markdown);
11
+ signal?.throwIfAborted();
12
+ if (accepted || contents.length === 1) {
15
13
  return [contents];
16
14
  }
17
15
  const chunked = splitter ? contents.reduce((acc, content) => {
@@ -22,11 +20,26 @@ var _mdastSplitter = async (contents, checker, splitterGenerator) => {
22
20
  acc[acc.length - 1].push(content);
23
21
  return acc;
24
22
  }, []) : chunk(contents, Math.ceil(contents.length / 2));
25
- const splitting = chunked.map((chunk2) => _mdastSplitter(chunk2, checker, splitterGenerator));
23
+ const splitting = chunked.map((chunk2) => _mdastSplitter(chunk2, checker, priority + 1, signal));
26
24
  return Promise.all(splitting).then((chunks) => chunks.flat());
27
25
  };
28
- var mdastSplitter = (mdast, checker, _options) => {
29
- return _mdastSplitter(unwarpRoot(mdast), checker, getSplitterGenerator());
26
+ var mdastSplitter = async (mdast, checker, options) => {
27
+ const signal = options?.signal;
28
+ if (!signal) {
29
+ return _mdastSplitter(unwarpRoot(mdast), checker, 0);
30
+ }
31
+ signal.throwIfAborted();
32
+ let onAbort = () => {
33
+ };
34
+ const aborted = new Promise((_, reject) => {
35
+ onAbort = () => reject(signal.reason);
36
+ signal.addEventListener("abort", onAbort, { once: true });
37
+ });
38
+ try {
39
+ return await Promise.race([_mdastSplitter(unwarpRoot(mdast), checker, 0, signal), aborted]);
40
+ } finally {
41
+ signal.removeEventListener("abort", onAbort);
42
+ }
30
43
  };
31
44
  export {
32
45
  mdastSplitter
@@ -1,20 +1,41 @@
1
1
  // src/mdast-handlers/custom-code-handler.ts
2
- import { toText } from "hast-util-to-text";
3
2
  import { trimTrailingLines } from "trim-trailing-lines";
4
3
  import { detectLanguage } from "../utils/detect-code-lang.js";
5
- var LANGUAGE_MATCH_REGEX = [/language-(\w+)/, /highlight-source-(\w+)/, /CodeBlock--language-(\w+)/];
4
+ import { classList, isElement } from "../utils/hast-fast.js";
5
+ import { preText } from "../utils/pre-text.js";
6
+ import { cleanTwoslash } from "./twoslash.js";
7
+ var LANGUAGE_MATCH_REGEX = [/^language-(\S+)$/, /^highlight-source-(\S+)$/, /^CodeBlock--language-(\S+)$/];
8
+ var LANGUAGE_ATTRIBUTES = ["dataLanguage", "dataLang"];
9
+ var attributeLanguage = (element) => {
10
+ for (const name of LANGUAGE_ATTRIBUTES) {
11
+ const value = element.properties?.[name];
12
+ if (typeof value === "string" && /^[\w+#.-]+$/.test(value.trim()) && value.trim() !== "plaintext") {
13
+ return value.trim();
14
+ }
15
+ }
16
+ return void 0;
17
+ };
18
+ 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);
19
+ var codeMeta = (pre) => {
20
+ const title = pre.properties?.dataTitle;
21
+ if (typeof title !== "string") {
22
+ return null;
23
+ }
24
+ const clean = title.replace(/["\r\n]+/g, " ").trim();
25
+ return clean ? `title="${clean}"` : null;
26
+ };
6
27
  var customCodeHandler = (state, node) => {
7
- const classNames = node.properties?.className || [];
8
- const codeValue = trimTrailingLines(toText(node)).trim();
9
- const classLang = classNames.map((className) => {
10
- const match = LANGUAGE_MATCH_REGEX.map((regex) => className.match(regex)).find((match2) => match2);
11
- return match?.[1];
12
- }).find((className) => className);
28
+ const twoslashLang = cleanTwoslash(node);
29
+ const code = node.children.find((child) => isElement(child) && child.tagName === "code");
30
+ const codeValue = trimTrailingLines(preText(node));
31
+ const classLang = twoslashLang || codeLanguage(...isElement(code) ? [code, node] : [node]);
13
32
  const lang = classLang || detectLanguage(codeValue) || null;
14
- const result = { type: "code", lang, meta: null, value: codeValue };
33
+ const result = { type: "code", lang, meta: codeMeta(node), value: codeValue };
15
34
  state.patch(node, result);
16
35
  return result;
17
36
  };
18
37
  export {
38
+ codeLanguage,
39
+ codeMeta,
19
40
  customCodeHandler
20
41
  };
@@ -2,49 +2,28 @@
2
2
  import { select } from "hast-util-select";
3
3
  import { defaultHandlers } from "hast-util-to-mdast";
4
4
  import { toString as hastToString } from "hast-util-to-string";
5
- import { toText } from "hast-util-to-text";
6
5
  import { trimTrailingLines } from "trim-trailing-lines";
7
6
  import { detectLanguage } from "../utils/detect-code-lang.js";
7
+ import { classList, collectElements, isElement } from "../utils/hast-fast.js";
8
+ import { preText } from "../utils/pre-text.js";
9
+ import { codeLanguage, codeMeta } from "./custom-code-handler.js";
8
10
  var CODE_BLOCK_REGEX = /highlight-source|language-|codegroup|codeblock|code-block/i;
9
11
  var CODE_FILENAME_SELECTORS = "[class*='fileName'],[class*='fileName'],[class*='title'],[class*='Title']";
10
- var LANGUAGE_MATCH_REGEX = [/language-(\w+)/, /highlight-source-(\w+)/, /CodeBlock--language-(\w+)/];
11
- var findRecursive = (array, condition, maxDepth = 3) => {
12
- if (maxDepth <= 0) {
13
- return null;
14
- }
15
- for (const value of array) {
16
- const result = condition(value);
17
- if (Array.isArray(result)) {
18
- return findRecursive(result, condition, maxDepth - 1);
19
- }
20
- if (result) {
21
- return value;
22
- }
23
- }
24
- return null;
25
- };
26
12
  var customDivHandler = (state, node) => {
27
- const classNames = Array.isArray(node.properties.className) ? node.properties.className : [];
28
- const codeBlock = findRecursive(node.children, (child) => {
29
- if (child.type !== "element") {
30
- return false;
31
- }
32
- if (child.tagName === "pre") {
33
- return true;
34
- }
35
- return child.children.filter((child2) => child2.type === "element");
36
- });
37
- if (codeBlock && classNames.some((className) => CODE_BLOCK_REGEX.test(className))) {
38
- const codeBlockClassNames = codeBlock.type === "element" ? codeBlock.properties.className ?? [] : [];
39
- const codeValue = trimTrailingLines(toText(codeBlock)).trim();
13
+ const classNames = classList(node);
14
+ if (!classNames.some((className) => CODE_BLOCK_REGEX.test(className))) {
15
+ return defaultHandlers.div(state, node);
16
+ }
17
+ const codeBlocks = collectElements(node, (element) => element.tagName === "pre");
18
+ const codeBlock = codeBlocks.length === 1 ? codeBlocks[0] : void 0;
19
+ if (codeBlock) {
20
+ const codeValue = trimTrailingLines(preText(codeBlock));
40
21
  const filenameElement = select(CODE_FILENAME_SELECTORS, node);
41
22
  const fileLang = filenameElement ? hastToString(filenameElement).match(/\.(\w+)$/)?.[1] : null;
42
- const classLang = [...classNames, ...codeBlockClassNames].map((className) => {
43
- const match = LANGUAGE_MATCH_REGEX.map((regex) => className.match(regex)).find((match2) => match2);
44
- return match?.[1];
45
- }).find((className) => className);
23
+ const code = codeBlock.children.find((child) => isElement(child) && child.tagName === "code");
24
+ const classLang = codeLanguage(...isElement(code) ? [code, codeBlock, node] : [codeBlock, node]);
46
25
  const lang = fileLang || classLang || detectLanguage(codeValue) || null;
47
- const result = { type: "code", lang, meta: null, value: codeValue };
26
+ const result = { type: "code", lang, meta: codeMeta(codeBlock), value: codeValue };
48
27
  state.patch(node, result);
49
28
  return result;
50
29
  }
@@ -12,11 +12,20 @@ var hasNoUsableSource = (node) => {
12
12
  }
13
13
  return false;
14
14
  };
15
+ var PLACEHOLDER_FILE = /^(?:[\w-]*[-_.])?placeholder[\w-]*\.(?:gif|png|svg|webp|jpe?g)$|^(?:spacer|blank|transparent|pixel)\.(?:gif|png)$/i;
16
+ var isUndescribedPlaceholder = (node) => {
17
+ if (stringProperty(node, "alt")?.trim() || stringProperty(node, "title")?.trim()) {
18
+ return false;
19
+ }
20
+ const src = stringProperty(node, "src") ?? "";
21
+ const file = src.split(/[?#]/)[0].split("/").pop() ?? "";
22
+ return PLACEHOLDER_FILE.test(file);
23
+ };
15
24
  var customImgHandler = (options) => (state, node) => {
16
25
  if (options?.hideImage) {
17
26
  return void 0;
18
27
  }
19
- if (hasNoUsableSource(node)) {
28
+ if (hasNoUsableSource(node) || isUndescribedPlaceholder(node)) {
20
29
  return void 0;
21
30
  }
22
31
  const image = defaultHandlers.image(state, node);
@@ -1,7 +1,8 @@
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";
4
+ import { findElement, isElement, numericProperty, walk } from "../utils/hast-fast.js";
5
+ import { flattenTableCells, tableHasCodeSamples, unfoldTable } from "./table-cell-flatten.js";
5
6
  var MAX_SPAN = 1e3;
6
7
  var clampSpans = (node) => {
7
8
  walk(node, (candidate) => {
@@ -16,6 +17,14 @@ var clampSpans = (node) => {
16
17
  }
17
18
  });
18
19
  };
20
+ var startsWithHeaderRow = (table) => {
21
+ const row = findElement(table, (element) => element.tagName === "tr");
22
+ if (!row) {
23
+ return false;
24
+ }
25
+ const cells = row.children.filter(isElement);
26
+ return cells.length > 0 && cells.every((cell) => cell.tagName === "th");
27
+ };
19
28
  var customTableHandler = (options) => (state, node) => {
20
29
  if (options?.asText) {
21
30
  const paragraph = { type: "paragraph", children: [{ type: "text", value: toText(node) }] };
@@ -23,7 +32,15 @@ var customTableHandler = (options) => (state, node) => {
23
32
  return paragraph;
24
33
  }
25
34
  clampSpans(node);
26
- return defaultHandlers.table(state, node);
35
+ const result = defaultHandlers.table(state, node);
36
+ if (!result || Array.isArray(result) || result.type !== "table") {
37
+ return result;
38
+ }
39
+ if (tableHasCodeSamples(result)) {
40
+ return unfoldTable(result, startsWithHeaderRow(node));
41
+ }
42
+ flattenTableCells(result);
43
+ return result;
27
44
  };
28
45
  export {
29
46
  customTableHandler
@@ -28,6 +28,20 @@ var hoistBoundaryWhitespace = (nodes) => {
28
28
  }
29
29
  return result;
30
30
  };
31
+ var termsAndDefinitions = (parent) => {
32
+ const found = [];
33
+ for (const child of parent.children) {
34
+ if (child.type !== "element") {
35
+ continue;
36
+ }
37
+ if (child.tagName === "dt" || child.tagName === "dd") {
38
+ found.push(child);
39
+ } else if (child.tagName === "div") {
40
+ found.push(...termsAndDefinitions(child));
41
+ }
42
+ }
43
+ return found;
44
+ };
31
45
  var definitionListHandler = (state, node) => {
32
46
  const items = [];
33
47
  let current;
@@ -42,11 +56,7 @@ var definitionListHandler = (state, node) => {
42
56
  items.push({ type: "listItem", spread: false, children: [heading, ...current.definitions] });
43
57
  current = void 0;
44
58
  };
45
- for (const child of node.children) {
46
- if (child.type !== "element") {
47
- continue;
48
- }
49
- const element = child;
59
+ for (const element of termsAndDefinitions(node)) {
50
60
  if (element.tagName === "dt") {
51
61
  if (current && current.definitions.length > 0) {
52
62
  flush();
@@ -65,7 +75,8 @@ var definitionListHandler = (state, node) => {
65
75
  }
66
76
  flush();
67
77
  if (items.length === 0) {
68
- return void 0;
78
+ const fallback = state.toFlow(state.all(node));
79
+ return fallback.length > 0 ? fallback : void 0;
69
80
  }
70
81
  const result = { type: "list", ordered: false, spread: false, children: items };
71
82
  state.patch(node, result);
@@ -0,0 +1,170 @@
1
+ // src/mdast-handlers/table-cell-flatten.ts
2
+ var PHRASING_TYPES = /* @__PURE__ */ new Set([
3
+ "text",
4
+ "emphasis",
5
+ "strong",
6
+ "delete",
7
+ "inlineCode",
8
+ "link",
9
+ "linkReference",
10
+ "image",
11
+ "imageReference",
12
+ "html",
13
+ "footnoteReference",
14
+ "inlineMath"
15
+ ]);
16
+ var PHRASING_PARENTS = /* @__PURE__ */ new Set(["emphasis", "strong", "delete", "link", "linkReference"]);
17
+ var lineBreak = () => ({ type: "html", value: "<br>" });
18
+ var needsFlattening = (nodes) => nodes.some((node) => {
19
+ if (node.type === "break") {
20
+ return true;
21
+ }
22
+ if (!PHRASING_TYPES.has(node.type)) {
23
+ return true;
24
+ }
25
+ if ((node.type === "text" || node.type === "inlineCode") && node.value.includes("\n")) {
26
+ return true;
27
+ }
28
+ return PHRASING_PARENTS.has(node.type) && "children" in node && needsFlattening(node.children);
29
+ });
30
+ var CellBuilder = class _CellBuilder {
31
+ constructor() {
32
+ this.out = [];
33
+ this.#pendingBlock = false;
34
+ }
35
+ #pendingBlock;
36
+ /** Starts a new visual line when the previous block left content on the current one. */
37
+ block() {
38
+ const last = this.out.at(-1);
39
+ if (this.#pendingBlock && last && !(last.type === "html" && last.value === "<br>")) {
40
+ this.out.push(lineBreak());
41
+ }
42
+ this.#pendingBlock = true;
43
+ }
44
+ add(nodes) {
45
+ for (const node of nodes) {
46
+ this.#addOne(node);
47
+ }
48
+ }
49
+ #addOne(node) {
50
+ switch (node.type) {
51
+ case "break":
52
+ this.out.push(lineBreak());
53
+ return;
54
+ case "text":
55
+ this.out.push({ type: "text", value: node.value.replace(/\s*\n\s*/g, " ") });
56
+ return;
57
+ case "inlineCode":
58
+ this.#codeLines(node.value);
59
+ return;
60
+ case "code":
61
+ this.block();
62
+ this.#codeLines(node.value);
63
+ return;
64
+ case "math":
65
+ this.block();
66
+ this.out.push({ type: "inlineMath", value: node.value.replace(/\s*\n\s*/g, " ") });
67
+ return;
68
+ case "list":
69
+ node.children.forEach((item, index) => {
70
+ this.block();
71
+ const marker = node.ordered ? `${(node.start ?? 1) + index}. ` : "- ";
72
+ this.out.push({ type: "text", value: marker });
73
+ this.#pendingBlock = false;
74
+ this.add(item.children);
75
+ });
76
+ return;
77
+ case "thematicBreak":
78
+ return;
79
+ default:
80
+ this.#addContainer(node);
81
+ }
82
+ }
83
+ #addContainer(node) {
84
+ if (PHRASING_PARENTS.has(node.type) && "children" in node) {
85
+ const inner = new _CellBuilder();
86
+ inner.add(node.children);
87
+ this.out.push({ ...node, children: inner.out });
88
+ return;
89
+ }
90
+ if (PHRASING_TYPES.has(node.type)) {
91
+ this.out.push(node);
92
+ return;
93
+ }
94
+ if ("children" in node) {
95
+ this.block();
96
+ this.add(node.children);
97
+ }
98
+ }
99
+ #codeLines(value) {
100
+ value.split("\n").forEach((line, index) => {
101
+ if (index > 0) {
102
+ this.out.push(lineBreak());
103
+ }
104
+ if (line.trim().length > 0) {
105
+ this.out.push({ type: "inlineCode", value: line });
106
+ }
107
+ });
108
+ }
109
+ };
110
+ var flattenTableCells = (table) => {
111
+ for (const row of table.children) {
112
+ for (const cell of row.children) {
113
+ if (!needsFlattening(cell.children)) {
114
+ continue;
115
+ }
116
+ const builder = new CellBuilder();
117
+ builder.add(cell.children);
118
+ cell.children = builder.out;
119
+ }
120
+ }
121
+ };
122
+ var hasMultilineCode = (nodes) => nodes.some(
123
+ (node) => node.type === "code" && node.value.includes("\n") || "children" in node && hasMultilineCode(node.children)
124
+ );
125
+ var tableHasCodeSamples = (table) => table.children.some((row) => row.children.some((cell) => hasMultilineCode(cell.children)));
126
+ var unfoldTable = (table, hasHeader) => {
127
+ const [first, ...rest] = table.children;
128
+ const labelled = hasHeader && rest.length > 0;
129
+ const labels = labelled && first ? first.children.map((cell) => cell.children) : [];
130
+ const rows = labelled ? rest : table.children;
131
+ const out = [];
132
+ rows.forEach((row, rowIndex) => {
133
+ if (rowIndex > 0) {
134
+ out.push({ type: "thematicBreak" });
135
+ }
136
+ row.children.forEach((cell, cellIndex) => {
137
+ const label = labels[cellIndex];
138
+ if (label && label.length > 0) {
139
+ out.push({ type: "paragraph", children: [{ type: "strong", children: label }] });
140
+ }
141
+ out.push(...asBlocks(cell.children));
142
+ });
143
+ });
144
+ return out;
145
+ };
146
+ var asBlocks = (nodes) => {
147
+ const blocks = [];
148
+ let run = [];
149
+ const flush = () => {
150
+ if (run.some((node) => node.type !== "text" || node.value.trim().length > 0)) {
151
+ blocks.push({ type: "paragraph", children: run });
152
+ }
153
+ run = [];
154
+ };
155
+ for (const node of nodes) {
156
+ if (PHRASING_TYPES.has(node.type) || node.type === "break") {
157
+ run.push(node);
158
+ } else {
159
+ flush();
160
+ blocks.push(node);
161
+ }
162
+ }
163
+ flush();
164
+ return blocks;
165
+ };
166
+ export {
167
+ flattenTableCells,
168
+ tableHasCodeSamples,
169
+ unfoldTable
170
+ };
@@ -0,0 +1,48 @@
1
+ // src/mdast-handlers/twoslash.ts
2
+ import { classList, isElement } from "../utils/hast-fast.js";
3
+ import { preText } from "../utils/pre-text.js";
4
+ var cleanTwoslash = (pre) => {
5
+ if (!classList(pre).includes("twoslash")) {
6
+ return void 0;
7
+ }
8
+ let language;
9
+ const clean = (element) => {
10
+ element.children = element.children.flatMap((child) => {
11
+ if (!isElement(child)) {
12
+ return [child];
13
+ }
14
+ const classes = classList(child);
15
+ if (classes.includes("language-id")) {
16
+ language ||= preText(child).trim() || void 0;
17
+ return [];
18
+ }
19
+ if (classes.includes("playground-link") || classes.includes("error-behind")) {
20
+ return [];
21
+ }
22
+ if (classes.includes("error")) {
23
+ return [errorLine(child)];
24
+ }
25
+ clean(child);
26
+ return [child];
27
+ });
28
+ };
29
+ clean(pre);
30
+ return language && /^[\w+#.-]+$/.test(language) ? language : void 0;
31
+ };
32
+ var errorLine = (error) => {
33
+ let code = "";
34
+ let message = "";
35
+ for (const child of error.children) {
36
+ if (isElement(child) && classList(child).includes("code")) {
37
+ code = preText(child).trim();
38
+ } else {
39
+ message += isElement(child) ? preText(child) : child.type === "text" ? child.value : "";
40
+ }
41
+ }
42
+ const prefix = /^\d+$/.test(code) ? `error TS${code}: ` : "error: ";
43
+ const lines = `${prefix}${message.trim()}`.split("\n").map((line) => `// ${line}`);
44
+ return { type: "element", tagName: "div", properties: {}, children: [{ type: "text", value: lines.join("\n") }] };
45
+ };
46
+ export {
47
+ cleanTwoslash
48
+ };
@@ -10,11 +10,8 @@ var DEFAULT_MDAST_TO_MARKDOWN_OPTIONS = {
10
10
  };
11
11
  var mdastToMarkdown = (mdast, options) => {
12
12
  const { baseUrl, ...toMarkdownOptions } = { ...DEFAULT_MDAST_TO_MARKDOWN_OPTIONS, ...options };
13
- let markdown = toMarkdown(warpRoot(mdast), toMarkdownOptions).replace(/\*\*\*\*/g, "");
14
- if (baseUrl) {
15
- markdown = linkReplacer(markdown, baseUrl);
16
- }
17
- return markdown;
13
+ const tree = warpRoot(mdast);
14
+ return toMarkdown(baseUrl ? linkReplacer(tree, baseUrl) : tree, toMarkdownOptions);
18
15
  };
19
16
  export {
20
17
  DEFAULT_MDAST_TO_MARKDOWN_OPTIONS,
@@ -92,9 +92,10 @@ var readName = (value) => {
92
92
  };
93
93
  var asString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
94
94
  var MAX_TITLE_LENGTH = 160;
95
- var asTitle = (value) => {
95
+ var MAX_DOCUMENT_TITLE_LENGTH = 300;
96
+ var asTitle = (value, maxLength = MAX_TITLE_LENGTH) => {
96
97
  const cleaned = clean(value);
97
- return cleaned && cleaned.length <= MAX_TITLE_LENGTH ? cleaned : void 0;
98
+ return cleaned && cleaned.length <= maxLength ? cleaned : void 0;
98
99
  };
99
100
  var clean = (value) => {
100
101
  if (value === void 0) {
@@ -160,7 +161,7 @@ var extractMetadata = (tree) => {
160
161
  }
161
162
  return void 0;
162
163
  };
163
- const title = asTitle(get("og:title", "twitter:title")) ?? asTitle(asString(article?.headline)) ?? asTitle(asString(article?.name)) ?? asTitle(documentTitle(tree)) ?? asTitle(firstHeading(tree));
164
+ const title = asTitle(get("og:title", "twitter:title")) ?? asTitle(asString(article?.headline)) ?? asTitle(asString(article?.name)) ?? asTitle(documentTitle(tree), MAX_DOCUMENT_TITLE_LENGTH) ?? asTitle(firstHeading(tree));
164
165
  return {
165
166
  title,
166
167
  description: clean(get("og:description", "twitter:description", "description")) ?? clean(asString(article?.description)),
@@ -1,6 +1,7 @@
1
1
  // src/normalize/index.ts
2
2
  import {
3
3
  classList,
4
+ findElement,
4
5
  isElement,
5
6
  numericProperty,
6
7
  property,
@@ -80,14 +81,16 @@ var katexDuplicates = (node) => {
80
81
  if (!(classes.includes("katex") || classes.includes("katex-display"))) {
81
82
  return [];
82
83
  }
83
- const hasMathml = node.children.some((child) => isElement(child) && classList(child).includes("katex-mathml"));
84
+ const hasMathml = node.children.some(
85
+ (child) => isElement(child) && classList(child).includes("katex-mathml") && hasMathSource(child)
86
+ );
84
87
  if (!hasMathml) {
85
88
  return [];
86
89
  }
87
90
  return node.children.filter((child) => isElement(child) && classList(child).includes("katex-html"));
88
91
  };
89
92
  var mediawikiDuplicates = (node) => {
90
- if (!classList(node).includes("mwe-math-element")) {
93
+ if (!classList(node).includes("mwe-math-element") || !hasMathSource(node)) {
91
94
  return [];
92
95
  }
93
96
  return node.children.filter(
@@ -98,8 +101,15 @@ var mathjaxDuplicates = (node) => {
98
101
  if (node.tagName !== "mjx-container") {
99
102
  return [];
100
103
  }
104
+ const hasMathml = node.children.some(
105
+ (child) => isElement(child) && child.tagName === "mjx-assistive-mml" && hasMathSource(child)
106
+ );
107
+ if (!hasMathml) {
108
+ return [];
109
+ }
101
110
  return node.children.filter((child) => isElement(child) && child.tagName !== "mjx-assistive-mml");
102
111
  };
112
+ var hasMathSource = (node) => Boolean(findElement(node, (element) => element.tagName === "math" && element.children.length > 0));
103
113
  var dedupeRenderedMath = (tree) => {
104
114
  const doomed = /* @__PURE__ */ new Set();
105
115
  walk(tree, (node) => {