Git Product home page Git Product logo

jinja-assets-compressor's Introduction

Build Status

jinja-assets-compressor

A Jinja2 extension to compile and/or compress your assets.

Installing

pip install jac

For LESS and CSS support, install less:

npm install -g less

For COFFEE support, install coffee-script:

npm install -g coffee-script

For Sass and SCSS support, install sass:

gem install sass

JavaScript minification is built-in using the Python rJsmin package.

When installing on Mac OS X set this shell variable, because jac dependencies contain C code:

export CFLAGS=-Qunused-arguments

Usage

To use it, you just have to put your css or js inside a compress tag.

{% compress 'css' %}
<style type="text/sass">
sass stuff
</style>
<link rel="stylesheet" type="text/sass" href="file.sass">
{% endcompress %}

{% compress 'js' %}
<script type="text/coffeescript">
coffee stuff
</script>
<script type="text/coffeescript" src="file.coffee"></script>
{% endcompress %}

Configuring Jinja

You just have to create an environment with jac on it and configure output dir, static prefix and say where it can find your sources.

import jinja2

from jac import CompressorExtension

env = jinja2.Environment(extensions=[CompressorExtension])
env.compressor_output_dir = './static/dist'
env.compressor_static_prefix = '/static'
env.compressor_source_dirs = './static_files'

After that just use template = env.from_string(html); template.render() to get it done.

Configuring Flask

Where you configure your app, just do this:

from jac.contrib.flask import JAC

app = Flask(__name__)
app.config['COMPRESSOR_DEBUG'] = app.config.get('DEBUG')
app.config['COMPRESSOR_OUTPUT_DIR'] = './static/dist'
app.config['COMPRESSOR_STATIC_PREFIX'] = '/static'
jac = JAC(app)

And you are done.

Offline Compression

JAC supports compressing static assets offline, then deploying to a production server. Here is a command to compress your static assets if using Flask: :

python -m jac.contrib.flask my_flask_module:create_app

Replace my_flask_module with the correct import path to find your Flask app.

Custom Compressors

The compressor_classes template env variable tells jac which compressor to use for each mimetype. The default value for compressor_classes is:

{
    'text/css': LessCompressor,
    'text/coffeescript': CoffeeScriptCompressor,
    'text/less': LessCompressor,
    'text/javascript': JavaScriptCompressor,
    'text/sass': SassCompressor,
    'text/scss': SassCompressor,
}

To use an alternate compressor class, provide a class with a compile class method accepting arg text and kwargs mimetype, cwd, uri_cwd, and debug. For example, to use libsass-python for SASS files instead of the built-in SassCompressor, create your custom compressor class:

import sass

class CustomSassCompressor(object):
    """Custom compressor for text/sass mimetype.

    Uses libsass-python for compression.
    """

    @classmethod
    def compile(cls, text, cwd=None, **kwargs):

        include_paths = []
        if cwd:
            include_paths += [cwd]

        return sass.compile(string=text, include_paths=include_paths)

Then tell jac to use your custom compressor for text/sass mimetypes:

env.compressor_classes['text/sass'] = CustomSassCompressor

The equivalent for Flask is:

jac.set_compressor('text/sass', CustomSassCompressor)

To only customize the path of a compressor which forks a subprocess for the compile step (LessCompressor, CoffeeScriptCompressor, and SassCompressor), just extend the compressor class and overwrite the binary class attribute:

from jac.compressors import SassCompressor

class CustomSassCompressor(SassCompressor):
    """Custom SASS compressor using Compass binary instead of libsass for text/sass mimetype.

    Uses the faster libsass wrapper sassc for SASS compression.
    https://github.com/sass/sassc
    """

    binary = '/usr/bin/sassc'

# Tell Flask to use our custom SASS compressor
jac.set_compressor('text/sass', CustomSassCompressor)

Running Tests

