@@ -0,0 +1,252 @@
|
||||
#!/usr/bin/env python3
|
||||
import ast
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import tokenize
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
OUT = ROOT.parent / "исходный_код_WESP.txt"
|
||||
|
||||
SKIP_DIRS = {
|
||||
"__pycache__",
|
||||
".git",
|
||||
".pytest_cache",
|
||||
".cursor",
|
||||
"tests",
|
||||
"migrations",
|
||||
"vendor",
|
||||
"logs",
|
||||
"legacy",
|
||||
}
|
||||
SKIP_FILES = {
|
||||
"proga.py",
|
||||
}
|
||||
EXTS = {".py", ".js", ".html", ".css"}
|
||||
|
||||
|
||||
def should_skip_file(path: Path) -> bool:
|
||||
if path.name in SKIP_FILES:
|
||||
return True
|
||||
if "legacy" in path.name.lower():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def should_skip_dir(name: str) -> bool:
|
||||
return name in SKIP_DIRS
|
||||
|
||||
|
||||
def iter_source_files():
|
||||
for dirpath, dirnames, filenames in os.walk(ROOT):
|
||||
dirnames[:] = [d for d in dirnames if not should_skip_dir(d)]
|
||||
rel = Path(dirpath).relative_to(ROOT)
|
||||
if any(part in SKIP_DIRS for part in rel.parts):
|
||||
continue
|
||||
for name in sorted(filenames):
|
||||
path = Path(dirpath) / name
|
||||
if path.suffix.lower() in EXTS and not should_skip_file(path):
|
||||
yield path
|
||||
|
||||
|
||||
def _drop_docstring(body):
|
||||
if not body:
|
||||
return body
|
||||
first = body[0]
|
||||
if not isinstance(first, ast.Expr):
|
||||
return body
|
||||
val = first.value
|
||||
if isinstance(val, ast.Constant) and isinstance(val.value, str):
|
||||
return body[1:]
|
||||
return body
|
||||
|
||||
|
||||
class _DocstringStripper(ast.NodeTransformer):
|
||||
def visit_FunctionDef(self, node):
|
||||
self.generic_visit(node)
|
||||
node.body = _drop_docstring(node.body)
|
||||
return node
|
||||
|
||||
def visit_AsyncFunctionDef(self, node):
|
||||
self.generic_visit(node)
|
||||
node.body = _drop_docstring(node.body)
|
||||
return node
|
||||
|
||||
def visit_ClassDef(self, node):
|
||||
self.generic_visit(node)
|
||||
node.body = _drop_docstring(node.body)
|
||||
return node
|
||||
|
||||
def visit_Module(self, node):
|
||||
self.generic_visit(node)
|
||||
node.body = _drop_docstring(node.body)
|
||||
return node
|
||||
|
||||
|
||||
def strip_python_comments(source: str) -> str:
|
||||
out = []
|
||||
try:
|
||||
for tok in tokenize.generate_tokens(io.StringIO(source).readline):
|
||||
if tok.type == tokenize.COMMENT:
|
||||
continue
|
||||
out.append(tok)
|
||||
return tokenize.untokenize(out)
|
||||
except (tokenize.TokenError, SyntaxError):
|
||||
return source
|
||||
|
||||
|
||||
def strip_python(source: str) -> str:
|
||||
try:
|
||||
tree = ast.parse(source)
|
||||
tree = _DocstringStripper().visit(tree)
|
||||
ast.fix_missing_locations(tree)
|
||||
source = ast.unparse(tree)
|
||||
except SyntaxError:
|
||||
pass
|
||||
return strip_python_comments(source)
|
||||
|
||||
|
||||
def strip_js(source: str) -> str:
|
||||
result = []
|
||||
i = 0
|
||||
n = len(source)
|
||||
in_single = False
|
||||
in_double = False
|
||||
in_template = False
|
||||
in_line_comment = False
|
||||
in_block_comment = False
|
||||
escape = False
|
||||
|
||||
while i < n:
|
||||
ch = source[i]
|
||||
nxt = source[i + 1] if i + 1 < n else ""
|
||||
|
||||
if in_line_comment:
|
||||
if ch == "\n":
|
||||
in_line_comment = False
|
||||
result.append(ch)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if in_block_comment:
|
||||
if ch == "*" and nxt == "/":
|
||||
in_block_comment = False
|
||||
i += 2
|
||||
continue
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if in_single:
|
||||
result.append(ch)
|
||||
if escape:
|
||||
escape = False
|
||||
elif ch == "\\":
|
||||
escape = True
|
||||
elif ch == "'":
|
||||
in_single = False
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if in_double:
|
||||
result.append(ch)
|
||||
if escape:
|
||||
escape = False
|
||||
elif ch == "\\":
|
||||
escape = True
|
||||
elif ch == '"':
|
||||
in_double = False
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if in_template:
|
||||
result.append(ch)
|
||||
if escape:
|
||||
escape = False
|
||||
elif ch == "\\":
|
||||
escape = True
|
||||
elif ch == "`":
|
||||
in_template = False
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if ch == "/" and nxt == "/":
|
||||
in_line_comment = True
|
||||
i += 2
|
||||
continue
|
||||
if ch == "/" and nxt == "*":
|
||||
in_block_comment = True
|
||||
i += 2
|
||||
continue
|
||||
if ch == "'":
|
||||
in_single = True
|
||||
result.append(ch)
|
||||
i += 1
|
||||
continue
|
||||
if ch == '"':
|
||||
in_double = True
|
||||
result.append(ch)
|
||||
i += 1
|
||||
continue
|
||||
if ch == "`":
|
||||
in_template = True
|
||||
result.append(ch)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
result.append(ch)
|
||||
i += 1
|
||||
|
||||
return "".join(result)
|
||||
|
||||
|
||||
def strip_html_css(source: str) -> str:
|
||||
source = re.sub(r"<!--[\s\S]*?-->", "", source)
|
||||
source = strip_js(source)
|
||||
return source
|
||||
|
||||
|
||||
def collapse_blank_lines(text: str) -> str:
|
||||
lines = [ln.rstrip() for ln in text.splitlines()]
|
||||
cleaned = []
|
||||
blank_run = 0
|
||||
for ln in lines:
|
||||
if not ln.strip():
|
||||
blank_run += 1
|
||||
if blank_run <= 2:
|
||||
cleaned.append("")
|
||||
continue
|
||||
blank_run = 0
|
||||
cleaned.append(ln)
|
||||
return "\n".join(cleaned).strip("\n")
|
||||
|
||||
|
||||
def strip_file(path: Path) -> str:
|
||||
text = path.read_text(encoding="utf-8", errors="replace")
|
||||
ext = path.suffix.lower()
|
||||
if ext == ".py":
|
||||
body = strip_python(text)
|
||||
elif ext == ".js":
|
||||
body = strip_js(text)
|
||||
else:
|
||||
body = strip_html_css(text)
|
||||
return collapse_blank_lines(body)
|
||||
|
||||
|
||||
def main():
|
||||
chunks = []
|
||||
for path in iter_source_files():
|
||||
body = strip_file(path)
|
||||
if body:
|
||||
chunks.append(body)
|
||||
content = "\n\n".join(chunks)
|
||||
OUT.write_text(content + "\n", encoding="utf-8")
|
||||
lines = content.count("\n") + (1 if content else 0)
|
||||
print(f"Written: {OUT}")
|
||||
print(f"Files: {len(chunks)}")
|
||||
print(f"Lines: {lines}")
|
||||
print(f"Size: {OUT.stat().st_size} bytes")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user