html_compose.resource.js_import

  1from typing import Iterable, Literal, NamedTuple
  2
  3from .. import elements as el
  4from ..util_funcs import flatten_iterable, is_iterable_but_not_str
  5from .util_funcs import _cachebust_resource_uri
  6
  7
  8class _ImportMapEntry(NamedTuple):
  9    """
 10    Common structure for import map entries
 11    """
 12
 13    name: str
 14    src: str
 15    scope_urls: Iterable[str] | None = None
 16
 17
 18class js_import:
 19    """
 20    A javascript import helper class which employs one or more of:
 21
 22    - `script(` to define the import
 23    - `importmap` which maps javascript module names to URLs
 24    Setting the name parameter like so
 25    ```python
 26    js_import(
 27        "https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm",
 28        name="alpinejs")
 29    ```
 30    creates an import map so javascript modules can be imported by name
 31    - `link(rel="modulepreload")` to preload the import
 32    - if cache_bust is true, appends a timestamp to the URL to
 33
 34    An production usage might look like:
 35
 36    ```python
 37    [
 38    js_import('./static/admin.js',
 39                name='admin',
 40                cache_bust=True),
 41
 42    js_import(
 43        "https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm",
 44        name="alpinejs",
 45        preload=True,
 46        hash=(
 47            "sha384-Yf57wlxlrA1+0X6Ye9NOBxQ1tpmiwI/"
 48            "9mFpv9tT/Rh2UAajwwAlTWHnvTGYhgv7p"
 49            ),
 50        crossorigin="anonymous"
 51        ),
 52
 53    ]
 54    ```
 55
 56    ...
 57
 58    ## Usage in window:
 59
 60    ```python
 61    script(type="module")[
 62        '''
 63        import Alpine from 'alpinejs'
 64        window.Alpine = Alpine
 65        Alpine.start()
 66        '''
 67    ]
 68    ```
 69
 70    ## Generated "HTML":
 71
 72    ```python
 73    import json
 74    from html_compose import el, unsafe_text
 75
 76    [
 77        el.script(type="importmap")[
 78            unsafe_text('{"imports": {"admin": "./static/admin.js?ts=1760157623", "alpinejs": "https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm"}}')
 79        ],
 80
 81        el.link(href="./static/admin.js?ts=1760157623", rel=["modulepreload"]),
 82
 83
 84        el.link(
 85            href="https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm",
 86            integrity="sha384-Yf57wlxlrA1+0X6Ye9NOBxQ1tpmiwI/9mFpv9tT/Rh2UAajwwAlTWHnvTGYhgv7p",
 87            crossorigin="anonymous",
 88            rel=["modulepreload"],
 89        ),
 90
 91        el.script(src="./static/admin.js?ts=1760157623", type="module"),
 92
 93        el.script(
 94            src="https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm",
 95            type="module",
 96            integrity="sha384-Yf57wlxlrA1+0X6Ye9NOBxQ1tpmiwI/9mFpv9tT/Rh2UAajwwAlTWHnvTGYhgv7p",
 97            crossorigin="anonymous",
 98        ),
 99    ]
100    ```
101
102    See:
103    - https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script/type/importmap
104    - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
105    - https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/modulepreload
106    - https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/preload
107    - https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
108    - https://www.srihash.org/
109
110    """
111
112    def __init__(
113        self,
114        source: str,
115        name: str | None = None,
116        preload: bool = False,
117        hash: str | None = None,
118        async_: bool = False,
119        defer: bool = False,
120        nomodule: bool = False,
121        scope_url: str | Iterable[str] | None = None,
122        crossorigin: Literal["", "anonymous", "user-credentials"]
123        | str
124        | None = None,
125        cache_bust: bool = False,
126    ):
127        """
128        A javascript import wrapper to manage optimal window import strategies.
129
130        If name is set, the import is added to an import map and imported
131        as a module.
132
133        Parameters
134        ----------
135        `source`:
136            The literal src passed to tags i.e. script and link preload.
137
138            If cache_bust is set, a timestamp is appended to the URL.
139        `name`:
140            The name of the import, e.g. "lodash" which can be used in
141            javascript type="module" imports.
142
143            Doing this automatically changes the script type to module
144
145        `preload`:
146            The modulepreload keyword, for the rel attribute of the `<link/>`
147            element, provides a declarative way to preemptively fetch a module
148            script, parse and compile it, and store it in the document's module
149            map for later execution.
150
151            If this is not a javascript module (i.e. name is not set),
152            the rel attribute is set to "preload" and the as_ attribute
153            is set to "script" instead.
154
155        `async_`:
156            The async attribute causes the script to be executed asynchronously as soon as it is available,
157            without blocking HTML parsing.
158
159        `defer`:
160            Defers script execution until document parsing is complete.
161            Has no effect on module scripts (they are deferred by default).
162
163        `nomodule`:
164            Added to script tag.
165
166            Indicates that the script should not be executed in browsers that
167            support ES modules — in effect, this can be used to serve fallback
168            scripts to older browsers that do not support javascript modules.
169
170        `hash`:
171            An optional SRI integrity hash for the import
172
173        `crossorigin`:
174            Optionally sets the crossorigin attribute on the script tag.
175            Valid values are "", "anonymous", "use-credentials"
176
177        `cache_bust`:
178            If true, appends a timestamp to the URL to work with browser
179            caching. Webservers configured for static resources manage this
180            feature automatically, but for development this can be useful.
181
182            This feature only works for local resources, i.e. those that
183            exist relative to `resource.settings.base_dir`
184
185        `scope_url`:
186            Optional route or list of paths where this import should be
187            included.
188
189            Scopes let you have different mappings for different parts of your
190            app.
191
192            Because they affect the import map but not what is loaded -
193            the script tag controls what loads - they are rarely used.
194
195        """
196
197        self.name = name
198        self.source = source
199        self.preload = preload
200        self.hash = hash
201        self.scope_urls = scope_url
202        if scope_url:
203            if isinstance(scope_url, str):
204                self.scope_urls = [scope_url]
205            elif is_iterable_but_not_str(scope_url):
206                # Materialize scope URLs so they can be iterated multiple times
207                # (flatten_iterable returns a generator by default).
208                self.scope_urls = list(flatten_iterable(scope_url))
209            else:
210                self.scope_urls = [str(scope_url)]
211        self.crossorigin = crossorigin
212        self.cache_bust = cache_bust
213        self.has_link = preload
214        self.async_ = async_
215        self.defer = defer
216        self.nomodule = nomodule
217        self._src = self.uri()
218        if hash and self.crossorigin is None:
219            raise ValueError(
220                "If hash is set, crossorigin must be set to ''/'anonymous'"
221            )
222
223    def uri(self) -> str:
224        """
225        Returns the source URI - with cache busting if enabled
226        which is implemented by getting the mtime of the local file
227
228        This operation performs up to two fs stats per call
229
230        1. The base static directory is checked once and cached per interpreter
231        2. The specific resource is checked if it is time to poll again
232           based on settings.stat_poll_interval
233        """
234        if not self.cache_bust:
235            return self.source
236
237        return _cachebust_resource_uri(self.source)
238
239    def import_map_entry(self) -> _ImportMapEntry | None:
240        """
241        Returns a tuple of (name, source, scope_url) for use in an import map
242        """
243
244        if self.name:
245            if self.scope_urls is None:
246                return _ImportMapEntry(name=self.name, src=self._src)
247            else:
248                return _ImportMapEntry(
249                    name=self.name, src=self._src, scope_urls=self.scope_urls
250                )
251
252        return None
253
254    def preload_link(self) -> el.link | None:
255        """
256        Returns one or more link element for this import
257        """
258        if self.preload:
259            attrs = {"href": self._src}
260            if self.hash:
261                attrs["integrity"] = self.hash
262            if self.crossorigin:
263                attrs["crossorigin"] = self.crossorigin
264            if self.name:
265                return el.link(attrs=attrs, rel="modulepreload")
266            else:
267                return el.link(attrs=attrs, rel="preload", as_="script")
268
269        return None
270
271    def script(self) -> el.script:
272        """
273        Returns a script tag for this import
274        """
275        attrs: dict[str, str | bool] = {"src": self._src}
276
277        if self.name:
278            attrs["type"] = "module"
279        if self.hash:
280            attrs["integrity"] = self.hash
281        if self.crossorigin:
282            attrs["crossorigin"] = self.crossorigin
283        if self.async_:
284            attrs["async"] = True
285        if self.defer and not self.name:
286            # Only set defer for non-modules
287            attrs["defer"] = True
288        if self.nomodule:
289            attrs["nomodule"] = True
290
291        return el.script(attrs=attrs)
class js_import:
 19class js_import:
 20    """
 21    A javascript import helper class which employs one or more of:
 22
 23    - `script(` to define the import
 24    - `importmap` which maps javascript module names to URLs
 25    Setting the name parameter like so
 26    ```python
 27    js_import(
 28        "https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm",
 29        name="alpinejs")
 30    ```
 31    creates an import map so javascript modules can be imported by name
 32    - `link(rel="modulepreload")` to preload the import
 33    - if cache_bust is true, appends a timestamp to the URL to
 34
 35    An production usage might look like:
 36
 37    ```python
 38    [
 39    js_import('./static/admin.js',
 40                name='admin',
 41                cache_bust=True),
 42
 43    js_import(
 44        "https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm",
 45        name="alpinejs",
 46        preload=True,
 47        hash=(
 48            "sha384-Yf57wlxlrA1+0X6Ye9NOBxQ1tpmiwI/"
 49            "9mFpv9tT/Rh2UAajwwAlTWHnvTGYhgv7p"
 50            ),
 51        crossorigin="anonymous"
 52        ),
 53
 54    ]
 55    ```
 56
 57    ...
 58
 59    ## Usage in window:
 60
 61    ```python
 62    script(type="module")[
 63        '''
 64        import Alpine from 'alpinejs'
 65        window.Alpine = Alpine
 66        Alpine.start()
 67        '''
 68    ]
 69    ```
 70
 71    ## Generated "HTML":
 72
 73    ```python
 74    import json
 75    from html_compose import el, unsafe_text
 76
 77    [
 78        el.script(type="importmap")[
 79            unsafe_text('{"imports": {"admin": "./static/admin.js?ts=1760157623", "alpinejs": "https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm"}}')
 80        ],
 81
 82        el.link(href="./static/admin.js?ts=1760157623", rel=["modulepreload"]),
 83
 84
 85        el.link(
 86            href="https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm",
 87            integrity="sha384-Yf57wlxlrA1+0X6Ye9NOBxQ1tpmiwI/9mFpv9tT/Rh2UAajwwAlTWHnvTGYhgv7p",
 88            crossorigin="anonymous",
 89            rel=["modulepreload"],
 90        ),
 91
 92        el.script(src="./static/admin.js?ts=1760157623", type="module"),
 93
 94        el.script(
 95            src="https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm",
 96            type="module",
 97            integrity="sha384-Yf57wlxlrA1+0X6Ye9NOBxQ1tpmiwI/9mFpv9tT/Rh2UAajwwAlTWHnvTGYhgv7p",
 98            crossorigin="anonymous",
 99        ),
100    ]
101    ```
102
103    See:
104    - https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script/type/importmap
105    - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
106    - https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/modulepreload
107    - https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/preload
108    - https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
109    - https://www.srihash.org/
110
111    """
112
113    def __init__(
114        self,
115        source: str,
116        name: str | None = None,
117        preload: bool = False,
118        hash: str | None = None,
119        async_: bool = False,
120        defer: bool = False,
121        nomodule: bool = False,
122        scope_url: str | Iterable[str] | None = None,
123        crossorigin: Literal["", "anonymous", "user-credentials"]
124        | str
125        | None = None,
126        cache_bust: bool = False,
127    ):
128        """
129        A javascript import wrapper to manage optimal window import strategies.
130
131        If name is set, the import is added to an import map and imported
132        as a module.
133
134        Parameters
135        ----------
136        `source`:
137            The literal src passed to tags i.e. script and link preload.
138
139            If cache_bust is set, a timestamp is appended to the URL.
140        `name`:
141            The name of the import, e.g. "lodash" which can be used in
142            javascript type="module" imports.
143
144            Doing this automatically changes the script type to module
145
146        `preload`:
147            The modulepreload keyword, for the rel attribute of the `<link/>`
148            element, provides a declarative way to preemptively fetch a module
149            script, parse and compile it, and store it in the document's module
150            map for later execution.
151
152            If this is not a javascript module (i.e. name is not set),
153            the rel attribute is set to "preload" and the as_ attribute
154            is set to "script" instead.
155
156        `async_`:
157            The async attribute causes the script to be executed asynchronously as soon as it is available,
158            without blocking HTML parsing.
159
160        `defer`:
161            Defers script execution until document parsing is complete.
162            Has no effect on module scripts (they are deferred by default).
163
164        `nomodule`:
165            Added to script tag.
166
167            Indicates that the script should not be executed in browsers that
168            support ES modules — in effect, this can be used to serve fallback
169            scripts to older browsers that do not support javascript modules.
170
171        `hash`:
172            An optional SRI integrity hash for the import
173
174        `crossorigin`:
175            Optionally sets the crossorigin attribute on the script tag.
176            Valid values are "", "anonymous", "use-credentials"
177
178        `cache_bust`:
179            If true, appends a timestamp to the URL to work with browser
180            caching. Webservers configured for static resources manage this
181            feature automatically, but for development this can be useful.
182
183            This feature only works for local resources, i.e. those that
184            exist relative to `resource.settings.base_dir`
185
186        `scope_url`:
187            Optional route or list of paths where this import should be
188            included.
189
190            Scopes let you have different mappings for different parts of your
191            app.
192
193            Because they affect the import map but not what is loaded -
194            the script tag controls what loads - they are rarely used.
195
196        """
197
198        self.name = name
199        self.source = source
200        self.preload = preload
201        self.hash = hash
202        self.scope_urls = scope_url
203        if scope_url:
204            if isinstance(scope_url, str):
205                self.scope_urls = [scope_url]
206            elif is_iterable_but_not_str(scope_url):
207                # Materialize scope URLs so they can be iterated multiple times
208                # (flatten_iterable returns a generator by default).
209                self.scope_urls = list(flatten_iterable(scope_url))
210            else:
211                self.scope_urls = [str(scope_url)]
212        self.crossorigin = crossorigin
213        self.cache_bust = cache_bust
214        self.has_link = preload
215        self.async_ = async_
216        self.defer = defer
217        self.nomodule = nomodule
218        self._src = self.uri()
219        if hash and self.crossorigin is None:
220            raise ValueError(
221                "If hash is set, crossorigin must be set to ''/'anonymous'"
222            )
223
224    def uri(self) -> str:
225        """
226        Returns the source URI - with cache busting if enabled
227        which is implemented by getting the mtime of the local file
228
229        This operation performs up to two fs stats per call
230
231        1. The base static directory is checked once and cached per interpreter
232        2. The specific resource is checked if it is time to poll again
233           based on settings.stat_poll_interval
234        """
235        if not self.cache_bust:
236            return self.source
237
238        return _cachebust_resource_uri(self.source)
239
240    def import_map_entry(self) -> _ImportMapEntry | None:
241        """
242        Returns a tuple of (name, source, scope_url) for use in an import map
243        """
244
245        if self.name:
246            if self.scope_urls is None:
247                return _ImportMapEntry(name=self.name, src=self._src)
248            else:
249                return _ImportMapEntry(
250                    name=self.name, src=self._src, scope_urls=self.scope_urls
251                )
252
253        return None
254
255    def preload_link(self) -> el.link | None:
256        """
257        Returns one or more link element for this import
258        """
259        if self.preload:
260            attrs = {"href": self._src}
261            if self.hash:
262                attrs["integrity"] = self.hash
263            if self.crossorigin:
264                attrs["crossorigin"] = self.crossorigin
265            if self.name:
266                return el.link(attrs=attrs, rel="modulepreload")
267            else:
268                return el.link(attrs=attrs, rel="preload", as_="script")
269
270        return None
271
272    def script(self) -> el.script:
273        """
274        Returns a script tag for this import
275        """
276        attrs: dict[str, str | bool] = {"src": self._src}
277
278        if self.name:
279            attrs["type"] = "module"
280        if self.hash:
281            attrs["integrity"] = self.hash
282        if self.crossorigin:
283            attrs["crossorigin"] = self.crossorigin
284        if self.async_:
285            attrs["async"] = True
286        if self.defer and not self.name:
287            # Only set defer for non-modules
288            attrs["defer"] = True
289        if self.nomodule:
290            attrs["nomodule"] = True
291
292        return el.script(attrs=attrs)

