Skip to content

Commit

Permalink
Merge pull request #120 from Niicck/react-refresh-kwargs
Browse files Browse the repository at this point in the history
Add kwargs support to vite_react_refresh
  • Loading branch information
Niicck authored Jan 22, 2024
2 parents 2556fdc + ed074ba commit d94858b
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 9 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,13 @@ like the previous tag.**
```
{% vite_react_refresh %}
```
If you're using React, this will generate the Javascript needed to support React HMR.
If you're using React, this will generate the Javascript `<script/>` needed to support React HMR.

```
{% vite_react_refresh nonce="{{ request.csp_nonce }}" %}
```

Any kwargs passed to vite_react_refresh will be added to its generated `<script/>` tag. For example, if your site is configured with a Content Security Policy using [django-csp](https://github.com/mozilla/django-csp) you'll want to add this value for `nonce`.

### Custom attributes

Expand Down
14 changes: 10 additions & 4 deletions django_vite/core/asset_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
DjangoViteAssetNotFoundError,
DjangoViteConfigNotFoundError,
)
from django_vite.core.tag_generator import Tag, TagGenerator
from django_vite.core.tag_generator import Tag, TagGenerator, attrs_to_str

DEFAULT_APP_NAME = "default"

Expand Down Expand Up @@ -590,12 +590,16 @@ def generate_vite_ws_client(self, **kwargs: Dict[str, str]) -> str:
attrs={"type": "module", **kwargs},
)

def generate_vite_react_refresh_url(self) -> str:
def generate_vite_react_refresh_url(self, **kwargs: Dict[str, str]) -> str:
"""
Generates the script for the Vite React Refresh for HMR.
Only used in development, in production this method returns
an empty string.
Keyword Arguments:
**kwargs {Dict[str, str]} -- Adds new attributes to generated
script tags.
Returns:
str -- The script or an empty string.
config_key {str} -- Key of the configuration to use.
Expand All @@ -605,8 +609,9 @@ def generate_vite_react_refresh_url(self) -> str:
return ""

url = self._get_dev_server_url(self.react_refresh_url)
attrs_str = attrs_to_str(kwargs)

return f"""<script type="module">
return f"""<script type="module" {attrs_str}>
import RefreshRuntime from '{url}'
RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () => {{}}
Expand Down Expand Up @@ -818,6 +823,7 @@ def generate_vite_ws_client(
def generate_vite_react_refresh_url(
self,
app: str = DEFAULT_APP_NAME,
**kwargs: Dict[str, str],
) -> str:
app_client = self._get_app_client(app)
return app_client.generate_vite_react_refresh_url()
return app_client.generate_vite_react_refresh_url(**kwargs)
13 changes: 11 additions & 2 deletions django_vite/core/tag_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
Tag = str


def attrs_to_str(attrs: Dict[str, str]):
"""
Convert dictionary of attributes into a string that can be injected into a <script/>
tag.
"""
attrs_str = " ".join([f'{key}="{value}"' for key, value in attrs.items()])
return attrs_str


class TagGenerator:
@staticmethod
def script(src: str, attrs: Dict[str, str]) -> Tag:
Expand All @@ -20,7 +29,7 @@ def script(src: str, attrs: Dict[str, str]) -> Tag:
str -- The script tag.
"""

attrs_str = " ".join([f'{key}="{value}"' for key, value in attrs.items()])
attrs_str = attrs_to_str(attrs)

return f'<script {attrs_str} src="{src}"></script>'

Expand Down Expand Up @@ -54,6 +63,6 @@ def stylesheet_preload(href: str) -> Tag:

@staticmethod
def preload(href: str, attrs: Dict[str, str]) -> Tag:
attrs_str = " ".join([f'{key}="{value}"' for key, value in attrs.items()])
attrs_str = attrs_to_str(attrs)

return f'<link href="{href}" {attrs_str} />'
13 changes: 11 additions & 2 deletions django_vite/templatetags/django_vite.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,22 @@ def vite_legacy_asset(

@register.simple_tag
@mark_safe
def vite_react_refresh(app: str = DEFAULT_APP_NAME) -> str:
def vite_react_refresh(
app: str = DEFAULT_APP_NAME,
**kwargs: Dict[str, str],
) -> str:
"""
Generates the script for the Vite React Refresh for HMR.
Only used in development, in production this method returns
an empty string.
Keyword Arguments:
**kwargs {Dict[str, str]} -- Adds new attributes to generated
script tags.
Returns:
str -- The script or an empty string.
"""
return DjangoViteAssetLoader.instance().generate_vite_react_refresh_url(app)
return DjangoViteAssetLoader.instance().generate_vite_react_refresh_url(
app, **kwargs
)
32 changes: 32 additions & 0 deletions tests/tests/templatetags/test_vite_react_refresh.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,35 @@ def test_vite_react_refresh_url_setting(patch_settings):
soup = BeautifulSoup(html, "html.parser")
script_tag = soup.script
assert "http://localhost:5173/static/foobar" in script_tag.text


@pytest.mark.parametrize(
"patch_settings",
[
{
"DJANGO_VITE_DEV_MODE": True,
"DJANGO_VITE_REACT_REFRESH_URL": "foobar",
},
{
"DJANGO_VITE": {
"default": {
"dev_mode": True,
"react_refresh_url": "foobar",
}
}
},
],
indirect=True,
)
def test_vite_react_refresh_uses_kwargs(patch_settings):
template = Template(
"""
{% load django_vite %}
{% vite_react_refresh nonce="woo-nonce" %}
"""
)
html = template.render(Context({}))
soup = BeautifulSoup(html, "html.parser")
script_tag = soup.script
assert script_tag.has_attr("nonce")
assert script_tag["nonce"] == "woo-nonce"

0 comments on commit d94858b

Please sign in to comment.