diff --git a/datasette/app.py b/datasette/app.py index 3353fbf3ff..7fbf77fb1b 100644 --- a/datasette/app.py +++ b/datasette/app.py @@ -1285,6 +1285,9 @@ def instance(self): def static(self, path): return self.path("-/static/{}".format(path)) + def static_plugins(self, plugin, path): + return self.path("-/static-plugins/{}/{}".format(plugin, path)) + def logout(self): return self.path("-/logout") diff --git a/docs/internals.rst b/docs/internals.rst index 5bc0efd5e9..439fe5b3a9 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -387,6 +387,14 @@ The ``datasette.urls`` object contains methods for building URLs to pages within ``datasette.urls.logout()`` Returns the URL to the logout page, usually ``"/-/logout"``. +``datasette.urls.static(path)`` + Returns the URL of one of Datasette's default static assets, for example ``"/-/static/app.css"``. + +``datasette.urls.static_plugins(plugin_name, path)`` + Returns the URL of one of the static assets belonging to a plugin. + + ``datasette.url.static_plugins("datasette_cluster_map", "datasette-cluster-map.js")`` would return ``"/-/static-plugins/datasette_cluster_map/datasette-cluster-map.js"``. + ``datasette.urls.database(database_name)`` Returns the URL to a database page, for example ``"/fixtures"`` diff --git a/docs/writing_plugins.rst b/docs/writing_plugins.rst index ba933018d4..f763f61775 100644 --- a/docs/writing_plugins.rst +++ b/docs/writing_plugins.rst @@ -115,7 +115,21 @@ If your plugin has a ``static/`` directory, Datasette will automatically configu /-/static-plugins/NAME_OF_PLUGIN_PACKAGE/yourfile.js -See `the datasette-plugin-demos repository `_ for an example of how to create a package that includes a static folder. +Use the ``datasette.urls.static_plugins(plugin_name, path)`` method to generate URLs to that asset that take the ``base_url`` setting into account, see :ref:`internals_datasette_urls`. + +To bundle the static assets for a plugin in the package that you publish to PyPI, add the following to the plugin's ``setup.py``: + +.. code-block:: python + + package_data={ + 'datasette_plugin_name': [ + 'static/plugin.js', + ], + }, + +Where ``datasette_plugin_name`` is the name of the plugin package (note that it uses underscores, not hyphens) and ``static/plugin.js`` is the path within that package to the static file. + +`datasette-cluster-map `__ is a useful example of a plugin that includes packaged static assets in this way. .. _writing_plugins_custom_templates: @@ -132,6 +146,18 @@ The priority order for template loading is: See :ref:`customization` for more details on how to write custom templates, including which filenames to use to customize which parts of the Datasette UI. +Templates should be bundled for distribution using the same ``package_data`` mechanism in ``setup.py`` described for static assets above, for example: + +.. code-block:: python + + package_data={ + 'datasette_plugin_name': [ + 'templates/my_template.html', + ], + }, + +You can also use wildcards here such as ``templates/*.html``. See `datasette-edit-schema `__ for an example of this pattern. + .. _writing_plugins_configuration: Writing plugins that accept configuration diff --git a/tests/test_internals_urls.py b/tests/test_internals_urls.py index deac632105..6498ee4366 100644 --- a/tests/test_internals_urls.py +++ b/tests/test_internals_urls.py @@ -47,6 +47,28 @@ def test_static(ds, base_url, file, expected): assert ds.urls.static(file) == expected +@pytest.mark.parametrize( + "base_url,plugin,file,expected", + [ + ( + "/", + "datasette_cluster_map", + "datasette-cluster-map.js", + "/-/static-plugins/datasette_cluster_map/datasette-cluster-map.js", + ), + ( + "/prefix/", + "datasette_cluster_map", + "datasette-cluster-map.js", + "/prefix/-/static-plugins/datasette_cluster_map/datasette-cluster-map.js", + ), + ], +) +def test_static_plugins(ds, base_url, plugin, file, expected): + ds._config["base_url"] = base_url + assert ds.urls.static_plugins(plugin, file) == expected + + @pytest.mark.parametrize( "base_url,expected", [ @@ -59,10 +81,13 @@ def test_logout(ds, base_url, expected): assert ds.urls.logout() == expected -@pytest.mark.parametrize("base_url,expected", [ - ("/", "/:memory:"), - ("/prefix/", "/prefix/:memory:"), -]) +@pytest.mark.parametrize( + "base_url,expected", + [ + ("/", "/:memory:"), + ("/prefix/", "/prefix/:memory:"), + ], +) def test_database(ds, base_url, expected): ds._config["base_url"] = base_url assert ds.urls.database(":memory:") == expected