File size: 1,427 Bytes
71d239c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
"""Lightweight structure for the non-code-ish files: HTML, CSS, JSON, YAML.

These rarely have "functions", but they do reference other files (an HTML page
loads scripts and stylesheets) — and those references are real edges in the
project graph, so we capture them.
"""
from __future__ import annotations

import re

from schema import Symbol

_HTML_SRC = re.compile(r"""<(?:script|link|img|a)\b[^>]*?\b(?:src|href)\s*=\s*['"]([^'"]+)['"]""",
                       re.IGNORECASE)
_CSS_IMPORT = re.compile(r"""@import\s+(?:url\()?['"]([^'"]+)['"]""")
_CSS_RULE = re.compile(r"([.#][A-Za-z_][\w-]*)\s*\{")


def parse(text: str, lang: str) -> tuple[list[Symbol], list[str]]:
    if lang == "html":
        return _parse_html(text)
    if lang == "css":
        return _parse_css(text)
    # json / yaml / markdown: no symbols, no edges we can trust cheaply.
    return [], []


def _parse_html(text: str) -> tuple[list[Symbol], list[str]]:
    refs = [r for r in _HTML_SRC.findall(text)
            if not r.startswith(("http://", "https://", "//", "data:", "#", "mailto:"))]
    seen: set[str] = set()
    uniq = [r for r in refs if not (r in seen or seen.add(r))]
    return [], uniq


def _parse_css(text: str) -> tuple[list[Symbol], list[str]]:
    selectors = _CSS_RULE.findall(text)[:12]
    symbols = [Symbol(kind="style", name=s) for s in selectors]
    return symbols, list(dict.fromkeys(_CSS_IMPORT.findall(text)))