8000 ax-grep/src/static.ts at v0.1.4 · hmmhmmhm/ax-grep · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
1130 lines (1028 loc) · 42.9 KB

static.ts

File metadata and controls

1130 lines (1028 loc) · 42.9 KB
if (attr(element, "disabled") !== null) return false;
const tabindex = attr(element, "tabindex");
if (tabindex !== null && Number(tabindex) >= 0) return true;
if (role && interactiveRoles.has(role)) return true;
return element.name === "a" && attr(element, "href") !== null;
}
function isHidden(element: Element): boolean {
if (attr(element, "hidden") !== null) return true;
if (attr(element, "aria-hidden") === "true") return true;
const style = attr(element, "style");
return style ? hiddenStylePattern.test(style) : false;
}
function isLikelyAd(element: Element): boolean {
const value = [
attr(element, "id"),
attr(element, "class"),
attr(element, "aria-label"),
attr(element, "title"),
attr(element, "data-testid"),
].filter(Boolean).join(" ").toLowerCase();
return /\b(ad|ads|advert|advertisement|banner|sponsor|sponsored|promotion|promoted|powerlink)\b/.test(value)
|| /파워링크|광고|직접홍보|홍보/.test(value);
}
function isLikelyBoilerplate(element: Element): boolean {
if (element.name === "footer") return true;
if (element.name === "main" || element.name === "article") return false;
const role = firstToken(attr(element, "role"));
if (role === "main" || role === "article") return false;
const value = [
attr(element, "id"),
attr(element, "class"),
attr(element, "aria-label"),
attr(element, "title"),
].filter(Boolean).join(" ").toLowerCase();
if (!value) return false;
if (/\b(content|contents|entry|post-body|article-body|story-body|view-content)\b/.test(value)) return false;
return /\b(footer|sidebar)\b/.test(value) || /푸터/.test(value);
}
function isLikelyBoilerplateTable(element: Element): boolean {
if (element.name !== "table") return false;
const value = [
attr(element, "id"),
attr(element, "class"),
attr(element, "aria-label"),
attr(element, "title"),
].filter(Boolean).join(" ").toLowerCase();
return /\bgall[_-]?list\b/.test(value) || /\bbottom[_-]?list\w*\b/.test(value);
}
function flattenBoilerplateTable(element: Element, context: StaticContext): SemanticNode | null {
const children = collectFlattenedBoilerplateItems(element, context);
if (children.length === 0) return null;
return containerNode(context, element.name, children);
}
function collectFlattenedBoilerplateItems(element: Element, context: StaticContext): SemanticNode[] {
const children: SemanticNode[] = [];
for (const child of element.children) {
if (!isElement(child)) continue;
if (shouldSkipElement(child, context)) continue;
if (!context.options.includeHidden && isHidden(child)) continue;
if (context.options.excludeLikelyAds && isLikelyAd(child)) continue;
const role = getRole(child);
const focusable = isFocusable(child, role);
const interactive = isInteractive(child, role, focusable);
const name = role ? computeName(child, role, context) : "";
if (role && name && (interactive || role === "heading" || role === "img")) {
const node: SemanticNode = {
id: nextId(context),
tag: child.name,
role,
name,
interactive,
focusable,
selector: getSelector(child),
xpath: getXPath(child),
children: [],
};
const description = computeDescription(child, context);
if (description) node.description = description;
const value = getValue(child);
if (value) node.value = value;
const state = getState(child);
if (Object.keys(state).length > 0) node.state = state;
if (context.options.includeAttributes) node.attributes = { ...child.attribs };
children.push(node);
continue;
}
children.push(...collectFlattenedBoilerplateItems(child, context));
}
return children;
}
function hasExplicitNameSource(element: Element): boolean {
return attr(element, "aria-label") !== null || attr(element, "aria-labelledby") !== null || attr(element, "title") !== null;
}
function directText(element: Element, maxLength: number): string {
return normalizeText(
element.children
.filter(isText)
.map((node) => node.data)
.join(" "),
maxLength,
);
}
function descendantText(element: Element, context?: StaticContext, options: DescendantTextOptions = {}): string {
const parts: string[] = [];
const shadowTemplate = element.children.find((child): child is Element => isElement(child) && isDeclarativeShadowTemplate(child));
if (shadowTemplate && context) {
const previousAssignments = context.slotAssignments;
context.slotAssignments = collectSlotAssignments(element);
collectDescendantText(shadowTemplate.children, parts, context, options);
context.slotAssignments = previousAssignments;
return parts.join(" ");
}
collectDescendantText(element.children, parts, context, options);
return parts.join(" ");
}
function collectDescendantText(nodes: AnyNode[], parts: string[], context?: StaticContext, options: DescendantTextOptions = {}): void {
for (const node of nodes) {
if (isText(node)) {
parts.push(node.data);
continue;
}
if (!isElement(node)) continue;
const nodeId = attr(node, "id");
if (nodeId && options.excludeIds?.has(nodeId)) continue;
if (node.name === "slot" && context?.slotAssignments) {
const slotName = attr(node, "name") ?? "";
const assigned = context.slotAssignments.get(slotName) ?? [];
collectDescendantText(assigned.length > 0 ? assigned : node.children, parts, context, options);
continue;
}
if (nonSemanticTags.has(node.name) || node.name === "noscript") continue;
collectDescendantText(node.children, parts, context, options);
}
0