Repository navigation
Conversation
* origin/main: Removed SC voting language from the document. Updated language for annual release cadence due to DEP 20. (django#120) Marked DEP 20's status as Accepted. (django#116) Use X in Deprecation Policy section of DEP 20 for clarity. (django#115) DEP 20 Adopt Annual Release Cycle (django#109) DEP 0019 - Technical governance for the Django project (django#107) Finalized DEP 0018 Dictionary-based MAILERS (ticket-35514) DEP 0018 Dictionary-based EMAIL_PROVIDERS (ticket-35514) (django#105) Marked DEP 0014 Background Workers as final Extended project template (django#98) Add django command (django#100) Moved DEP 0011 to superseded (django#102)
…ject all the time
| Browser support for multiple import maps is spotty at the time of writing. | ||
| Chromium and Safari support multiple import maps since early 2025, but Firefox | ||
| still only uses the first import map and ignores all others (see `MDN | ||
| <https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script/type/importmap>`__). | ||
| This means that when we want to use import maps, we have to ensure that there's | ||
| only a single import map in the output or wait for years until we can use this | ||
| feature. |
There was a problem hiding this comment.
Mozilla has implemented this feature and is discussing enabling it. Given the many related websites listed as broken, I'm pretty sure this will be enabled soon (I'd say in the next 6 months). There's also a proposal to include it in Interop 2027.
So I think the design constraint of only emitting one import map can be relaxed.
There was a problem hiding this comment.
Thanks, that's very interesting! I didn't know that we're already at that stage of browser support.
Until a feature is baseline available we still have to wait for ~30 months. That's quite a long time so if we get this supported within a few months or something like that it is still worth doing.
There was a problem hiding this comment.
30 months is two and a half years, which means June 2029 for Baseline Widely Available. Meaning this constraint will only be useful in two versions of Django at best (Django 6.2 and Django 2028). Many more future versions of Django will need to adapt to this API constraint (or have a depreciation plan) even though there won't be any practical use case anymore.
I'm mentioning all this because of the extra contributor work that could be avoided if it's considered an ok tradeoff.
There was a problem hiding this comment.
I think I'm a bit more conservative in the way I'm interpreting baseline availability. To me it means I don't have to think about it too much when the support is somewhat important. It doesn't mean I get to use it if the availability is central to the project because I definitely am serving environments where browsers and devices are lagging behind.
Not having browser support for multiple import maps isn't a visual glitch or something comparable, it's a total showstopper.
TLDR: some people would have to wait significantly longer than 30 months.
There was a problem hiding this comment.
I added a paragraph to the Rationale section with my reasoning for supporting this in Django (a6d9a32). The import map support doesn't have to be removed later, it just becomes less essential.
Thanks for the bugzilla links!
|
@jacobtylerwalls Can I ask you to apply the Django on the Med label on this PR as well? It has been open for much longer but most of the work was done last week. Thanks in advance :) |
codingjoe
left a comment
There was a problem hiding this comment.
@matthiask can I suggest adding type hints to the new APIs you are proposing? Even if they don't make it into production code, they help with the reading. For example, Importmap(integrity) expects a dict, whereas Script(integrit) would expect a string.
It will also help to focus the docstrings on their external behavior, rather than on their arguments.
Yes, sure! I just did that now. |
codingjoe
left a comment
There was a problem hiding this comment.
I didn't do a full review yet but left some notes and tried implementing it in my package.
I used:
import typing
@typing.runtime_checkable
class ImportMapLike(typing.Protocol):
"""
Structural type of an import map.
"""
imports: Mapping[str, str]
scopes: Mapping[str, Mapping[str, str]]
integrity: Mapping[str, str]
# my concrete backport
def __or__(self, other):
if isinstnace(other, ImportMapLike):
# merging mappsingsMy main concern is ImportMap.render(). Should it return a script-tag, or the import map JSON?
Importantly, the importmap-tag is subject to CSP. In my project is don't use neither nonce, nor inline-script for their legitimate security limitations. (Thanks for pointing those out @adamchainz).
Instead I use rendered JSON to add an explicit sha-512 digest to my script-src policy. See also: https://github.com/codingjoe/relay/blob/94847a54b2b4aaaa5b4546e958551d184c018647/root/middleware.py#L11-L44
| Paths in ``imports`` and ``scopes`` are resolved when rendering, like | ||
| ``Script`` paths: Relative paths are passed through ``static()``, while | ||
| URLs with a scheme and paths starting with ``/``, ``./`` or ``../`` are | ||
| used as they are. Paths ending with ``/`` are used as they are too, | ||
| since static file storages cannot resolve directories. Scope prefixes | ||
| are never resolved. |
There was a problem hiding this comment.
MediaAsset uses if self._path.startswith(("http://", "https://", "/")): so relative paths will be resolved with static.
There was a problem hiding this comment.
Thanks! I think we probably should do the same thing Django does in HashedFilesMixin. Passing through ./ and ../ came from django-js-asset and I don't think that's very useful in this context.
EDIT: Not exactly the same, since HashedFileMixin also strips STATIC_URL if files already have it and still processes them. I don't think we should copy that feature here.
| def render(self, *, attrs: dict[str, str | bool] | None = None) -> str: | ||
| """ | ||
| Render the ``<script type="importmap">`` element. The import map | ||
| with resolved paths is serialized as JSON and escaped using | ||
| ``json_script()``. ``attrs`` are added like in | ||
| ``MediaAsset.render()`` (raising a ``ValueError`` for conflicting | ||
| attributes), e.g. for CSP nonces. | ||
| """ |
There was a problem hiding this comment.
Importmaps do need a nonce, but not SRI integrity. Still, an integrity method would be great to use a hashed CSP instead of a nonce.
There was a problem hiding this comment.
Good point. I added a proposal for adding a csp_hash method or something like that to the ImportMap object. integrity would be a fitting name since it returns the same data structure as proposed in DEP0021, but might be misleading. I'm not sure.
| def __eq__(self, other: object) -> bool: | ||
| """ | ||
| Import maps are equal when their data and attributes are equal. | ||
| """ | ||
|
|
||
| def __or__(self, other: ImportMap) -> ImportMap: | ||
| """ | ||
| Return a new import map combining this import map and ``other``. | ||
|
|
||
| ``imports``, ``integrity`` and each scope in ``scopes`` are merged | ||
| like dictionaries, so entries of ``other`` replace entries of this | ||
| import map. Attributes are merged the same way. Neither import map | ||
| is modified. | ||
| """ |
There was a problem hiding this comment.
I think you don't need a docstring here, those methods are fairly self-explainatory ;)
| For paths resolved through ``static()``, ``integrity`` metadata is added | ||
| automatically if the static files storage supports it (see `Deferred | ||
| functionality`_). ``integrity`` is only needed for other modules, e.g. | ||
| modules loaded from a CDN. |
There was a problem hiding this comment.
This would also be limited to relative paths only, which are locaded inside staticfiles.
There was a problem hiding this comment.
Yes. I reworded that slightly to explain that specifying the integrity explicitly is only needed for files loaded from elsewhere.
…ld be done in Django
I haven't used protocols often yet, but using protocols makes sense to me. I worry a bit that this introduces some additional complexity to the DEP itself (
Looks great, thanks for the link! I think it should return a full tag as the media assets do. I don't feel too strongly about that though. I added the deferred proposal to add a |
|
|
Thanks! That's interesting. That's another reason to leave it out for now. Adding a factory function which converts an |
|
I don't know if I linked this here before, but just in case: mdn/browser-compat-data#30774 |
See also django/new-features#214
HTML