virtualenv venv
. venv/bin/activate
pip install -r requirements_tests.txt
make coverage
make lint

Or use tox to run with multiple python versions:

pip install tox
tox

jinja-assets-compressor's People

Contributors

alanhamlett avatar andreasbackx avatar jaysonsantos avatar requires avatar rochacon avatar samael500 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

jinja-assets-compressor's Issues

Automatically select type based on relation attribute

When you don't specify a type on a css file included as a link it throws an error Tags to be compressed must have a type attribute. Considering we have the rel attribute we can guess that it is css like we guess with JS.

{% compress 'css' %}
     <link href="{{ url_for('static', filename='layout.css') }}" rel="stylesheet">
{% endcompress %}

how to deploy on multi servers with this tools?

I use this tools on multi servers, and it generates 404 error because there are different compressed js file between different servers.

So I want ask, how to deploy on multi servers with this tools?

Allow configuring compiler commands

For changing compiler programs and their arguments.

Follow the django-compressor convention of mime-type to compiler command which is passed to str.format()

COMPILERS = (
    # ('text/coffeescript', 'coffee --compile --stdio'),
    # ('text/less', 'lessc {infile} {outfile}'),
    # ('text/x-sass', 'sass {infile} {outfile}'),
    # ('text/stylus', 'stylus < {infile} > {outfile}'),
    # ('text/x-scss', 'sass --scss {infile} {outfile}'),
)

Dependencies shouldn't be pinned

Hi,

Since this is a library, the dependencies for it shouldn't be pinned - if there are known incompatibilities, the versions should be set to ranges (like >=1.1,<2, if it's incompatible with versions 2 and above from a certain library) and not exact versions. Otherwise, projects which use this library won't be able to use other versions from these dependencies.

E.g.: I have a project which uses a newer version of six, but since jac pins it to an older version, I can't use that, it's conflicting.

Thanks!

Keep reference to original file name in compressed file name

Hi there,

Thanks for the great library!

I was wondering whether we could add an option to keep a reference to the original file name in the compressed file name?

For example, if I have a custom.css and wrap it around a {% compress 'css' %} block, the new file name will be something like 5ef7a08e1b4dfe00216375aa6b9ecc0d.css.

I would love to have it transformed into something like custom.5ef7a08e1b4dfe00216375aa6b9ecc0d.css, so that I know that this file is a compression of custom.css.

Thanks!

Can't install on python 3.5

