Skip to content

Commit

Permalink
Merge pull request #27 from MasterKale/feat/react-scripts-4.0.0-support
Browse files Browse the repository at this point in the history
feat/react-scripts-4.0.0-support
  • Loading branch information
MasterKale authored Mar 1, 2021
2 parents 4681329 + dc27b2e commit fba3899
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 131 deletions.
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.py]
indent_size = 4
1 change: 0 additions & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ verify_ssl = true
django = "*"

[packages]
bleach = "*"
six = "*"
webencodings = "*"
django-proxy = "*"
Expand Down
78 changes: 31 additions & 47 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 31 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,25 +185,23 @@ React assets will be included with the other static assets in the `settings.STAT

Similar to the `bundle_js` template variable mentioned earlier, **django-cra-helper** includes numerous other template variables when the CRA liveserver is _not_ running:

<details>
<summary>For older projects using <strong>react-scripts@&lt;=2.1.8</strong></summary>
<details open>
<summary>For projects using <strong>react-scripts@&gt;=3.2.0</strong></summary>

The two most important variables are `main_js` and `main_css`. These can be injected into the page via a typical call to `{% static %}` in the template:
Starting with `[email protected]`, a new `entrypoints` property can be found in **asset-manifest.json**. This contains an array of files that **django-cra-helper** makes available in templates to more easily inject these files via new `entrypoints.css` and `entrypoints.js` arrays:

```html
{% if main_css %}
<link href="{% static main_css %}" rel="stylesheet">
{% endif %}
{% for file in entrypoints.css %}
<link href="{% static file %}" rel="stylesheet">
{% endfor %}
```
```html
{% if main_js %}
<script type="text/javascript" src="{% static main_js %}"></script>
{% endif %}
{% for file in entrypoints.js %}
<script type="text/javascript" src="{% static file %}"></script>
{% endfor %}
```

> NOTE: Recent attempts at building a fresh CRA project with `[email protected]` were unsuccessful in recreating SPAs that allowed for just a single `main_js`. `npm run build`-produced artifacts functioned almost identically to artifacts generated the same as `[email protected]`, detailed below.
>
> There may be child dependencies of `react-scripts` that make it no longer possible to start apps that will function with the above instructions. In these cases, please try the instructions in the next section.
> NOTE: These JavaScript and CSS files should be arranged in an order required for the site to load; the ultimate order is derived from the order present in **asset-manifest.json**.
</details>
<details>
Expand All @@ -223,22 +221,24 @@ Similar to the `bundle_js` template variable mentioned earlier, **django-cra-hel
</details>

<details>
<summary>For projects using <strong>react-scripts@&gt;=3.2.0</strong></summary>
<summary>For older projects using <strong>react-scripts@&lt;=2.1.8</strong></summary>

Starting with `[email protected]`, a new `entrypoints` property can be found in **asset-manifest.json**. This contains an array of files that **django-cra-helper** makes available in templates to more easily inject these files via new `entrypoints.css` and `entrypoints.js` arrays. These **replace** the `main_css` and `main_js` values used above:
The two most important variables are `main_js` and `main_css`. These can be injected into the page via a typical call to `{% static %}` in the template:

```html
{% for file in entrypoints.css %}
<link href="{% static file %}" rel="stylesheet">
{% endfor %}
{% if main_css %}
<link href="{% static main_css %}" rel="stylesheet">
{% endif %}
```
```html
{% for file in entrypoints.js %}
<script type="text/javascript" src="{% static file %}"></script>
{% endfor %}
{% if main_js %}
<script type="text/javascript" src="{% static main_js %}"></script>
{% endif %}
```

> NOTE: These JavaScript and CSS files should be arranged in an order required for the site to load; the ultimate order is derived from the order present in **asset-manifest.json**.
> NOTE: Recent attempts at building a fresh CRA project with `[email protected]` were unsuccessful in recreating SPAs that allowed for just a single `main_js`. `npm run build`-produced artifacts functioned almost identically to artifacts generated the same as `[email protected]`, detailed below.
>
> There may be child dependencies of `react-scripts` that make it no longer possible to start apps that will function with the above instructions. In these cases, please try the instructions in the next section.
</details>
### Supporting CRA's relative paths
Expand All @@ -261,12 +261,15 @@ Before: /static/js/main.319f1c51.chunk.js
After: /frontend/static/js/main.319f1c51.chunk.js
```

**To make sure the React imports/assets/etc... can be found even when hosted through Django, you'll also need to update `STATIC_URL` in Django's settings.py to include the path prefix:**
To make sure the React imports/assets/etc... can be found even when hosted through Django, you'll also need to update `STATIC_URL` in Django's settings.py to include the path prefix:

