webforai 4.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 (113) hide show
  1. package/README.md +22 -0
  2. package/dist/bin.js +1895 -74
  3. package/dist/cjs/extract-mdast.js +58 -0
  4. package/dist/cjs/extractors/index.js +5 -0
  5. package/dist/cjs/extractors/lib/block-features.js +465 -0
  6. package/dist/cjs/extractors/lib/block-model.generated.js +45 -0
  7. package/dist/cjs/extractors/lib/block-model.js +72 -0
  8. package/dist/cjs/extractors/lib/blocks.js +212 -0
  9. package/dist/cjs/extractors/lib/code-tabs.js +191 -0
  10. package/dist/cjs/extractors/lib/constants.js +1 -1
  11. package/dist/cjs/extractors/lib/layout-tables.js +123 -0
  12. package/dist/cjs/extractors/lib/sanitize.js +144 -28
  13. package/dist/cjs/extractors/lib/takumi-signal.js +76 -0
  14. package/dist/cjs/extractors/lib/ui-chrome.js +7 -0
  15. package/dist/cjs/extractors/presets/auto.js +3 -3
  16. package/dist/cjs/extractors/presets/kiwame.js +231 -0
  17. package/dist/cjs/extractors/presets/takumi.js +34 -3
  18. package/dist/cjs/html-to-markdown.js +58 -4
  19. package/dist/cjs/html-to-mdast.js +2 -2
  20. package/dist/cjs/index.js +4 -0
  21. package/dist/cjs/mdast-handlers/custom-code-handler.js +16 -4
  22. package/dist/cjs/mdast-handlers/custom-div-handler.js +3 -3
  23. package/dist/cjs/mdast-handlers/custom-img-handler.js +10 -1
  24. package/dist/cjs/mdast-handlers/custom-table-handler.js +18 -1
  25. package/dist/cjs/mdast-handlers/table-cell-flatten.js +195 -0
  26. package/dist/cjs/mdast-handlers/twoslash.js +71 -0
  27. package/dist/cjs/metadata/index.js +4 -3
  28. package/dist/cjs/utils/hast-fast.js +1 -1
  29. package/dist/cjs/utils/parse-html.js +113 -0
  30. package/dist/cjs/utils/pre-text.js +104 -0
  31. package/dist/extract-mdast.js +58 -0
  32. package/dist/extractors/index.js +3 -0
  33. package/dist/extractors/lib/block-features.js +437 -0
  34. package/dist/extractors/lib/block-model.generated.js +21 -0
  35. package/dist/extractors/lib/block-model.js +48 -0
  36. package/dist/extractors/lib/blocks.js +185 -0
  37. package/dist/extractors/lib/code-tabs.js +168 -0
  38. package/dist/extractors/lib/constants.js +1 -1
  39. package/dist/extractors/lib/layout-tables.js +99 -0
  40. package/dist/extractors/lib/sanitize.js +142 -28
  41. package/dist/extractors/lib/takumi-signal.js +53 -0
  42. package/dist/extractors/lib/ui-chrome.js +5 -0
  43. package/dist/extractors/presets/auto.js +3 -3
  44. package/dist/extractors/presets/kiwame.js +206 -0
  45. package/dist/extractors/presets/takumi.js +34 -3
  46. package/dist/html-to-markdown.js +57 -4
  47. package/dist/html-to-mdast.js +2 -2
  48. package/dist/index.js +4 -0
  49. package/dist/mdast-handlers/custom-code-handler.js +15 -4
  50. package/dist/mdast-handlers/custom-div-handler.js +4 -4
  51. package/dist/mdast-handlers/custom-img-handler.js +10 -1
  52. package/dist/mdast-handlers/custom-table-handler.js +19 -2
  53. package/dist/mdast-handlers/table-cell-flatten.js +170 -0
  54. package/dist/mdast-handlers/twoslash.js +48 -0
  55. package/dist/metadata/index.js +4 -3
  56. package/dist/types/cli/commands/jobs/options.d.ts +2 -2
  57. package/dist/types/cli/commands/jobs/options.js.map +1 -1
  58. package/dist/types/cli/commands/webforai/options.js.map +1 -1
  59. package/dist/types/cli/commands/webforai/run.js.map +1 -1
  60. package/dist/types/cli/constants.d.ts +3 -1
  61. package/dist/types/cli/constants.js.map +1 -1
  62. package/dist/types/extract-mdast.js.map +1 -1
  63. package/dist/types/extractors/index.d.ts +1 -0
  64. package/dist/types/extractors/index.js.map +1 -1
  65. package/dist/types/extractors/lib/block-features.d.ts +43 -0
  66. package/dist/types/extractors/lib/block-features.js.map +1 -0
  67. package/dist/types/extractors/lib/block-model.d.ts +28 -0
  68. package/dist/types/extractors/lib/block-model.generated.d.ts +5 -0
  69. package/dist/types/extractors/lib/block-model.generated.js.map +1 -0
  70. package/dist/types/extractors/lib/block-model.js.map +1 -0
  71. package/dist/types/extractors/lib/blocks.d.ts +53 -0
  72. package/dist/types/extractors/lib/blocks.js.map +1 -0
  73. package/dist/types/extractors/lib/code-tabs.d.ts +30 -0
  74. package/dist/types/extractors/lib/code-tabs.js.map +1 -0
  75. package/dist/types/extractors/lib/constants.js.map +1 -1
  76. package/dist/types/extractors/lib/layout-tables.d.ts +17 -0
  77. package/dist/types/extractors/lib/layout-tables.js.map +1 -0
  78. package/dist/types/extractors/lib/sanitize.d.ts +33 -3
  79. package/dist/types/extractors/lib/sanitize.js.map +1 -1
  80. package/dist/types/extractors/lib/takumi-signal.d.ts +12 -0
  81. package/dist/types/extractors/lib/takumi-signal.js.map +1 -0
  82. package/dist/types/extractors/lib/ui-chrome.d.ts +8 -0
  83. package/dist/types/extractors/lib/ui-chrome.js.map +1 -1
  84. package/dist/types/extractors/presets/auto.d.ts +6 -1
  85. package/dist/types/extractors/presets/auto.js.map +1 -1
  86. package/dist/types/extractors/presets/kiwame.d.ts +31 -0
  87. package/dist/types/extractors/presets/kiwame.js.map +1 -0
  88. package/dist/types/extractors/presets/takumi.js.map +1 -1
  89. package/dist/types/html-to-markdown.d.ts +9 -0
  90. package/dist/types/html-to-markdown.js.map +1 -1
  91. package/dist/types/html-to-mdast.js.map +1 -1
  92. package/dist/types/index.d.ts +1 -1
  93. package/dist/types/index.js.map +1 -1
  94. package/dist/types/mdast-handlers/custom-code-handler.d.ts +7 -0
  95. package/dist/types/mdast-handlers/custom-code-handler.js.map +1 -1
  96. package/dist/types/mdast-handlers/custom-div-handler.js.map +1 -1
  97. package/dist/types/mdast-handlers/custom-img-handler.js.map +1 -1
  98. package/dist/types/mdast-handlers/custom-table-handler.js.map +1 -1
  99. package/dist/types/mdast-handlers/table-cell-flatten.d.ts +18 -0
  100. package/dist/types/mdast-handlers/table-cell-flatten.js.map +1 -0
  101. package/dist/types/mdast-handlers/twoslash.d.ts +14 -0
  102. package/dist/types/mdast-handlers/twoslash.js.map +1 -0
  103. package/dist/types/metadata/index.js.map +1 -1
  104. package/dist/types/utils/hast-fast.d.ts +1 -1
  105. package/dist/types/utils/hast-fast.js.map +1 -1
  106. package/dist/types/utils/parse-html.d.ts +33 -0
  107. package/dist/types/utils/parse-html.js.map +1 -0
  108. package/dist/types/utils/pre-text.d.ts +12 -0
  109. package/dist/types/utils/pre-text.js.map +1 -0
  110. package/dist/utils/hast-fast.js +1 -1
  111. package/dist/utils/parse-html.js +87 -0
  112. package/dist/utils/pre-text.js +81 -0
  113. package/package.json +3 -1
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var code_tabs_exports = {};
20
+ __export(code_tabs_exports, {
21
+ CodeTabCollector: () => CodeTabCollector
22
+ });
23
+ module.exports = __toCommonJS(code_tabs_exports);
24
+ var import_hast_fast = require("../../utils/hast-fast");
25
+ const MAX_LABEL_LENGTH = 80;
26
+ class CodeTabCollector {
27
+ #groups = [];
28
+ /** Groups whose tab list's parent is still being traversed, innermost last. */
29
+ #open = [];
30
+ #isHidden;
31
+ #radioStrips = [];
32
+ /** @param isHidden - Elements whose text a reader never sees, excluded from labels. */
33
+ constructor(isHidden) {
34
+ this.#isHidden = isHidden;
35
+ }
36
+ #applyRadioStrip(strip, parent) {
37
+ const labels = strip.children.filter((child) => (0, import_hast_fast.isElement)(child) && child.tagName === "label");
38
+ const panels = radioStripPanels(strip, parent);
39
+ if (!panels || panels.length !== labels.length) {
40
+ return;
41
+ }
42
+ panels.forEach((panel, index) => labelCode(panel, tabLabel(labels[index], this.#isHidden)));
43
+ parent.children.splice(parent.children.indexOf(strip), 1);
44
+ }
45
+ /**
46
+ * Call for every element, in document order, with its depth.
47
+ *
48
+ * A group only collects panels inside the tab list's parent: a page-level tab strip must not
49
+ * lend its labels to an unrelated code switcher further down, and an inner group closes before
50
+ * the outer group's next panel.
51
+ */
52
+ visit(element, depth, parent) {
53
+ if (isRadioTabStrip(element)) {
54
+ this.#radioStrips.push({ strip: element, parent });
55
+ return;
56
+ }
57
+ while (this.#open.length > 0 && depth < (this.#open.at(-1)?.depth ?? 0)) {
58
+ this.#open.pop();
59
+ }
60
+ const role = (0, import_hast_fast.stringProperty)(element, "role");
61
+ if (role === "tablist") {
62
+ const group2 = { tabs: [], panels: [] };
63
+ this.#groups.push(group2);
64
+ this.#open.push({ group: group2, depth });
65
+ return;
66
+ }
67
+ const group = this.#open.at(-1)?.group;
68
+ if (!group) {
69
+ return;
70
+ }
71
+ if (role === "tab") {
72
+ group.tabs.push(element);
73
+ } else if (role === "tabpanel") {
74
+ group.panels.push(element);
75
+ }
76
+ }
77
+ /** Labels the code block of every panel whose tab could be identified. */
78
+ apply() {
79
+ for (const { strip, parent } of this.#radioStrips) {
80
+ this.#applyRadioStrip(strip, parent);
81
+ }
82
+ for (const group of this.#groups) {
83
+ if (group.tabs.length < 2 || group.panels.length === 0) {
84
+ continue;
85
+ }
86
+ for (const [panel, tab] of matchTabs(group)) {
87
+ labelCode(panel, tabLabel(tab, this.#isHidden));
88
+ }
89
+ }
90
+ }
91
+ }
92
+ const radioStripPanels = (strip, parent) => {
93
+ const index = parent.children.indexOf(strip);
94
+ for (let cursor = index + 1; cursor < parent.children.length; cursor++) {
95
+ const sibling = parent.children[cursor];
96
+ if ((0, import_hast_fast.isElement)(sibling)) {
97
+ return sibling.children.filter(import_hast_fast.isElement);
98
+ }
99
+ }
100
+ return void 0;
101
+ };
102
+ const isRadioTabStrip = (element) => {
103
+ const first = element.children.find(import_hast_fast.isElement);
104
+ if (!(first?.tagName === "input" && (0, import_hast_fast.stringProperty)(first, "type") === "radio")) {
105
+ return false;
106
+ }
107
+ let labels = 0;
108
+ for (const child of element.children) {
109
+ if (!(0, import_hast_fast.isElement)(child)) {
110
+ continue;
111
+ }
112
+ if (child.tagName === "label") {
113
+ labels += 1;
114
+ } else if (!(child.tagName === "input" && (0, import_hast_fast.stringProperty)(child, "type") === "radio")) {
115
+ return false;
116
+ }
117
+ }
118
+ return labels >= 2;
119
+ };
120
+ const indexById = (elements) => {
121
+ const byId = /* @__PURE__ */ new Map();
122
+ for (const element of elements) {
123
+ const id = (0, import_hast_fast.stringProperty)(element, "id");
124
+ if (id) {
125
+ byId.set(id, element);
126
+ }
127
+ }
128
+ return byId;
129
+ };
130
+ const matchTabs = ({ tabs, panels }) => {
131
+ const tabById = indexById(tabs);
132
+ const panelById = indexById(panels);
133
+ const tabOf = /* @__PURE__ */ new Map();
134
+ for (const panel of panels) {
135
+ const tab = tabById.get((0, import_hast_fast.stringProperty)(panel, "ariaLabelledBy") ?? "");
136
+ if (tab) {
137
+ tabOf.set(panel, tab);
138
+ }
139
+ }
140
+ for (const tab of tabs) {
141
+ const panel = panelById.get((0, import_hast_fast.stringProperty)(tab, "ariaControls") ?? "");
142
+ if (panel && !tabOf.has(panel)) {
143
+ tabOf.set(panel, tab);
144
+ }
145
+ }
146
+ if (tabOf.size === 0 && tabs.length === panels.length) {
147
+ panels.forEach((panel, index) => tabOf.set(panel, tabs[index]));
148
+ }
149
+ return tabOf;
150
+ };
151
+ const tabLabel = (tab, isHidden) => {
152
+ const label = (textOf(tab, isHidden) || (0, import_hast_fast.stringProperty)(tab, "dataTitle") || "").replace(/\s+/g, " ").trim();
153
+ return label.length > 0 && label.length <= MAX_LABEL_LENGTH ? label : void 0;
154
+ };
155
+ const labelCode = (panel, label) => {
156
+ if (!label) {
157
+ return;
158
+ }
159
+ const blocks = [];
160
+ collectPre(panel, blocks);
161
+ if (blocks.length === 1 && (0, import_hast_fast.stringProperty)(blocks[0], "dataTitle") === void 0) {
162
+ blocks[0].properties.dataTitle = label;
163
+ }
164
+ };
165
+ const collectPre = (element, into) => {
166
+ for (const child of element.children) {
167
+ if (!(0, import_hast_fast.isElement)(child) || into.length > 1) {
168
+ continue;
169
+ }
170
+ if (child.tagName === "pre") {
171
+ into.push(child);
172
+ continue;
173
+ }
174
+ collectPre(child, into);
175
+ }
176
+ };
177
+ const textOf = (element, isHidden) => {
178
+ let text = "";
179
+ for (const child of element.children) {
180
+ if (child.type === "text") {
181
+ text += child.value;
182
+ } else if ((0, import_hast_fast.isElement)(child) && !isHidden(child)) {
183
+ text += textOf(child, isHidden);
184
+ }
185
+ }
186
+ return text;
187
+ };
188
+ // Annotate the CommonJS export names for ESM import in node:
189
+ 0 && (module.exports = {
190
+ CodeTabCollector
191
+ });
@@ -43,7 +43,7 @@ const REGEXPS = {
43
43
  * `class="post-header"`), so matching them here deletes the page's headings. The landmark
44
44
  * versions of those elements are handled structurally instead.
45
45
  */
46
- unlikelyCandidates: /-ad-|ai2html|banner|breadcrumb|combx|comment(?!ary)|community|cover-wrap|disqus|gdpr|legends|menu|related|remark|replies|rss|shoutbox|sidebar|site-index|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote|speechify-ignore|avatar|subscribe|newsletter|paywall|consent|cookie|toolbar|masthead|promo|share|sharing|widget|tooltip|dropdown|modal|overlay|backdrop|carousel|announcement|notification|toast|skip-link|screen-reader|visually-hidden|sr-only/i,
46
+ unlikelyCandidates: /-ad-|ai2html|banner|breadcrumb|combx|comment(?!ary)|community|cover-wrap|disqus|gdpr|legends|menu|related|remark|replies|rss|shoutbox|sidebar|site-index|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote|speechify-ignore|avatar|subscribe|newsletter|paywall|consent|cookie|toolbar|masthead|promo|share|sharing|widget|tooltip|dropdown|modal|overlay|backdrop|carousel|announcement|notification|toast|skip-link|screen-reader|(?<!not-)visually-hidden|(?<!not-)sr-only/i,
47
47
  /** Rescues containers whose class merely happens to contain an unlikely substring. */
48
48
  okMaybeItsaCandidate: /and|article|body|column|content|main|shadow|code|post|entry|story|markdown|prose|doc/i,
49
49
  /** Very high-confidence boilerplate, not rescued by the clause above. */
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var layout_tables_exports = {};
20
+ __export(layout_tables_exports, {
21
+ isLayoutTable: () => isLayoutTable,
22
+ unwrapLayoutTables: () => unwrapLayoutTables
23
+ });
24
+ module.exports = __toCommonJS(layout_tables_exports);
25
+ var import_hast_fast = require("../../utils/hast-fast");
26
+ const DATA_MARKERS = /* @__PURE__ */ new Set(["caption", "thead", "tfoot", "th", "col", "colgroup"]);
27
+ const BLOCK_IN_CELL = /* @__PURE__ */ new Set(["h1", "h2", "h3", "h4", "h5", "h6", "table", "form"]);
28
+ const TABLE_PARTS = /* @__PURE__ */ new Set(["tbody", "thead", "tfoot", "tr", "td", "th"]);
29
+ const TABLE_CONTEXT = /* @__PURE__ */ new Set(["table", "tbody", "thead", "tfoot", "tr"]);
30
+ const shapeOf = (table) => {
31
+ const shape = { rows: 0, columns: 0, dataMarker: false, blockInCell: false };
32
+ const visit = (node, inCell) => {
33
+ for (const child of node.children) {
34
+ if (!(0, import_hast_fast.isElement)(child)) {
35
+ continue;
36
+ }
37
+ const tag = child.tagName;
38
+ if (tag === "table") {
39
+ shape.blockInCell = shape.blockInCell || inCell;
40
+ continue;
41
+ }
42
+ if (DATA_MARKERS.has(tag)) {
43
+ shape.dataMarker = true;
44
+ }
45
+ if (tag === "tr") {
46
+ shape.rows += 1;
47
+ const cells = child.children.filter(
48
+ (cell) => (0, import_hast_fast.isElement)(cell) && (cell.tagName === "td" || cell.tagName === "th")
49
+ ).length;
50
+ shape.columns = Math.max(shape.columns, cells);
51
+ }
52
+ if (inCell && BLOCK_IN_CELL.has(tag)) {
53
+ shape.blockInCell = true;
54
+ }
55
+ visit(child, inCell || tag === "td" || tag === "th");
56
+ }
57
+ };
58
+ visit(table, false);
59
+ return shape;
60
+ };
61
+ const isLayoutTable = (table) => {
62
+ const role = (0, import_hast_fast.stringProperty)(table, "role");
63
+ if (role === "presentation" || role === "none" || (0, import_hast_fast.stringProperty)(table, "datatable") === "0") {
64
+ return true;
65
+ }
66
+ const shape = shapeOf(table);
67
+ if (shape.blockInCell) {
68
+ return true;
69
+ }
70
+ if (shape.dataMarker) {
71
+ return false;
72
+ }
73
+ if (shape.rows <= 1 || shape.columns <= 1) {
74
+ return true;
75
+ }
76
+ if (shape.rows >= 10 || shape.columns > 4) {
77
+ return false;
78
+ }
79
+ return shape.rows * shape.columns <= 10;
80
+ };
81
+ const unwrap = (table) => {
82
+ const cells = [];
83
+ const collect = (node) => {
84
+ for (const child of node.children) {
85
+ if (!(0, import_hast_fast.isElement)(child)) {
86
+ continue;
87
+ }
88
+ if (child.tagName === "td" || child.tagName === "th") {
89
+ cells.push({ type: "element", tagName: "div", properties: {}, children: child.children });
90
+ } else if (child.tagName !== "table") {
91
+ collect(child);
92
+ }
93
+ }
94
+ };
95
+ collect(table);
96
+ return { type: "element", tagName: "div", properties: {}, children: cells };
97
+ };
98
+ const unwrapLayoutTables = (root) => {
99
+ const visit = (parent) => {
100
+ const parentTag = (0, import_hast_fast.isElement)(parent) ? parent.tagName : "";
101
+ for (let index = 0; index < parent.children.length; index++) {
102
+ const child = parent.children[index];
103
+ if (!(0, import_hast_fast.isElement)(child)) {
104
+ continue;
105
+ }
106
+ if (TABLE_PARTS.has(child.tagName) && !TABLE_CONTEXT.has(parentTag)) {
107
+ child.tagName = "div";
108
+ }
109
+ visit(child);
110
+ if (child.tagName === "table" && isLayoutTable(child)) {
111
+ parent.children[index] = unwrap(child);
112
+ }
113
+ }
114
+ };
115
+ if ("children" in root) {
116
+ visit(root);
117
+ }
118
+ };
119
+ // Annotate the CommonJS export names for ESM import in node:
120
+ 0 && (module.exports = {
121
+ isLayoutTable,
122
+ unwrapLayoutTables
123
+ });
@@ -18,6 +18,7 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var sanitize_exports = {};
20
20
  __export(sanitize_exports, {
21
+ REFERENCE_HEADING: () => REFERENCE_HEADING,
21
22
  cleanContent: () => cleanContent,
22
23
  dropEmptyElements: () => dropEmptyElements,
23
24
  findCleanupTargets: () => findCleanupTargets,
@@ -25,30 +26,46 @@ __export(sanitize_exports, {
25
26
  findUnlikelyElements: () => findUnlikelyElements,
26
27
  hoistNoscriptImages: () => hoistNoscriptImages,
27
28
  stripNonContent: () => stripNonContent,
28
- stripUnlikely: () => stripUnlikely
29
+ stripUnlikely: () => stripUnlikely,
30
+ unlikelyTier: () => unlikelyTier
29
31
  });
30
32
  module.exports = __toCommonJS(sanitize_exports);
31
33
  var import_hast_fast = require("../../utils/hast-fast");
34
+ var import_code_tabs = require("./code-tabs");
32
35
  var import_constants = require("./constants");
33
36
  var import_ui_chrome = require("./ui-chrome");
34
37
  const INVISIBLE_STYLE = /(^|;)\s*(display\s*:\s*none|visibility\s*:\s*hidden)\s*(;|$)/i;
35
38
  const hasHiddenClass = (element) => {
36
39
  const classes = (0, import_hast_fast.classList)(element);
37
40
  if (!classes.some((name) => import_constants.HIDDEN_CLASS_NAMES.has(name))) {
38
- return classes.some((name) => import_constants.REGEXPS.hidden.test(name));
41
+ return classes.some((name) => import_constants.REGEXPS.hidden.test(name) || isScreenReaderOnlyClass(name));
39
42
  }
40
43
  return !classes.some((name) => import_constants.RESPONSIVE_DISPLAY_OVERRIDE.test(name));
41
44
  };
45
+ const SCREEN_READER_ONLY = /^(sronly|visuallyhidden|screenreader(only|text))(focusable|styles\w*)?$/;
46
+ const isScreenReaderOnlyClass = (name) => {
47
+ const first = name.charCodeAt(0) | 32;
48
+ if (first !== 115 && first !== 118) {
49
+ return false;
50
+ }
51
+ return SCREEN_READER_ONLY.test(name.toLowerCase().replace(/[-_]/g, ""));
52
+ };
42
53
  const isNonContent = (node) => {
43
54
  if (node.type === "comment" || node.type === "doctype") {
44
55
  return true;
45
56
  }
46
57
  return (0, import_hast_fast.isElement)(node) && import_constants.NON_CONTENT_TAGS.has(node.tagName);
47
58
  };
48
- const isHidden = (element) => {
59
+ const isHidden = (element, parent, siblingCode) => {
60
+ if (!looksHidden(element)) {
61
+ return false;
62
+ }
49
63
  if (containsMath(element)) {
50
64
  return false;
51
65
  }
66
+ return !isHiddenCodePanel(element, parent, siblingCode);
67
+ };
68
+ const looksHidden = (element) => {
52
69
  if ((0, import_hast_fast.isTruthyAttribute)(element, "hidden")) {
53
70
  return true;
54
71
  }
@@ -61,10 +78,57 @@ const isHidden = (element) => {
61
78
  }
62
79
  const width = (0, import_hast_fast.numericProperty)(element, "data-rwidth");
63
80
  const height = (0, import_hast_fast.numericProperty)(element, "data-rheight");
64
- if (width === 0 && height === 0) {
81
+ return width === 0 && height === 0;
82
+ };
83
+ const MAX_CODE_PANEL_PROSE = 200;
84
+ const panelCode = (element) => {
85
+ let code = "";
86
+ let prose = 0;
87
+ const visit = (node, insideCode) => {
88
+ for (const child of node.children) {
89
+ if (child.type === "text") {
90
+ if (insideCode) {
91
+ code += child.value;
92
+ } else {
93
+ prose += child.value.trim().length;
94
+ if (prose > MAX_CODE_PANEL_PROSE) {
95
+ return false;
96
+ }
97
+ }
98
+ } else if ((0, import_hast_fast.isElement)(child) && !visit(child, insideCode || child.tagName === "pre")) {
99
+ return false;
100
+ }
101
+ }
65
102
  return true;
103
+ };
104
+ return visit(element, element.tagName === "pre") && code.trim().length > 0 ? code : void 0;
105
+ };
106
+ const visibleSiblingCode = (parent, cache) => {
107
+ let codes = cache.get(parent);
108
+ if (!codes) {
109
+ codes = /* @__PURE__ */ new Set();
110
+ for (const child of parent.children) {
111
+ if ((0, import_hast_fast.isElement)(child) && !looksHidden(child)) {
112
+ const code = panelCode(child);
113
+ if (code !== void 0) {
114
+ codes.add(code);
115
+ }
116
+ }
117
+ }
118
+ cache.set(parent, codes);
66
119
  }
67
- return false;
120
+ return codes;
121
+ };
122
+ const isHiddenCodePanel = (element, parent, cache) => {
123
+ const code = panelCode(element);
124
+ if (code === void 0) {
125
+ return false;
126
+ }
127
+ const siblings = visibleSiblingCode(parent, cache);
128
+ if (siblings.has(code)) {
129
+ return false;
130
+ }
131
+ return (0, import_hast_fast.stringProperty)(element, "role") === "tabpanel" || siblings.size > 0;
68
132
  };
69
133
  const PLACEHOLDER_IMAGE_SRC = /^data:image\/|^about:blank$|spacer\.gif$|blank\.(gif|png)$/i;
70
134
  const isPlaceholderImage = (element) => {
@@ -82,15 +146,16 @@ const isPlaceholderImage = (element) => {
82
146
  }
83
147
  return false;
84
148
  };
85
- const hoistNoscriptImages = (tree) => {
86
- const visit = (parent) => {
149
+ const hoistNoscriptImages = (tree, onElement) => {
150
+ const visit = (parent, depth) => {
87
151
  for (let index = 0; index < parent.children.length; index++) {
88
152
  const child = parent.children[index];
89
153
  if (!(0, import_hast_fast.isElement)(child)) {
90
154
  continue;
91
155
  }
92
156
  if (child.tagName !== "noscript") {
93
- visit(child);
157
+ onElement?.(child, depth, parent);
158
+ visit(child, depth + 1);
94
159
  continue;
95
160
  }
96
161
  const hasImage = child.children.some(
@@ -110,7 +175,7 @@ const hoistNoscriptImages = (tree) => {
110
175
  }
111
176
  };
112
177
  if ("children" in tree) {
113
- visit(tree);
178
+ visit(tree, 0);
114
179
  }
115
180
  };
116
181
  const containsMath = (element) => {
@@ -125,12 +190,15 @@ const containsMath = (element) => {
125
190
  return false;
126
191
  };
127
192
  const stripNonContent = (tree) => {
128
- hoistNoscriptImages(tree);
129
- return (0, import_hast_fast.pruneInPlace)(tree, (node) => {
193
+ const tabs = new import_code_tabs.CodeTabCollector((element) => looksHidden(element) || import_constants.NON_CONTENT_TAGS.has(element.tagName));
194
+ hoistNoscriptImages(tree, (element, depth, parent) => tabs.visit(element, depth, parent));
195
+ tabs.apply();
196
+ const siblingCode = /* @__PURE__ */ new WeakMap();
197
+ return (0, import_hast_fast.pruneInPlace)(tree, (node, parent) => {
130
198
  if (isNonContent(node)) {
131
199
  return false;
132
200
  }
133
- if ((0, import_hast_fast.isElement)(node) && isHidden(node)) {
201
+ if ((0, import_hast_fast.isElement)(node) && isHidden(node, parent, siblingCode)) {
134
202
  return false;
135
203
  }
136
204
  return true;
@@ -157,32 +225,32 @@ const findLandmarkChrome = (tree) => {
157
225
  visit(tree, 0, false);
158
226
  return chrome;
159
227
  };
160
- const isUnlikelyCandidate = (element) => {
228
+ const unlikelyTier = (element) => {
161
229
  if (element.tagName === "article" || element.tagName === "main" || element.tagName === "body") {
162
- return false;
230
+ return void 0;
163
231
  }
164
232
  const role = (0, import_hast_fast.stringProperty)(element, "role");
165
233
  if (role && import_constants.UNLIKELY_ROLES.has(role)) {
166
- return true;
234
+ return "strong";
167
235
  }
168
236
  if (import_constants.CHROME_TAGS.has(element.tagName)) {
169
- return true;
237
+ return "strong";
170
238
  }
171
239
  const match = (0, import_hast_fast.matchString)(element);
172
240
  if (import_constants.REGEXPS.specialUnlikelyCandidates.test(match)) {
173
- return true;
241
+ return "strong";
174
242
  }
175
243
  for (const name of [...(0, import_hast_fast.classList)(element), (0, import_hast_fast.stringProperty)(element, "id") ?? ""]) {
176
244
  if (import_constants.REGEXPS.stronglyUnlikely.test(name)) {
177
- return true;
245
+ return "strong";
178
246
  }
179
247
  }
180
248
  if (import_constants.REGEXPS.unlikelyCandidates.test(match) && !import_constants.REGEXPS.okMaybeItsaCandidate.test(match)) {
181
- return true;
249
+ return "weak";
182
250
  }
183
- return false;
251
+ return void 0;
184
252
  };
185
- const findUnlikelyElements = (tree) => {
253
+ const findUnlikelyElements = (tree, spareWeak) => {
186
254
  const landmarks = findLandmarkChrome(tree);
187
255
  const doomed = [];
188
256
  const visit = (node) => {
@@ -193,7 +261,15 @@ const findUnlikelyElements = (tree) => {
193
261
  if (!(0, import_hast_fast.isElement)(child)) {
194
262
  continue;
195
263
  }
196
- if (landmarks.has(child) || isUnlikelyCandidate(child)) {
264
+ if (landmarks.has(child)) {
265
+ doomed.push(child);
266
+ continue;
267
+ }
268
+ if (PREFORMATTED_TAGS.has(child.tagName)) {
269
+ continue;
270
+ }
271
+ const tier = unlikelyTier(child);
272
+ if (tier === "strong" || tier === "weak" && !spareWeak?.(child)) {
197
273
  doomed.push(child);
198
274
  continue;
199
275
  }
@@ -338,11 +414,15 @@ const findCleanupTargets = (tree, collector) => {
338
414
  visit(child);
339
415
  continue;
340
416
  }
417
+ if (LITERAL_TEXT_TAGS.has(child.tagName)) {
418
+ collectButtons(child, doomed);
419
+ continue;
420
+ }
341
421
  if (isLinkOnlyBlock(child, collector)) {
342
422
  condemnLinkBlock(child, siblings, index, collector, doomed);
343
423
  continue;
344
424
  }
345
- if (isMidArticleWidget(child)) {
425
+ if (isMidArticleWidget(child, collector)) {
346
426
  doomed.push(child);
347
427
  continue;
348
428
  }
@@ -384,18 +464,52 @@ const countAnchors = (element) => {
384
464
  return count;
385
465
  };
386
466
  const LITERAL_TEXT_TAGS = /* @__PURE__ */ new Set(["code", "kbd", "samp", "var", "pre"]);
387
- const isMidArticleWidget = (element) => {
388
- if (LITERAL_TEXT_TAGS.has(element.tagName)) {
389
- return false;
467
+ const PREFORMATTED_TAGS = /* @__PURE__ */ new Set(["pre", "code"]);
468
+ const collectButtons = (element, into) => {
469
+ for (const child of element.children) {
470
+ if (!(0, import_hast_fast.isElement)(child)) {
471
+ continue;
472
+ }
473
+ if (child.tagName === "button") {
474
+ into.push(child);
475
+ continue;
476
+ }
477
+ collectButtons(child, into);
390
478
  }
479
+ };
480
+ const isMidArticleWidget = (element, collector) => {
391
481
  if (element.tagName === "form" || element.tagName === "fieldset") {
392
482
  return true;
393
483
  }
394
484
  if (element.tagName === "button") {
395
485
  return true;
396
486
  }
397
- return (0, import_ui_chrome.isUiChromeText)(elementText(element));
487
+ if (collector.metrics(element).text > import_ui_chrome.MAX_CONSENT_PLACEHOLDER_LENGTH) {
488
+ return false;
489
+ }
490
+ const text = elementText(element);
491
+ return (0, import_ui_chrome.isUiChromeText)(text) || (0, import_ui_chrome.isConsentPlaceholderText)(text) && !hasBlockDescendant(element);
398
492
  };
493
+ const BLOCK_TAGS = /* @__PURE__ */ new Set([
494
+ "p",
495
+ "div",
496
+ "section",
497
+ "article",
498
+ "figure",
499
+ "figcaption",
500
+ "blockquote",
501
+ "ul",
502
+ "ol",
503
+ "table",
504
+ "pre",
505
+ "h1",
506
+ "h2",
507
+ "h3",
508
+ "h4",
509
+ "h5",
510
+ "h6"
511
+ ]);
512
+ const hasBlockDescendant = (element) => element.children.some((child) => (0, import_hast_fast.isElement)(child) && (BLOCK_TAGS.has(child.tagName) || hasBlockDescendant(child)));
399
513
  const cleanContent = (tree, collector) => {
400
514
  const doomed = new Set(findCleanupTargets(tree, collector));
401
515
  if (doomed.size > 0) {
@@ -418,6 +532,7 @@ const elementText = (node) => {
418
532
  };
419
533
  // Annotate the CommonJS export names for ESM import in node:
420
534
  0 && (module.exports = {
535
+ REFERENCE_HEADING,
421
536
  cleanContent,
422
537
  dropEmptyElements,
423
538
  findCleanupTargets,
@@ -425,5 +540,6 @@ const elementText = (node) => {
425
540
  findUnlikelyElements,
426
541
  hoistNoscriptImages,
427
542
  stripNonContent,
428
- stripUnlikely
543
+ stripUnlikely,
544
+ unlikelyTier
429
545
  });