diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 037e3c2f..6c803e3e 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,8 +1,5 @@
blank_issues_enabled: false
contact_links:
- - name: Documentation
- url: https://idom-docs.herokuapp.com/
- about: Refer to the documentation before starting a discussion
- - name: Community Support
- url: https://github.com/idom-team/idom/discussions
- about: Report issues, request features, and ask questions
+ - name: Start a Discussion
+ url: https://github.com/idom-team/django-idom/discussions
+ about: Report issues, request features, ask questions, and share ideas
diff --git a/.github/ISSUE_TEMPLATE/issue-form.yml b/.github/ISSUE_TEMPLATE/issue-form.yml
new file mode 100644
index 00000000..342316ad
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/issue-form.yml
@@ -0,0 +1,23 @@
+name: Plan a Task
+description: Create a detailed plan of action (ONLY START AFTER DISCUSSION PLEASE 🙏).
+labels: ["flag: triage"]
+body:
+- type: textarea
+ attributes:
+ label: Current Situation
+ description: Discuss how things currently are, why they require action, and any relevant prior discussion/context.
+ validations:
+ required: false
+- type: textarea
+ attributes:
+ label: Proposed Actions
+ description: Describe what ought to be done, and why that will address the reasons for action mentioned above.
+ validations:
+ required: false
+- type: textarea
+ attributes:
+ label: Work Items
+ description: |
+ An itemized list or detailed description of the work to be done to based on the proposed actions above.
+ validations:
+ required: false
diff --git a/.github/workflows/publish-js.yml b/.github/workflows/publish-js.yml
new file mode 100644
index 00000000..7c4049e6
--- /dev/null
+++ b/.github/workflows/publish-js.yml
@@ -0,0 +1,29 @@
+# This workflows will upload a Javscript Package using NPM to npmjs.org when a release is created
+# For more information see: https://docs.github.com/en/actions/guides/publishing-nodejs-packages
+
+name: Publish Javascript
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ # Setup .npmrc file to publish to npm
+ - uses: actions/setup-node@v2
+ with:
+ node-version: "14.x"
+ registry-url: "https://registry.npmjs.org"
+ - name: Prepare Release
+ working-directory: ./src/js
+ run: |
+ npm install -g npm@7.22.0
+ npm install
+ - name: Publish Release
+ working-directory: ./src/js
+ run: npm run publish
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTOMATION_TOKEN }}
diff --git a/.github/workflows/publish-py.yml b/.github/workflows/publish-py.yml
new file mode 100644
index 00000000..4b4de80e
--- /dev/null
+++ b/.github/workflows/publish-py.yml
@@ -0,0 +1,36 @@
+# This workflows will upload a Python Package using Twine when a release is created
+# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
+
+name: Publish Python
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ release-package:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-node@v2-beta
+ with:
+ node-version: "14.x"
+ - name: Set up Python
+ uses: actions/setup-python@v1
+ with:
+ python-version: "3.x"
+ - name: Install latest NPM
+ run: |
+ npm install -g npm@7.22.0
+ npm --version
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r requirements/build-pkg.txt
+ - name: Build and publish
+ env:
+ TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
+ TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+ run: |
+ python -m build --sdist --wheel --outdir dist .
+ twine upload dist/*
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index d3097b33..2675c040 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: [3.7, 3.8, 3.9]
+ python-version: ["3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v2
- uses: nanasess/setup-chromedriver@master
diff --git a/.gitignore b/.gitignore
index 1a06cffb..2477a484 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
# Django IDOM Build Artifacts
-src/django_idom/static/js
+src/django_idom/static/
# Django #
logs
@@ -55,6 +55,7 @@ wheels/
*.egg
*.manifest
*.spec
+MANIFEST
# Installer logs
pip-log.txt
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..ac99bf59
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,65 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+
+
+## [Unreleased]
+
+### Added
+
+- Nothing (yet)
+
+## [0.0.2] - 2022-01-30
+
+### Added
+
+- Ability to declare the HTML class of the top-level component `div`
+- `name = ...` parameter to IDOM HTTP paths for use with `django.urls.reverse()`
+- Cache versioning to automatically invalidate old web module files from the cache backend
+- Automatic pre-population of the IDOM component registry
+- Type hinting for `IdomWebsocket`
+
+### Changed
+
+- Fetching web modules from disk and/or cache is now fully async
+- Static files are now contained within a `django_idom/` parent folder
+- Upgraded IDOM to version `0.36.0`
+- Minimum Django version required is now `4.0`
+- Minimum Python version required is now `3.8`
+
+### Removed
+
+- `IDOM_WEB_MODULES_PATH` has been replaced with Django `include(...)`
+- `IDOM_WS_MAX_RECONNECT_DELAY` has been renamed to `IDOM_WS_MAX_RECONNECT_TIMEOUT`
+- `idom_web_modules` cache backend has been renamed to `idom`
+
+### Fixed
+
+- Increase test timeout values to prevent false positives
+- Windows compatibility for building Django-IDOM
+
+### Security
+
+- Fixed potential directory travesal attack on the IDOM web modules URL
+
+## [0.0.1] - 2021-08-18
+
+### Added
+
+- Support for IDOM within the Django
+
+[unreleased]: https://github.com/idom-team/django-idom/compare/0.0.2...HEAD
+[0.0.2]: https://github.com/idom-team/django-idom/compare/0.0.1...0.0.2
+[0.0.1]: https://github.com/idom-team/django-idom/releases/tag/0.0.1
diff --git a/MANIFEST.in b/MANIFEST.in
index a43e500c..5b9e5fe2 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,2 +1,2 @@
-recursive-include src/django_idom/static/ *
-recursive-include src/django_idom/templates/ *.html
+recursive-include src/django_idom/static *
+recursive-include src/django_idom/templates *.html
diff --git a/README.md b/README.md
index 08c80f6d..9d3120e6 100644
--- a/README.md
+++ b/README.md
@@ -1,210 +1,142 @@
-# Django IDOM
+# Django IDOM · [](https://github.com/idom-team/django-idom/actions?query=workflow%3ATest) [](https://pypi.python.org/pypi/django-idom) [](https://github.com/idom-team/django-idom/blob/main/LICENSE)
-
-
-
-
-
-
-
-
-
-
-`django-idom` allows you to integrate [IDOM](https://github.com/idom-team/idom) into
-Django applications. IDOM is a pure Python library inspired by
-[ReactJS](https://reactjs.org/) for creating responsive web interfaces.
+`django-idom` allows Django to integrate with [IDOM](https://github.com/idom-team/idom), a reactive Python web framework for building **interactive websites without needing a single line of Javascript**.
**You can try IDOM now in a Jupyter Notebook:**
-
+# Quick Example
-# Install Django IDOM
-
-```bash
-pip install django-idom
-```
+## `example_app/components.py`
-# Django Integration
+This is where you'll define your [IDOM](https://github.com/idom-team/idom) components. Ultimately though, you should
+feel free to organize your component modules as you wish. Any components created will ultimately be referenced
+by Python dotted path in `your-template.html`.
-To integrate IDOM into your application you'll need to modify or add the following files to `your_project`:
+```python
+from idom import component, html
+from django_idom import IdomWebsocket
+# Components are CamelCase by ReactJS convention
+@component
+def Hello(websocket: IdomWebsocket, greeting_recipient: str):
+ return html.header(f"Hello {greeting_recipient}!")
```
-your_project/
-├── __init__.py
-├── asgi.py
-├── settings.py
-├── urls.py
-└── example_app/
- ├── __init__.py
- ├── idom.py
- ├── templates/
- │ └── your-template.html
- └── urls.py
-```
-
-## `asgi.py`
-Follow the [`channels`](https://channels.readthedocs.io/en/stable/)
-[installation guide](https://channels.readthedocs.io/en/stable/installation.html) in
-order to create ASGI websockets within Django. Then, we will add a path for IDOM's
-websocket consumer using `IDOM_WEBSOCKET_PATH`.
+## [`example_app/templates/your-template.html`](https://docs.djangoproject.com/en/dev/topics/templates/)
-_Note: If you wish to change the route where this websocket is served from, see the
-available [settings](#settings.py)._
+In your templates, you may add IDOM components into your HTML by using the `idom_component`
+template tag. This tag requires the dotted path to the component function.
-```python
+Additonally, you can pass in keyworded arguments into your component function.
-import os
+In context this will look a bit like the following...
-from django.core.asgi import get_asgi_application
+```jinja
+{% load idom %}
-from django_idom import IDOM_WEB_MODULES_PATH
+
+
+
+ Example Project
+
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_app.settings")
+
+ {% idom_component "my_django_project.example_app.components.Hello" greeting_recipient="World" %}
+
+
+```
-# Fetch ASGI application before importing dependencies that require ORM models.
-http_asgi_app = get_asgi_application()
+# Installation
-from channels.routing import ProtocolTypeRouter, URLRouter
+Install `django-idom` via pip.
-application = ProtocolTypeRouter(
- {
- "http": http_asgi_app,
- "websocket": URLRouter(
- # add a path for IDOM's websocket
- [IDOM_WEB_MODULES_PATH]
- ),
- }
-)
+```bash
+pip install django-idom
```
-## `settings.py`
+You'll also need to modify a few files in your Django project.
+
+## [`settings.py`](https://docs.djangoproject.com/en/dev/topics/settings/)
-In your settings you'll need to add `django_idom` to the
-[`INSTALLED_APPS`](https://docs.djangoproject.com/en/3.2/ref/settings/#std:setting-INSTALLED_APPS)
-list:
+In your settings you'll need to add `channels` and `django_idom` to [`INSTALLED_APPS`](https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-INSTALLED_APPS).
```python
INSTALLED_APPS = [
...,
+ "channels",
"django_idom",
]
+
+# Ensure ASGI_APPLICATION is set properly based on your project name!
+ASGI_APPLICATION = "my_django_project.asgi.application"
```
-You may configure additional options as well:
+**Optional:** You can also configure IDOM settings.
```python
-# the base URL for all IDOM-releated resources
-IDOM_BASE_URL: str = "_idom/"
-
-# Set cache size limit for loading JS files for IDOM.
-# Only applies when not using Django's caching framework (see below).
-IDOM_WEB_MODULE_LRU_CACHE_SIZE: int | None = None
-
-# Configure a cache for loading JS files
+# If "idom" cache is not configured, then we'll use "default" instead
CACHES = {
- # Configure a cache for loading JS files for IDOM
- "idom_web_modules": {"BACKEND": ...},
- # If the above cache is not configured, then we'll use the "default" instead
- "default": {"BACKEND": ...},
+ "idom": {"BACKEND": ...},
}
+
+# Maximum seconds between two reconnection attempts that would cause the client give up.
+# 0 will disable reconnection.
+IDOM_WS_MAX_RECONNECT_TIMEOUT: int = 604800
+
+# The URL for IDOM to serve websockets
+IDOM_WEBSOCKET_URL: str = "idom/"
```
-## `urls.py`
+## [`urls.py`](https://docs.djangoproject.com/en/dev/topics/http/urls/)
-You'll need to include IDOM's static web modules path using `IDOM_WEB_MODULES_PATH`.
-Similarly to the `IDOM_WEBSOCKET_PATH`. If you wish to change the route where this
-websocket is served from, see the available [settings](#settings.py).
+Add IDOM HTTP paths to your `urlpatterns`.
```python
-from django_idom import IDOM_WEB_MODULES_PATH
+from django.urls import include, path
urlpatterns = [
- IDOM_WEB_MODULES_PATH,
+ path("idom/", include("django_idom.http.urls")),
...
]
```
-## `example_app/components.py`
+## [`asgi.py`](https://docs.djangoproject.com/en/dev/howto/deployment/asgi/)
-This is where, by a convention similar to that of
-[`views.py`](https://docs.djangoproject.com/en/3.2/topics/http/views/), you'll define
-your [IDOM](https://github.com/idom-team/idom) components. Ultimately though, you should
-feel free to organize your component modules you wish. The components created here will
-ultimately be referenced by name in `your-template.html`. `your-template.html`.
+Register IDOM's websocket using `IDOM_WEBSOCKET_PATH`.
-```python
-import idom
-
-@idom.component
-def Hello(greeting_recipient): # component names are camelcase by convention
- return Header(f"Hello {greeting_recipient}!")
-```
-
-## `example_app/templates/your-template.html`
-
-In your templates, you may inject a view of an IDOM component into your templated HTML
-by using the `idom_component` template tag. This tag which requires the name of a component
-to render (of the form `module_name.ComponentName`) and keyword arguments you'd like to
-pass it from the template.
-
-```python
-idom_component module_name.ComponentName param_1="something" param_2="something-else"
-```
-
-In context this will look a bit like the following...
-
-```jinja
-
-{% load static %}
-{% load idom %}
-
-
-
-
- ...
- {% idom_component "your_project.example_app.components.Hello" greeting_recipient="World" %}
-
-
-```
-
-## `example_app/views.py`
-
-You can then serve `your-template.html` from a view just
-[like any other](https://docs.djangoproject.com/en/3.2/intro/tutorial03/#write-views-that-actually-do-something).
+_Note: If you do not have an `asgi.py`, follow the [`channels` installation guide](https://channels.readthedocs.io/en/stable/installation.html)._
```python
-from django.http import HttpResponse
-from django.template import loader
-
-def your_view(request):
- context = {}
- return HttpResponse(
- loader.get_template("your-template.html").render(context, request)
- )
-```
-
-## `example_app/urls.py`
+import os
+from django.core.asgi import get_asgi_application
-Include your view in the list of urlpatterns
+# Ensure DJANGO_SETTINGS_MODULE is set properly based on your project name!
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "my_django_project.settings")
+django_asgi_app = get_asgi_application()
-```python
-from django.urls import path
-from .views import your_view # define this view like any other HTML template view
+from channels.auth import AuthMiddlewareStack
+from channels.routing import ProtocolTypeRouter, URLRouter
+from channels.sessions import SessionMiddlewareStack
+from django_idom import IDOM_WEBSOCKET_PATH
-urlpatterns = [
- path("", your_view),
- ...
-]
+application = ProtocolTypeRouter(
+ {
+ "http": django_asgi_app,
+ "websocket": SessionMiddlewareStack(
+ AuthMiddlewareStack(URLRouter([IDOM_WEBSOCKET_PATH]))
+ ),
+ }
+)
```
# Developer Guide
diff --git a/requirements/build-pkg.txt b/requirements/build-pkg.txt
new file mode 100644
index 00000000..82f40eaf
--- /dev/null
+++ b/requirements/build-pkg.txt
@@ -0,0 +1,3 @@
+twine
+wheel
+build
diff --git a/requirements/pkg-deps.txt b/requirements/pkg-deps.txt
index b823d656..43b36f85 100644
--- a/requirements/pkg-deps.txt
+++ b/requirements/pkg-deps.txt
@@ -1,2 +1,3 @@
-channels<4.0.0 # Django websocket features
-idom<1.0.0 # Python React
+channels <4.0.0
+idom >=0.36.0, <0.37.0
+aiofile >=3.0, <4.0
diff --git a/requirements/test-env.txt b/requirements/test-env.txt
index c100c316..6f2e151e 100644
--- a/requirements/test-env.txt
+++ b/requirements/test-env.txt
@@ -1,6 +1,3 @@
django
selenium
-
-# required due issue with channels:
-# https://github.com/django/channels/issues/1639#issuecomment-817994671
-twisted<21
+twisted
diff --git a/setup.py b/setup.py
index adeb5937..9957e4ae 100644
--- a/setup.py
+++ b/setup.py
@@ -38,7 +38,7 @@ def list2cmdline(cmd_list):
package = {
"name": name,
- "python_requires": ">=3.7",
+ "python_requires": ">=3.8",
"packages": find_packages(str(src_dir)),
"package_dir": {"": "src"},
"description": "Control the web with Python",
@@ -52,15 +52,14 @@ def list2cmdline(cmd_list):
"zip_safe": False,
"classifiers": [
"Framework :: Django",
- "Framework :: Django :: 3.1",
- "Framework :: Django :: 3.2",
+ "Framework :: Django :: 4.0",
"Operating System :: OS Independent",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"Topic :: Multimedia :: Graphics",
- "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
"Environment :: Web Environment",
],
}
diff --git a/src/django_idom/__init__.py b/src/django_idom/__init__.py
index 90a4aba1..88821fcd 100644
--- a/src/django_idom/__init__.py
+++ b/src/django_idom/__init__.py
@@ -1,5 +1,6 @@
-from .paths import IDOM_WEB_MODULES_PATH, IDOM_WEBSOCKET_PATH
+from .websocket.consumer import IdomWebsocket
+from .websocket.paths import IDOM_WEBSOCKET_PATH
-__version__ = "0.0.1"
-__all__ = ["IDOM_WEB_MODULES_PATH", "IDOM_WEBSOCKET_PATH"]
+__version__ = "0.0.2"
+__all__ = ["IDOM_WEBSOCKET_PATH", "IdomWebsocket"]
diff --git a/src/django_idom/apps.py b/src/django_idom/apps.py
new file mode 100644
index 00000000..6a963664
--- /dev/null
+++ b/src/django_idom/apps.py
@@ -0,0 +1,11 @@
+from django.apps import AppConfig
+
+from django_idom.utils import ComponentPreloader
+
+
+class DjangoIdomConfig(AppConfig):
+ name = "django_idom"
+
+ def ready(self):
+ # Populate the IDOM component registry when Django is ready
+ ComponentPreloader().register_all()
diff --git a/src/django_idom/config.py b/src/django_idom/config.py
index 9b2d9f0a..c024d77d 100644
--- a/src/django_idom/config.py
+++ b/src/django_idom/config.py
@@ -1,27 +1,19 @@
from typing import Dict
from django.conf import settings
-from django.core.cache import DEFAULT_CACHE_ALIAS
+from django.core.cache import DEFAULT_CACHE_ALIAS, caches
from idom.core.proto import ComponentConstructor
IDOM_REGISTERED_COMPONENTS: Dict[str, ComponentConstructor] = {}
-IDOM_BASE_URL = getattr(settings, "IDOM_BASE_URL", "_idom/")
-IDOM_WEBSOCKET_URL = IDOM_BASE_URL + "websocket/"
-IDOM_WEB_MODULES_URL = IDOM_BASE_URL + "web_module/"
+IDOM_WEBSOCKET_URL = getattr(settings, "IDOM_WEBSOCKET_URL", "idom/")
+IDOM_WS_MAX_RECONNECT_TIMEOUT = getattr(
+ settings, "IDOM_WS_MAX_RECONNECT_TIMEOUT", 604800
+)
-_CACHES = getattr(settings, "CACHES", {})
-if _CACHES:
- if "idom_web_modules" in getattr(settings, "CACHES", {}):
- IDOM_WEB_MODULE_CACHE = "idom_web_modules"
- else:
- IDOM_WEB_MODULE_CACHE = DEFAULT_CACHE_ALIAS
+# Determine if using Django caching or LRU cache
+if "idom" in getattr(settings, "CACHES", {}):
+ IDOM_CACHE = caches["idom"]
else:
- IDOM_WEB_MODULE_CACHE = None
-
-
-# the LRU cache size for the route serving IDOM_WEB_MODULES_DIR files
-IDOM_WEB_MODULE_LRU_CACHE_SIZE = getattr(
- settings, "IDOM_WEB_MODULE_LRU_CACHE_SIZE", None
-)
+ IDOM_CACHE = caches[DEFAULT_CACHE_ALIAS]
diff --git a/src/django_idom/http/__init__.py b/src/django_idom/http/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/django_idom/http/urls.py b/src/django_idom/http/urls.py
new file mode 100644
index 00000000..019a603e
--- /dev/null
+++ b/src/django_idom/http/urls.py
@@ -0,0 +1,14 @@
+from django.urls import path
+
+from . import views
+
+
+app_name = "idom"
+
+urlpatterns = [
+ path(
+ "web_module/",
+ views.web_modules_file,
+ name="web_modules",
+ )
+]
diff --git a/src/django_idom/http/views.py b/src/django_idom/http/views.py
new file mode 100644
index 00000000..552d7418
--- /dev/null
+++ b/src/django_idom/http/views.py
@@ -0,0 +1,34 @@
+import os
+
+from aiofile import async_open
+from django.core.exceptions import SuspiciousOperation
+from django.http import HttpRequest, HttpResponse
+from idom.config import IDOM_WED_MODULES_DIR
+
+from django_idom.config import IDOM_CACHE
+
+
+async def web_modules_file(request: HttpRequest, file: str) -> HttpResponse:
+ """Gets JavaScript required for IDOM modules at runtime. These modules are
+ returned from cache if available."""
+ web_modules_dir = IDOM_WED_MODULES_DIR.current
+ path = web_modules_dir.joinpath(*file.split("/")).absolute()
+
+ # Prevent attempts to walk outside of the web modules dir
+ if str(web_modules_dir) != os.path.commonpath((path, web_modules_dir)):
+ raise SuspiciousOperation(
+ "Attempt to access a directory outside of IDOM_WED_MODULES_DIR."
+ )
+
+ # Fetch the file from cache, if available
+ last_modified_time = os.stat(path).st_mtime
+ cache_key = f"django_idom:web_module:{str(path).lstrip(str(web_modules_dir))}"
+ response = await IDOM_CACHE.aget(cache_key, version=last_modified_time)
+ if response is None:
+ async with async_open(path, "r") as fp:
+ response = HttpResponse(await fp.read(), content_type="text/javascript")
+ await IDOM_CACHE.adelete(cache_key)
+ await IDOM_CACHE.aset(
+ cache_key, response, timeout=None, version=last_modified_time
+ )
+ return response
diff --git a/src/django_idom/paths.py b/src/django_idom/paths.py
deleted file mode 100644
index 62a346e1..00000000
--- a/src/django_idom/paths.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from django.urls import path
-
-from . import views
-from .config import IDOM_WEB_MODULES_URL, IDOM_WEBSOCKET_URL
-from .websocket_consumer import IdomAsyncWebSocketConsumer
-
-
-IDOM_WEBSOCKET_PATH = path(
- IDOM_WEBSOCKET_URL + "/", IdomAsyncWebSocketConsumer.as_asgi()
-)
-"""A URL resolver for :class:`IdomAsyncWebSocketConsumer`
-
-While this is relatively uncommon in most Django apps, because the URL of the
-websocket must be defined by the setting ``IDOM_WEBSOCKET_URL``. There's no need
-to allow users to configure the URL themselves.
-"""
-
-
-IDOM_WEB_MODULES_PATH = path(
- IDOM_WEB_MODULES_URL + "", views.web_modules_file
-)
-"""A URL resolver for static web modules required by IDOM
-
-While this is relatively uncommon in most Django apps, because the URL of the
-websocket must be defined by the setting ``IDOM_WEBSOCKET_URL``. There's no need
-to allow users to configure the URL themselves.
-"""
diff --git a/src/django_idom/templates/idom/view.html b/src/django_idom/templates/idom/component.html
similarity index 64%
rename from src/django_idom/templates/idom/view.html
rename to src/django_idom/templates/idom/component.html
index adfc3a6d..fc8ba6f8 100644
--- a/src/django_idom/templates/idom/view.html
+++ b/src/django_idom/templates/idom/component.html
@@ -1,12 +1,13 @@
{% load static %}
-
+