Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for type=module & attr nomodule #1006

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions compressor/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,12 @@ def output_preload(self, mode, content, forced=False, basename=None):
"""
return self.output_file(mode, content, forced, basename)

def output_module(self, mode, content, forced=False, basename=None):
"""
The output method that returns <script> with type="module"
"""
return self.output_file(mode, content, forced, basename)

def render_output(self, mode, context=None):
"""
Renders the compressor output with the appropriate template for
Expand Down
7 changes: 7 additions & 0 deletions compressor/js.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,19 @@ def split_contents(self):
else:
content = (SOURCE_HUNK, self.parser.elem_content(elem), None, elem)
self.split_content.append(content)

# one or the other with these 2
if 'async' in attribs:
extra = ' async'
elif 'defer' in attribs:
extra = ' defer'
else:
extra = ''

# optional additional valid script attribute
if 'nomodule' in attribs:
extra += ' nomodule'

# Append to the previous node if it had the same attribute
append_to_previous = (self.extra_nodes
and self.extra_nodes[-1][0] == extra)
Expand Down
1 change: 1 addition & 0 deletions compressor/templates/compressor/css_module.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<link rel="stylesheet" href="{{ compressed.url }}" type="text/css"{% if compressed.media %} media="{{ compressed.media }}"{% endif %}>
1 change: 1 addition & 0 deletions compressor/templates/compressor/js_module.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script type="module" src="{{ compressed.url }}"{{ compressed.extra }}></script>
9 changes: 5 additions & 4 deletions compressor/templatetags/compress.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
OUTPUT_FILE = 'file'
OUTPUT_INLINE = 'inline'
OUTPUT_PRELOAD = 'preload'
OUTPUT_MODES = (OUTPUT_FILE, OUTPUT_INLINE, OUTPUT_PRELOAD)
OUTPUT_MODULE = 'module'
OUTPUT_MODES = (OUTPUT_FILE, OUTPUT_INLINE, OUTPUT_PRELOAD, OUTPUT_MODULE)


class CompressorMixin:
Expand Down Expand Up @@ -158,7 +159,7 @@ def compress(parser, token):

Syntax::

{% compress <js/css> [<file/inline> [block_name]] %}
{% compress <js/css> [<file/inline/preload/module> [block_name]] %}
<html of inline or linked JS/CSS>
{% endcompress %}

Expand All @@ -183,8 +184,8 @@ def compress(parser, token):
mode = args[2]
if mode not in OUTPUT_MODES:
raise template.TemplateSyntaxError(
"%r's second argument must be '%s' or '%s'." %
(args[0], OUTPUT_FILE, OUTPUT_INLINE))
"%r's second argument must be '%s', '%s', '%s' or '%s'." %
(args[0], OUTPUT_FILE, OUTPUT_INLINE, OUTPUT_PRELOAD, OUTPUT_MODULE))
else:
mode = OUTPUT_FILE
if len(args) == 4:
Expand Down
11 changes: 9 additions & 2 deletions compressor/tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@ def test_js_preload_output(self):
out = '<link rel="preload" href="/static/CACHE/js/8a0fed36c317.js" as="script" />'
self.assertEqual(out, self.js_node.output(mode="preload"))

def test_js_module_output(self):
# this needs to have the same hash as in the test above
out = '<script type="module" src="/static/CACHE/js/8a0fed36c317.js"></script>'
self.assertEqual(out, self.js_node.output(mode="module"))

def test_js_override_url(self):
self.js_node.context.update({'url': 'This is not a url, just a text'})
out = '<script src="/static/CACHE/js/8a0fed36c317.js"></script>'
Expand Down Expand Up @@ -347,7 +352,7 @@ def test_correct_backend(self):
class JsAsyncDeferTestCase(SimpleTestCase):
def setUp(self):
self.js = """\
<script src="/static/js/one.js" type="text/javascript"></script>
<script nomodule src="/static/js/one.js" type="text/javascript"></script>
<script src="/static/js/two.js" type="text/javascript" async></script>
<script src="/static/js/three.js" type="text/javascript" defer></script>
<script type="text/javascript">obj.value = "value";</script>
Expand All @@ -361,8 +366,10 @@ def extract_attr(tag):
return 'async'
if tag.has_attr('defer'):
return 'defer'
if tag.has_attr('nomodule'):
return 'nomodule'
js_node = JsCompressor('js', self.js)
output = [None, 'async', 'defer', None, 'async', None]
output = ['nomodule', 'async', 'defer', None, 'async', None]
scripts = make_soup(js_node.output()).find_all('script')
attrs = [extract_attr(s) for s in scripts]
self.assertEqual(output, attrs)
Expand Down
29 changes: 28 additions & 1 deletion docs/usage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Usage
.. code-block:: django

{% load compress %}
{% compress <js/css> [<file/inline/preload> [block_name]] %}
{% compress <js/css> [<file/inline/preload/nomodule> [block_name]] %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't that be module?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nomodule is an attribute itself, but module is a type attribute option - they have a relationship but they are not the same
this is for how the library strips all attributes

<html of inline or linked JS/CSS>
{% endcompress %}

Expand Down Expand Up @@ -63,6 +63,33 @@ Result:
<link rel="preload" href="/static/CACHE/js/d01466eb4fc6.js" as="script" />



ES5 nomodule & ES6 type="module"

Adding the ``nomodule`` attribute will generate be passed attribute to the script tag for the compressed resource in the template.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sentence didn't quite parse for me :)

Adding the ``module`` parameter will generate the script tag with type="module" for the compressed resource in the template:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest clarifying that nomodule needs to go into the script html tag, and module needs to go into the compress templatetag.


.. code-block:: django

{% compress js %}
<!-- ES5 must be in separate compress (split types) -->
<script nomodule async src="static/js/es5.js"></script>
{% endcompress %}

{% compress js module %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better if this supported the type="module" syntax instead of the additional templatetag parameter. Then i would also worry less if it still worked properly with COMPRESS_ENABLED = False.

<!-- ES6 and above JS -->
<script async src="static/js/es6.js"></script>
{% endcompress %}

Result:

.. code-block:: django

<script nomodule async src="/static/CACHE/js/d01466eb4fc6.js"></script>
<script type="module" async src="/static/CACHE/js/d0246230b4fc6.js"></script>



Specifying a ``block_name`` will change the output filename. It can also be
accessed in the :ref:`post_compress signal <signals>` in the ``context`` parameter.

Expand Down