Hello!
Thanks for providing awesome package!
I've upgraded to kubuntu 16.04 and it have python 3 version 3.5 by default.
But jac is dependant on rjsmin==1.0.10 which causes an error during installation Need at max python 3.4 (vs. 3.5.1).
Newer version of rjsmin support python 3.5 (see ndparker/rjsmin#5)
So please fix dependancy problem.

How to ignore BeautifulSoup warning?

I have found some related issue, but I still don't know how to ignore, where is the setting to be placed ?

This is my code:

    # jac
    mode = os.getenv(DEPLOY_MODE_KEY, 'DEV')
    app.config['COMPRESSOR_DEBUG'] = (mode == 'DEV')
    app.config['COMPRESSOR_OUTPUT_DIR'] = '%s/niss/static/dist' % sys.path[0]
    app.config['COMPRESSOR_STATIC_PREFIX'] = '/static/dist'
    jac = JAC(app)

If you change the source code, Why can't i download the latest version?

Renaming the `frameworks` package to `contrib`

I think that contrib may be a better naming for the external libraries/frameworks support package. As an example the import of the JAC Flask extension (PR: #1) would be something like:

from jac.contrib.flask import JAC

offline_compress not work with macro

offline_compress not work with macro, e.g:

{% import 'home/circle.html' as circle with context %}
{% set js = circle.js() %}
    {% compress 'js' %}
        {{ js }}
    {% endcompress %}

this wont generate the compressed js file

please fix this bug,thanks

Raise a better error when compiler is not found in PATH

When the compiler is not found we let OSError: [Errno 2] No such file or directory be thrown. We should return a better error and a better message like Executable {name} not found.

Full stacktrace of an example:

Traceback (most recent call last):
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/flask/app.py", line 2000, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/flask/app.py", line 1991, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/flask/app.py", line 1567, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/flask/app.py", line 1988, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/flask/app.py", line 1641, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/flask/app.py", line 1544, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/flask/app.py", line 1639, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/flask/app.py", line 1625, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/flask_cache/__init__.py", line 297, in decorated_function
    rv = f(*args, **kwargs)
  File "/Users/liuyiqi/code/src/opendata/app/home/views.py", line 9, in index
    return render_template('home.html')
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/flask/templating.py", line 134, in render_template
    context, ctx.app)
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/flask/templating.py", line 116, in _render
    rv = template.render(context)
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/jinja2/environment.py", line 1008, in render
    return self.environment.handle_exception(exc_info, True)
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/jinja2/environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/liuyiqi/code/src/opendata/app/templates/home.html", line 3, in top-level template code
    {% extends 'layouts/base.html' %}
  File "/Users/liuyiqi/code/src/opendata/app/templates/layouts/base.html", line 26, in top-level template code
    {% compress 'css' %}
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/jac/extension.py", line 39, in _compress_block
    return self.compressor.compress(html, compression_type)
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/jac/base.py", line 103, in compress
    debug=self.config.compressor_debug)
  File "/Users/liuyiqi/venv/opendata/lib/python2.7/site-packages/jac/compressors/less.py", line 35, in compile
    stderr=subprocess.PIPE, cwd=None)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 710, in __init__
    errread, errwrite)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1335, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

Allow compressing outside Flask request context

... so the compress and _make_hash steps can be part of a release cycle instead of run every time a template renders.

This could be done with a cli tool to find all templates, compress them, then cache the list of files compressed with their final locations. Then every time a template renders, hash the list of files (paths not contents), lookup location of compressed contents, re-render dom element with new src/href.

Not work with flask-user and config problem with flask

Old config not working

from jac.contrib.flask import JAC

app = Flask(__name__)
app.config['COMPRESSOR_DEBUG'] = app.config.get('DEBUG')
app.config['COMPRESSOR_OUTPUT_DIR'] = './static/dist'
app.config['COMPRESSOR_STATIC_PREFIX'] = '/static'
jac = JAC(app)

change to

jac.init_app(app)
app.jinja_env.compressor_debug = app.config.get('DEBUG')
app.jinja_env.compressor_output_dir = './static'
app.jinja_env.compressor_static_prefix = '/static'

When use with flask-user, flask contrb need moidify static_finder

def static_finder(app):
    def find(path=None):
        if path is None:
            folders = set()
            for blueprint in app.blueprints.values():
                if blueprint.static_folder is not None:
                    folders.update([blueprint.static_folder])
            folders.update([app.static_folder])
            return folders
        else:
            bp_values = app.blueprints.values()
            # dectect flask-user
            if 'flask_user' in [x.name for x in bp_values]:
                app.blueprints['flask_user'].name = 'user'

            for rule in app.url_map.iter_rules():
                if '.' in rule.endpoint:
                    with_blueprint = True
                    blueprint_name = rule.endpoint.rsplit('.', 1)[0]
                    for x in bp_values:
                        if x.name == blueprint_name:
                            blueprint = x

                    data = rule.match(u('{subdomain}|{path}').format(
                        subdomain=blueprint.subdomain or '',
                        path=path,
                    ))
                else:
                    with_blueprint = False
                    data = rule.match(u('|{0}').format(path))

                if data:
                    static_folder = blueprint.static_folder if with_blueprint and blueprint.static_folder is not None else app.static_folder
                    return os.path.join(static_folder, data['filename'])

        raise IOError(2, u('File not found {0}.').format(path))

    return find

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.