script type=module support, closes #1186

pull/1204/head
Simon Willison 2021-01-13 17:50:52 -08:00
rodzic 640ac7071b
commit fa0c3777b8
6 zmienionych plików z 74 dodań i 24 usunięć

Wyświetl plik

@ -864,19 +864,23 @@ class Datasette:
if isinstance(url_or_dict, dict):
url = url_or_dict["url"]
sri = url_or_dict.get("sri")
module = bool(url_or_dict.get("module"))
else:
url = url_or_dict
sri = None
module = False
if url in seen_urls:
continue
seen_urls.add(url)
if url.startswith("/"):
# Take base_url into account:
url = self.urls.path(url)
script = {"url": url}
if sri:
output.append({"url": url, "sri": sri})
else:
output.append({"url": url})
script["sri"] = sri
if module:
script["module"] = True
output.append(script)
return output
def app(self):

Wyświetl plik

@ -8,7 +8,7 @@
<link rel="stylesheet" href="{{ url.url }}"{% if url.sri %} integrity="{{ url.sri }}" crossorigin="anonymous"{% endif %}>
{% endfor %}
{% for url in extra_js_urls %}
<script src="{{ url.url }}"{% if url.sri %} integrity="{{ url.sri }}" crossorigin="anonymous"{% endif %}></script>
<script {% if url.module %}type="module" {% endif %}src="{{ url.url }}"{% if url.sri %} integrity="{{ url.sri }}" crossorigin="anonymous"{% endif %}></script>
{% endfor %}
{% block extra_head %}{% endblock %}
</head>

Wyświetl plik

@ -5,6 +5,8 @@ Custom pages and templates
Datasette provides a number of ways of customizing the way data is displayed.
.. _customization_css_and_javascript:
Custom CSS and JavaScript
-------------------------
@ -25,7 +27,12 @@ Your ``metadata.json`` file can include links that look like this:
]
}
The extra CSS and JavaScript files will be linked in the ``<head>`` of every page.
The extra CSS and JavaScript files will be linked in the ``<head>`` of every page:
.. code-block:: html
<link rel="stylesheet" href="https://simonwillison.net/static/css/all.bf8cd891642c.css">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
You can also specify a SRI (subresource integrity hash) for these assets:
@ -46,9 +53,39 @@ You can also specify a SRI (subresource integrity hash) for these assets:
]
}
This will produce:
.. code-block:: html
<link rel="stylesheet" href="https://simonwillison.net/static/css/all.bf8cd891642c.css"
integrity="sha384-9qIZekWUyjCyDIf2YK1FRoKiPJq4PHt6tp/ulnuuyRBvazd0hG7pWbE99zvwSznI"
crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g="
crossorigin="anonymous"></script>
Modern browsers will only execute the stylesheet or JavaScript if the SRI hash
matches the content served. You can generate hashes using `www.srihash.org <https://www.srihash.org/>`_
Items in ``"extra_js_urls"`` can specify ``"module": true`` if they reference JavaScript that uses `JavaScript modules <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules>`__. This configuration:
.. code-block:: json
{
"extra_js_urls": [
{
"url": "https://example.datasette.io/module.js",
"module": true
}
]
}
Will produce this HTML:
.. code-block:: html
<script type="module" src="https://example.datasette.io/module.js"></script>
CSS classes on the <body>
~~~~~~~~~~~~~~~~~~~~~~~~~

Wyświetl plik

@ -182,7 +182,7 @@ This can be a list of URLs:
@hookimpl
def extra_css_urls():
return [
'https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css'
"https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"
]
Or a list of dictionaries defining both a URL and an
@ -190,21 +190,17 @@ Or a list of dictionaries defining both a URL and an
.. code-block:: python
from datasette import hookimpl
@hookimpl
def extra_css_urls():
return [{
'url': 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css',
'sri': 'sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4',
"url": "https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css",
"sri": "sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4",
}]
This function can also return an awaitable function, useful if it needs to run any async code:
.. code-block:: python
from datasette import hookimpl
@hookimpl
def extra_css_urls(datasette):
async def inner():
@ -233,8 +229,8 @@ return a list of URLs, a list of dictionaries or an awaitable function that retu
@hookimpl
def extra_js_urls():
return [{
'url': 'https://code.jquery.com/jquery-3.3.1.slim.min.js',
'sri': 'sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo',
"url": "https://code.jquery.com/jquery-3.3.1.slim.min.js",
"sri": "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo",
}]
You can also return URLs to files from your plugin's ``static/`` directory, if
@ -242,12 +238,21 @@ you have one:
.. code-block:: python
from datasette import hookimpl
@hookimpl
def extra_js_urls():
return [
'/-/static-plugins/your-plugin/app.js'
"/-/static-plugins/your-plugin/app.js"
]
If your code uses `JavaScript modules <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules>`__ you should include the ``"module": True`` key. See :ref:`customization_css_and_javascript` for more details.
.. code-block:: python
@hookimpl
def extra_js_urls():
return [{
"url": "/-/static-plugins/your-plugin/app.js",
"module": True
]
Examples: `datasette-cluster-map <https://github.com/simonw/datasette-cluster-map>`_, `datasette-vega <https://github.com/simonw/datasette-vega>`_

Wyświetl plik

@ -61,6 +61,7 @@ def extra_js_urls():
"sri": "SRIHASH",
},
"https://plugin-example.datasette.io/plugin1.js",
{"url": "https://plugin-example.datasette.io/plugin.module.js", "module": True},
]

Wyświetl plik

@ -118,16 +118,19 @@ def test_hook_extra_css_urls(app_client, path, expected_decoded_object):
def test_hook_extra_js_urls(app_client):
response = app_client.get("/")
scripts = Soup(response.body, "html.parser").findAll("script")
assert [
s
for s in scripts
if s.attrs
== {
script_attrs = [s.attrs for s in scripts]
for attrs in [
{
"integrity": "SRIHASH",
"crossorigin": "anonymous",
"src": "https://plugin-example.datasette.io/jquery.js",
}
]
},
{
"src": "https://plugin-example.datasette.io/plugin.module.js",
"type": "module",
},
]:
assert any(s == attrs for s in script_attrs), "Expected: {}".format(attrs)
def test_plugins_with_duplicate_js_urls(app_client):