A javascript import helper class which employs one or more of:

  • script( to define the import
  • importmap which maps javascript module names to URLs Setting the name parameter like so
js_import(
    "https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm",
    name="alpinejs")

creates an import map so javascript modules can be imported by name

  • link(rel="modulepreload") to preload the import
  • if cache_bust is true, appends a timestamp to the URL to

An production usage might look like:

[
js_import('./static/admin.js',
            name='admin',
            cache_bust=True),

js_import(
    "https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm",
    name="alpinejs",
    preload=True,
    hash=(
        "sha384-Yf57wlxlrA1+0X6Ye9NOBxQ1tpmiwI/"
        "9mFpv9tT/Rh2UAajwwAlTWHnvTGYhgv7p"
        ),
    crossorigin="anonymous"
    ),

]

...

Usage in window:

script(type="module")[
    '''
    import Alpine from 'alpinejs'
    window.Alpine = Alpine
    Alpine.start()
    '''
]

Generated "HTML":

import json
from html_compose import el, unsafe_text

[
    el.script(type="importmap")[
        unsafe_text('{"imports": {"admin": "./static/admin.js?ts=1760157623", "alpinejs": "https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm"}}')
    ],

    el.link(href="./static/admin.js?ts=1760157623", rel=["modulepreload"]),


    el.link(
        href="https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm",
        integrity="sha384-Yf57wlxlrA1+0X6Ye9NOBxQ1tpmiwI/9mFpv9tT/Rh2UAajwwAlTWHnvTGYhgv7p",
        crossorigin="anonymous",
        rel=["modulepreload"],
    ),

    el.script(src="./static/admin.js?ts=1760157623", type="module"),

    el.script(
        src="https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm",
        type="module",
        integrity="sha384-Yf57wlxlrA1+0X6Ye9NOBxQ1tpmiwI/9mFpv9tT/Rh2UAajwwAlTWHnvTGYhgv7p",
        crossorigin="anonymous",
    ),
]

See:

js_import( source: str, name: str | None = None, preload: bool = False, hash: str | None = None, async_: bool = False, defer: bool = False, nomodule: bool = False, scope_url: Union[str, Iterable[str], NoneType] = None, crossorigin: Union[Literal['', 'anonymous', 'user-credentials'], str, NoneType] = None, cache_bust: bool = False)
113    def __init__(
114        self,
115        source: str,
116        name: str | None = None,
117        preload: bool = False,
118        hash: str | None = None,
119        async_: bool = False,
120        defer: bool = False,
121        nomodule: bool = False,
122        scope_url: str | Iterable[str] | None = None,
123        crossorigin: Literal["", "anonymous", "user-credentials"]
124        | str
125        | None = None,
126        cache_bust: bool = False,
127    ):
128        """
129        A javascript import wrapper to manage optimal window import strategies.
130
131        If name is set, the import is added to an import map and imported
132        as a module.
133
134        Parameters
135        ----------
136        `source`:
137            The literal src passed to tags i.e. script and link preload.
138
139            If cache_bust is set, a timestamp is appended to the URL.
140        `name`:
141            The name of the import, e.g. "lodash" which can be used in
142            javascript type="module" imports.
143
144            Doing this automatically changes the script type to module
145
146        `preload`:
147            The modulepreload keyword, for the rel attribute of the `<link/>`
148            element, provides a declarative way to preemptively fetch a module
149            script, parse and compile it, and store it in the document's module
150            map for later execution.
151
152            If this is not a javascript module (i.e. name is not set),
153            the rel attribute is set to "preload" and the as_ attribute
154            is set to "script" instead.
155
156        `async_`:
157            The async attribute causes the script to be executed asynchronously as soon as it is available,
158            without blocking HTML parsing.
159
160        `defer`:
161            Defers script execution until document parsing is complete.
162            Has no effect on module scripts (they are deferred by default).
163
164        `nomodule`:
165            Added to script tag.
166
167            Indicates that the script should not be executed in browsers that
168            support ES modules — in effect, this can be used to serve fallback
169            scripts to older browsers that do not support javascript modules.
170
171        `hash`:
172            An optional SRI integrity hash for the import
173
174        `crossorigin`:
175            Optionally sets the crossorigin attribute on the script tag.
176            Valid values are "", "anonymous", "use-credentials"
177
178        `cache_bust`:
179            If true, appends a timestamp to the URL to work with browser
180            caching. Webservers configured for static resources manage this
181            feature automatically, but for development this can be useful.
182
183            This feature only works for local resources, i.e. those that
184            exist relative to `resource.settings.base_dir`
185
186        `scope_url`:
187            Optional route or list of paths where this import should be
188            included.
189
190            Scopes let you have different mappings for different parts of your
191            app.
192
193            Because they affect the import map but not what is loaded -
194            the script tag controls what loads - they are rarely used.
195
196        """
197
198        self.name = name
199        self.source = source
200        self.preload = preload
201        self.hash = hash
202        self.scope_urls = scope_url
203        if scope_url:
204            if isinstance(scope_url, str):
205                self.scope_urls = [scope_url]
206            elif is_iterable_but_not_str(scope_url):
207                # Materialize scope URLs so they can be iterated multiple times
208                # (flatten_iterable returns a generator by default).
209                self.scope_urls = list(flatten_iterable(scope_url))
210            else:
211                self.scope_urls = [str(scope_url)]
212        self.crossorigin = crossorigin
213        self.cache_bust = cache_bust
214        self.has_link = preload
215        self.async_ = async_
216        self.defer = defer
217        self.nomodule = nomodule
218        self._src = self.uri()
219        if hash and self.crossorigin is None:
220            raise ValueError(
221                "If hash is set, crossorigin must be set to ''/'anonymous'"
222            )

A javascript import wrapper to manage optimal window import strategies.

If name is set, the import is added to an import map and imported as a module.

Parameters

source: The literal src passed to tags i.e. script and link preload.

If cache_bust is set, a timestamp is appended to the URL.

name: The name of the import, e.g. "lodash" which can be used in javascript type="module" imports.

Doing this automatically changes the script type to module

preload: The modulepreload keyword, for the rel attribute of the <link/> element, provides a declarative way to preemptively fetch a module script, parse and compile it, and store it in the document's module map for later execution.

If this is not a javascript module (i.e. name is not set),
the rel attribute is set to "preload" and the as_ attribute
is set to "script" instead.

async_: The async attribute causes the script to be executed asynchronously as soon as it is available, without blocking HTML parsing.

defer: Defers script execution until document parsing is complete. Has no effect on module scripts (they are deferred by default).

nomodule: Added to script tag.

Indicates that the script should not be executed in browsers that
support ES modules — in effect, this can be used to serve fallback
scripts to older browsers that do not support javascript modules.

hash: An optional SRI integrity hash for the import

crossorigin: Optionally sets the crossorigin attribute on the script tag. Valid values are "", "anonymous", "use-credentials"

cache_bust: If true, appends a timestamp to the URL to work with browser caching. Webservers configured for static resources manage this feature automatically, but for development this can be useful.

This feature only works for local resources, i.e. those that
exist relative to `resource.settings.base_dir`

scope_url: Optional route or list of paths where this import should be included.

Scopes let you have different mappings for different parts of your
app.

Because they affect the import map but not what is loaded -
the script tag controls what loads - they are rarely used.
name
source
preload
hash
scope_urls
crossorigin
cache_bust
async_
defer
nomodule
def uri(self) -> str:
224    def uri(self) -> str:
225        """
226        Returns the source URI - with cache busting if enabled
227        which is implemented by getting the mtime of the local file
228
229        This operation performs up to two fs stats per call
230
231        1. The base static directory is checked once and cached per interpreter
232        2. The specific resource is checked if it is time to poll again
233           based on settings.stat_poll_interval
234        """
235        if not self.cache_bust:
236            return self.source
237
238        return _cachebust_resource_uri(self.source)

Returns the source URI - with cache busting if enabled which is implemented by getting the mtime of the local file

This operation performs up to two fs stats per call

  1. The base static directory is checked once and cached per interpreter
  2. The specific resource is checked if it is time to poll again based on settings.stat_poll_interval
def import_map_entry(self) -> html_compose.resource.js_import._ImportMapEntry | None:
240    def import_map_entry(self) -> _ImportMapEntry | None:
241        """
242        Returns a tuple of (name, source, scope_url) for use in an import map
243        """
244
245        if self.name:
246            if self.scope_urls is None:
247                return _ImportMapEntry(name=self.name, src=self._src)
248            else:
249                return _ImportMapEntry(
250                    name=self.name, src=self._src, scope_urls=self.scope_urls
251                )
252
253        return None

Returns a tuple of (name, source, scope_url) for use in an import map

def script(self) -> html_compose.elements.script_element.script:
272    def script(self) -> el.script:
273        """
274        Returns a script tag for this import
275        """
276        attrs: dict[str, str | bool] = {"src": self._src}
277
278        if self.name:
279            attrs["type"] = "module"
280        if self.hash:
281            attrs["integrity"] = self.hash
282        if self.crossorigin:
283            attrs["crossorigin"] = self.crossorigin
284        if self.async_:
285            attrs["async"] = True
286        if self.defer and not self.name:
287            # Only set defer for non-modules
288            attrs["defer"] = True
289        if self.nomodule:
290            attrs["nomodule"] = True
291
292        return el.script(attrs=attrs)

Returns a script tag for this import