mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-06 23:12:22 +02:00
static/js/document.js is 17,579 lines and is about to be decomposed behind a
re-export wrapper. 51 test files read it off disk and grep it as text, and 28
of those slice it with `src.split("function a", 1)[1].split("function b", 1)[0]`
-- "the region between a and b", which only means what the test intends while a
and b are neighbours in one file. Several also hard-code the file's two-space
indentation, which no extracted module reproduces. Left alone, the first
extraction makes those assertions cover the wrong region, and an `x in region`
check passes while covering more than it was written for.
tests/helpers/document_source is the one place that names the file now:
- document_source() is the entry plus everything under static/js/document/,
so a membership assertion keeps finding its subject wherever it lands;
- function_body()/declaration() locate a construct by name in whichever
module defines it and end at its real closing brace, so neither moving it
nor moving its neighbour changes the region.
The rewrite only collapses a slice when the old terminator sat at the
construct's end. 28 slices deliberately span a whole family of functions --
everything from _docxHexColor to exportAsDocx -- and collapsing one to its
first member drops what the assertions look for, so those stay as they are and
are listed in KNOWN_ADJACENCY_SLICES, to be converted as each family becomes a
module. That list may only shrink.
Two guards come with it:
- test_document_source_test_hygiene fails on a direct read of the entry file
and on any new adjacency slice;
- test_frontend_module_graph resolves every relative import under static/
(718 of them, none broken today) and requires the document module set to
stay in the sw.js precache, since the worker fetches the URLs it lists and
not what they import.
test_document_module_api pins the 38 default-export keys and 29 named exports
by loading the module in a browser and reading what it actually exports, rather
than grepping for the literal object -- after extraction that object may be
assembled from imports, and a source-shape check would pass while the export
was broken.
No JavaScript moves here. static/ is untouched.
103 lines
3.7 KiB
Python
103 lines
3.7 KiB
Python
"""Every module the frontend imports must exist, and the document set must be cached.
|
|
|
|
There is no bundler here, so nothing resolves the import graph before a browser
|
|
does. A specifier that names a file which is not there is valid JavaScript:
|
|
``node --check`` passes, and ``test_frontend_module_version_parity.py`` checks
|
|
that a module is loaded under one URL identity without checking that the URL
|
|
leads anywhere. The failure surfaces as a blank panel at runtime, and in the
|
|
test suite as a scatter of unrelated browser tests going red at once with no
|
|
mention of the missing file.
|
|
|
|
That is affordable to close statically, so this closes it.
|
|
"""
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
from tests.helpers.document_source import document_source_paths
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
STATIC = ROOT / "static"
|
|
|
|
# Vendored third-party builds and the committed editor build output are not
|
|
# ours to reason about.
|
|
_SKIP_DIRS = ("lib/", "js/editor/build/")
|
|
|
|
# `import x from '...'`, `export ... from '...'`, and `import('...')`. Only
|
|
# quoted specifiers: a template literal is not statically resolvable, and the
|
|
# app does not use one.
|
|
_SPECIFIER = re.compile(
|
|
r"""(?:^|[^\w.$])(?:import|export)\s*(?:[\w*{},\s$]*?\s*from\s*)?['"]([^'"]+)['"]"""
|
|
r"""|\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)""",
|
|
re.M,
|
|
)
|
|
|
|
|
|
def _own_scripts() -> list[Path]:
|
|
out = []
|
|
for path in sorted(STATIC.rglob("*.js")):
|
|
rel = path.relative_to(STATIC).as_posix()
|
|
if any(rel.startswith(d) or f"/{d}" in rel for d in _SKIP_DIRS):
|
|
continue
|
|
out.append(path)
|
|
return out
|
|
|
|
|
|
def _imports(path: Path):
|
|
"""(line, specifier, resolved path) for each relative/app-absolute import."""
|
|
source = path.read_text(encoding="utf-8")
|
|
for match in _SPECIFIER.finditer(source):
|
|
specifier = match.group(1) or match.group(2)
|
|
if not specifier:
|
|
continue
|
|
if not (specifier.startswith(".") or specifier.startswith("/static/")):
|
|
continue # bare specifier: not a file in this tree
|
|
bare = specifier.split("?")[0].split("#")[0]
|
|
if bare.startswith("/static/"):
|
|
target = STATIC / bare.removeprefix("/static/")
|
|
else:
|
|
target = path.parent / bare
|
|
line = source.count("\n", 0, match.start()) + 1
|
|
yield line, specifier, target
|
|
|
|
|
|
def test_sources_are_discoverable() -> None:
|
|
"""Guard the guard: this must not pass by scanning nothing."""
|
|
scripts = _own_scripts()
|
|
assert len(scripts) > 100, len(scripts)
|
|
total = sum(1 for p in scripts for _ in _imports(p))
|
|
assert total > 300, total
|
|
|
|
|
|
def test_every_frontend_import_resolves_to_a_file() -> None:
|
|
broken = [
|
|
f"{path.relative_to(ROOT)}:{line} -> {specifier}"
|
|
for path in _own_scripts()
|
|
for line, specifier, target in _imports(path)
|
|
if not target.is_file()
|
|
]
|
|
|
|
assert not broken, "imports naming files that do not exist: " + repr(broken)
|
|
|
|
|
|
def test_document_implementation_set_is_precached() -> None:
|
|
"""A module extracted out of document.js must join the offline manifest.
|
|
|
|
``static/sw.js`` fetches the URLs it lists, nothing they in turn import, so
|
|
a new module under ``static/js/document/`` is not cached just because the
|
|
entry point that imports it is. Without it the editor breaks offline for
|
|
anyone whose cache predates the split.
|
|
"""
|
|
service_worker = (STATIC / "sw.js").read_text(encoding="utf-8")
|
|
|
|
missing = []
|
|
for path in document_source_paths():
|
|
url = "/static/" + path.relative_to(STATIC).as_posix()
|
|
if not re.search(rf"['\"]{re.escape(url)}(?:\?[^'\"]*)?['\"]", service_worker):
|
|
missing.append(url)
|
|
|
|
assert not missing, (
|
|
"document editor modules absent from the sw.js precache lists: "
|
|
f"{missing}"
|
|
)
|