mirror of
https://github.com/simonw/datasette.git
synced 2026-06-01 22:56:58 +02:00
Add content hash to JS includes (closes #2714)
The CSS link in base.html already carries `?{{ app_css_hash }}` so that
browsers refetch when the bundled file changes. The five first-party JS
files shipped with datasette did not. Cache-busting JS the same way
matches the existing CSS pattern and uses the static_hash() helper that
already powers app_css_hash().
Files updated:
- datasette/app.py: expose static_hash as a callable in template context.
- datasette/handle_exception.py: include static_hash in the error-page
template context (mirrors the existing app_css_hash entry there).
- datasette/templates/base.html: hash datasette-manager.js and
navigation-search.js.
- datasette/templates/table.html: hash column-chooser.js, table.js, and
mobile-column-actions.js.
- tests/test_html.py: new test_js_content_hash parametrized across all
five files; existing test_navigation_menu_links updated to expect the
new query string.
Vendored libraries (cm-editor-6.0.1.bundle.js, sql-formatter-2.3.3.min.js,
json-format-highlight-1.0.1.js) already carry a version in the filename
and were left unchanged.
This commit is contained in:
parent
de55a76d40
commit
5f83d94119
5 changed files with 30 additions and 6 deletions
|
|
@ -2052,6 +2052,7 @@ class Datasette:
|
|||
and "ds_actor" in request.cookies
|
||||
and request.actor,
|
||||
"app_css_hash": self.app_css_hash(),
|
||||
"static_hash": self.static_hash,
|
||||
"zip": zip,
|
||||
"body_scripts": body_scripts,
|
||||
"format_bytes": format_bytes,
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ def handle_exception(datasette, request, exception):
|
|||
info,
|
||||
urls=datasette.urls,
|
||||
app_css_hash=datasette.app_css_hash(),
|
||||
static_hash=datasette.static_hash,
|
||||
menu_links=lambda: [],
|
||||
)
|
||||
),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="stylesheet" href="{{ url.url }}"{% if url.get("sri") %} integrity="{{ url.sri }}" crossorigin="anonymous"{% endif %}>
|
||||
{% endfor %}
|
||||
<script>window.datasetteVersion = '{{ datasette_version }}';</script>
|
||||
<script src="{{ urls.static('datasette-manager.js') }}" defer></script>
|
||||
<script src="{{ urls.static('datasette-manager.js') }}?{{ static_hash('datasette-manager.js') }}" defer></script>
|
||||
{% for url in extra_js_urls %}
|
||||
<script {% if url.module %}type="module" {% endif %}src="{{ url.url }}"{% if url.get("sri") %} integrity="{{ url.sri }}" crossorigin="anonymous"{% endif %}></script>
|
||||
{% endfor %}
|
||||
|
|
@ -70,7 +70,7 @@
|
|||
{% endfor %}
|
||||
|
||||
{% if select_templates %}<!-- Templates considered: {{ select_templates|join(", ") }} -->{% endif %}
|
||||
<script src="{{ urls.static('navigation-search.js') }}" defer></script>
|
||||
<script src="{{ urls.static('navigation-search.js') }}?{{ static_hash('navigation-search.js') }}" defer></script>
|
||||
<navigation-search url="/-/jump"></navigation-search>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
{% block extra_head %}
|
||||
{{- super() -}}
|
||||
<script src="{{ urls.static('column-chooser.js') }}" defer></script>
|
||||
<script src="{{ urls.static('table.js') }}" defer></script>
|
||||
<script src="{{ urls.static('mobile-column-actions.js') }}" defer></script>
|
||||
<script src="{{ urls.static('column-chooser.js') }}?{{ static_hash('column-chooser.js') }}" defer></script>
|
||||
<script src="{{ urls.static('table.js') }}?{{ static_hash('table.js') }}" defer></script>
|
||||
<script src="{{ urls.static('mobile-column-actions.js') }}?{{ static_hash('mobile-column-actions.js') }}" defer></script>
|
||||
<script>DATASETTE_ALLOW_FACET = {{ datasette_allow_facet }};</script>
|
||||
<style>
|
||||
@media only screen and (max-width: 576px) {
|
||||
|
|
|
|||
|
|
@ -604,6 +604,26 @@ async def test_404(ds_client, path):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"filename,template_path",
|
||||
[
|
||||
("datasette-manager.js", "/"),
|
||||
("navigation-search.js", "/"),
|
||||
("table.js", "/fixtures/facetable"),
|
||||
("column-chooser.js", "/fixtures/facetable"),
|
||||
("mobile-column-actions.js", "/fixtures/facetable"),
|
||||
],
|
||||
)
|
||||
async def test_js_content_hash(ds_client, filename, template_path):
|
||||
response = await ds_client.get(template_path)
|
||||
assert response.status_code == 200
|
||||
expected = (
|
||||
f'<script src="/-/static/{filename}?{ds_client.ds.static_hash(filename)}"'
|
||||
)
|
||||
assert expected in response.text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"path,expected_redirect",
|
||||
|
|
@ -1038,7 +1058,9 @@ async def test_navigation_menu_links(
|
|||
navigation_search_script = soup.find(
|
||||
"script", {"src": re.compile(r"navigation-search\.js")}
|
||||
)
|
||||
assert navigation_search_script["src"] == "/-/static/navigation-search.js"
|
||||
assert navigation_search_script["src"] == (
|
||||
f"/-/static/navigation-search.js?{ds_client.ds.static_hash('navigation-search.js')}"
|
||||
)
|
||||
assert details.find("li").find("button") == search_button
|
||||
if not actor_id:
|
||||
# The app menu is always visible, but anonymous users do not see logout
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue