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

Combined JsCss compressor #445

Closed
tkrajca opened this issue Oct 7, 2013 · 1 comment
Closed

Combined JsCss compressor #445

tkrajca opened this issue Oct 7, 2013 · 1 comment

Comments

@tkrajca
Copy link

tkrajca commented Oct 7, 2013

Hi Jannis,

I have not got a reply on irc last week so I thought I would email you instead.

I wrote a JsCss compressor for django-compressor and was wondering whether it would be worth adding it to the django-compressor itself (I would need to add appropriate level of documentation and tests and make it nicer).

Features:
* compresses both javascript and css "at once"
* outputs javascript or css with attribute data-compress=false untouched
* outputs the rest of html untouched

So one can then put something like this into the base template:

<!doctype html>
<html>
<head>
   {% compress js %}
   ...
   {% endcompress %}
</body>
</html>

and it will compress all js and css in all the templates that inherit from base. So any resulting html will then load at most one css and/or one js file. I use 'compress js' even though it's for both css and js simply because I didn't want to change the templatetag logic.

In this sense, it could even be made into a middleware class.

Attached is the source code of the compressor (it's quickly put together, nothing extra nice yet but it works quite nicely with one of our internal projects).

Cheers,
Tomas

from compressor.js import JsCompressor as DefaultJsCompressor
from compressor.css import CssCompressor as DefaultCssCompressor
from compressor.parser.lxml import LxmlParser
from compressor.base import Compressor

#
# settings.py
#
# COMPRESS_JS_COMPRESSOR = '.compress.JsCssCompressor'
# COMPRESS_PARSER = '.compress.CompressParser'
#
# base.html
#
# <!doctype html>
# <html>
# <head>
#    {% compress js %}
#    ...
#    {% endcompress %}
# </head>
# </html>


class CompressorMixin(Compressor):
    def split_contents(self):
        _split_contents = super(CompressorMixin, self).split_contents()
        for kind, value, basename, elem in _split_contents:
            self.parser.elem_remove(elem)
        return _split_contents


class JsCompressor(CompressorMixin, DefaultJsCompressor):
    pass


class CssCompressor(CompressorMixin, DefaultCssCompressor):
    pass


class JsCssCompressor(object):
    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs

    def output(self, mode='file', forced=False):
        css_compressor = CssCompressor(*self.args, **self.kwargs)
        css_output = css_compressor.output(mode, forced)
        self.kwargs.update({
            'content': css_compressor.parser.get_processed_content()})
        js_compressor = JsCompressor(*self.args, **self.kwargs)
        js_output = js_compressor.output(mode, forced)
        return (css_output + js_output +
                js_compressor.parser.get_processed_content())


class CompressParser(LxmlParser):
    def js_elems(self):
        return self.tree.xpath("//script[not(@data-compress='false')]")

    def css_elems(self):
        return self.tree.xpath(
            '//link[re:test(@rel, "^stylesheet$", "i")]'
            '[not(@data-compress="false")]|style[not(@data-compress="false")]',
            namespaces={"re": "http://exslt.org/regular-expressions"})

    def get_processed_content(self):
        # remove <root> and </root>
        return self.tostring(self.tree, method='html')[6:-7]

    def elem_remove(self, elem):
        elem.getparent().remove(elem)
@diox
Copy link
Member

diox commented Sep 26, 2015

Hi,

Sorry nobody got to you sooner. I feel this is a little too complex to have in compressor core, especially as it's not a common use-case. It should be possible to implement this outside compressor though.

@diox diox closed this as completed Sep 26, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants