mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-09 12:17:13 +00:00
sys-front-inj-design now renders templates/css/app.css.j2 ahead of the role's stylesheet and templates/js/design.js.j2 ahead of its script. The branding URLs, the slot dimensions and the logo guard live there instead of being repeated per role: the guard that stood in 41 sheets is one condition in the composing template, and a sheet reads branding through --design-url-* / --design-slot-* custom properties. 29 stylesheets and 43 design scripts lose their Jinja and move to static files. Roles whose sheet carries app-specific URLs declare them as design_css_urls and read them as --design-url-app-*. The script prelude is prepended only when the role script references it, and it carries urls only while a logo is configured, so every role that needs neither is emitted byte for byte as before. Five checks keep the mechanism honest: a composed sheet may only read design custom properties that app.css.j2 declares for its role, a template may not carry Jinja expression text outside its delimiters, a design script may not quote its config object, and the injected-script parse check now covers the design channel. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
91 lines
3.2 KiB
Python
91 lines
3.2 KiB
Python
"""Injected JavaScript must still parse after ``to_one_liner`` collapses it.
|
|
|
|
``sys-front-inj-javascript`` loads a role's ``javascript.js`` or its ``.j2``
|
|
template and ``sys-front-inj-design`` loads its ``design.js`` behind the
|
|
per-app config prelude, runs it through ``to_one_liner`` - which strips every
|
|
comment and newline - and inlines the result under a CSP hash. A script that
|
|
parses only
|
|
before that collapse is served broken *with a matching hash*: the browser
|
|
rejects nothing and executes nothing, so the page loses its injection without
|
|
a single error anywhere in the deploy. Parsing the collapsed form is the only
|
|
place that failure is observable.
|
|
|
|
Jinja is not rendered. Expressions become a neutral literal and statements are
|
|
dropped, which is enough to expose an ASI break or a comment-stripping defect
|
|
without standing up an inventory.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
from typing import TYPE_CHECKING
|
|
|
|
from plugins.filter.text_filters import to_one_liner
|
|
from utils.cache.files import PROJECT_ROOT, read_text
|
|
|
|
if TYPE_CHECKING:
|
|
from pathlib import Path
|
|
|
|
ROLES = PROJECT_ROOT / "roles"
|
|
PATTERNS = (
|
|
"*/templates/javascript.js.j2",
|
|
"*/templates/javascript.js",
|
|
"*/files/javascript/javascript.js",
|
|
"*/templates/design.js.j2",
|
|
"*/templates/design.js",
|
|
"*/templates/js/design.js.j2",
|
|
)
|
|
EXPRESSION = re.compile(r"\{\{.*?\}\}", re.DOTALL)
|
|
STATEMENT = re.compile(r"\{%.*?%\}", re.DOTALL)
|
|
|
|
|
|
def _sources() -> list[Path]:
|
|
return sorted({path for pattern in PATTERNS for path in ROLES.glob(pattern)})
|
|
|
|
|
|
def _have_node() -> bool:
|
|
return shutil.which("node") is not None
|
|
|
|
|
|
@unittest.skipUnless(_have_node(), "node is not available in PATH")
|
|
class TestInjectedJavascript(unittest.TestCase):
|
|
def test_every_injected_script_parses_after_one_lining(self) -> None:
|
|
sources = _sources()
|
|
self.assertTrue(
|
|
sources, f"no injected JavaScript matched {PATTERNS} under {ROLES}"
|
|
)
|
|
|
|
for path in sources:
|
|
with self.subTest(role=path.relative_to(ROLES).parts[0]):
|
|
text = read_text(str(path))
|
|
if path.name.endswith(".j2"):
|
|
text = EXPRESSION.sub("0", STATEMENT.sub("", text))
|
|
collapsed = to_one_liner(text)
|
|
self.assertNotIn("\n", collapsed)
|
|
self.assertNotIn(
|
|
"]=]",
|
|
collapsed,
|
|
"body_filter.lua.j2 embeds the snippet as a Lua [=[ ]=] long"
|
|
" string; an inner ]=] closes it early and the OpenResty"
|
|
" config stops loading for every site on the host",
|
|
)
|
|
|
|
with tempfile.NamedTemporaryFile("w", suffix=".js") as handle:
|
|
handle.write(collapsed)
|
|
handle.flush()
|
|
proc = subprocess.run(
|
|
["node", "--check", handle.name],
|
|
capture_output=True,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
self.assertEqual(proc.returncode, 0, proc.stderr)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|