```py
STATIC_URL = '/frontend/static/'
CRA_PACKAGE_JSON_HOMEPAGE = '/frontend'
```

The value set to `CRA_PACKAGE_JSON_HOMEPAGE` above should match the value of `"homepage"` in **package.json** so that **django-cra-helper** can find the CRA liveserver and redirect appropriately:

Once these changes are made then the React app should be able to find everything it needs to function.

## React in Django templates
Expand Down Expand Up @@ -332,7 +335,6 @@ Below is the Django app view's **index.html** template that can render across mu

```html
{% load static %}
{% load cra_helper_tags %}
<!DOCTYPE html>
<html lang="en">

Expand All @@ -353,9 +355,13 @@ Below is the Django app view's **index.html** template that can render across mu
<body>
<div id="react">Loading...</div>

{{ props | json_script:"react-props" }}

<script>
window.component = '{{ component }}';
window.props = {{ props | json }};
window.props = JSON.parse(
document.getElementById('react-props').textContent
);
window.reactRoot = document.getElementById('react');
</script>
{% if bundle_js %}
Expand All @@ -378,12 +384,8 @@ Below is the Django app view's **index.html** template that can render across mu
```
The context's `component` and `props` are bound to `window.component` and `window.props` respectively.

Note the use of the `json` filter when setting `windows.props`! `{% load cra_helper_tags %}` provides this filter as a way to easily sanitize and convert a Python `dict` to a Javascript `Object`. The View context prepared above thus renders to the following typical Javascript Object:
Note the use of the `json_script` filter when setting `windows.props`. [Django provides this filter](https://docs.djangoproject.com/en/3.1/ref/templates/builtins/#json-script) as a way to easily sanitize and convert a Python `dict` to a Javascript `Object`. The contents of the injected `<script>` tag can be run through `JSON.parse()` to safely assign it to a variable.

```js
// This is what is returned in the rendered HTML
window.props = {"env": "Django"};
```
Finally, `window.reactRoot` specifies the container element that the React component should be rendered into. Setting a value for this is only required if the container's `id` is *not* **"root"** (the same ID assigned to the container `<div>` in the CRA project's `index.html`.)

### Combining Django and React routes
Expand Down
17 changes: 16 additions & 1 deletion cra_helper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,23 @@
else:
CRA_APP_NAME = 'react'

# Be mindful of CRA's relative paths when bootstraping from the CRA liveserver
_cra_liveserver_url = CRA_URL
if hasattr(settings, 'CRA_PACKAGE_JSON_HOMEPAGE'):
relative_path = str(settings.CRA_PACKAGE_JSON_HOMEPAGE)
# Normalize homepage path if it starts with / or ends with /
if relative_path.startswith('/'):
# Strip leading /
relative_path = relative_path[1:]
if relative_path.endswith('/'):
# Strip trailing /
relative_path = relative_path[0:-1]

# Should result in something like 'http://localhost:3000/frontend'
_cra_liveserver_url = '{}/{}'.format(_cra_liveserver_url, relative_path)

# The path to the CRA project directory, relative to the Django project's base directory
CRA_FS_APP_DIR = os.path.join(settings.BASE_DIR, CRA_APP_NAME)

# A list of entries in CRA's build bundle's
STATIC_ASSET_MANIFEST = generate_manifest(CRA_URL, CRA_FS_APP_DIR)
STATIC_ASSET_MANIFEST = generate_manifest(_cra_liveserver_url, CRA_FS_APP_DIR)
2 changes: 2 additions & 0 deletions cra_helper/asset_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ def generate_manifest(cra_url: str, app_dir: str) -> dict:
# These two files will alternate being loaded into the page
'{}/static/js/0.chunk.js'.format(cra_url),
'{}/static/js/1.chunk.js'.format(cra_url),
# [email protected]+
'{}/static/js/vendors~main.chunk.js'.format(cra_url),
# This bundle seems to contain some vendor files
'{}/static/js/main.chunk.js'.format(cra_url)
]
Expand Down
Empty file.
21 changes: 0 additions & 21 deletions cra_helper/templatetags/cra_helper_tags.py

This file was deleted.

1 change: 1 addition & 0 deletions cra_helper/tests/test_asset_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def test_returns_bundle_url_if_cra_is_running(self, mock_hosted_check):
'http://foo.bar:9999/static/js/bundle.js',
'http://foo.bar:9999/static/js/0.chunk.js',
'http://foo.bar:9999/static/js/1.chunk.js',
'http://foo.bar:9999/static/js/vendors~main.chunk.js',
'http://foo.bar:9999/static/js/main.chunk.js'
]
})
Expand Down
30 changes: 0 additions & 30 deletions cra_helper/tests/test_cra_helper_tags.py

This file was deleted.

Loading

0 comments on commit fba3899

Please sign in to comment.