diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | compressor/filters/cssmin/__init__.py | 3 | ||||
-rwxr-xr-x | compressor/filters/cssmin/rcssmin.py | 374 | ||||
-rw-r--r-- | compressor/filters/jsmin/__init__.py | 3 | ||||
-rwxr-xr-x | compressor/filters/jsmin/rjsmin.py | 514 | ||||
-rw-r--r-- | docs/settings.txt | 4 | ||||
-rw-r--r-- | requirements/tests.txt | 2 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rw-r--r-- | tox.ini | 8 |
9 files changed, 19 insertions, 893 deletions
@@ -10,7 +10,7 @@ runtests: coverage run --branch --source=compressor `which django-admin.py` test --settings=compressor.test_settings compressor coveragereport: - coverage report --omit=compressor/test*,compressor/filters/jsmin/rjsmin*,compressor/filters/cssmin/cssmin* + coverage report --omit=compressor/test* test: flake8 runtests coveragereport diff --git a/compressor/filters/cssmin/__init__.py b/compressor/filters/cssmin/__init__.py index 01a916d..3b127a9 100644 --- a/compressor/filters/cssmin/__init__.py +++ b/compressor/filters/cssmin/__init__.py @@ -11,7 +11,8 @@ class CSSCompressorFilter(CallbackOutputFilter): class rCSSMinFilter(CallbackOutputFilter): - callback = "compressor.filters.cssmin.rcssmin.cssmin" + callback = "rcssmin.cssmin" + dependencies = ["rcssmin"] kwargs = { "keep_bang_comments": True } diff --git a/compressor/filters/cssmin/rcssmin.py b/compressor/filters/cssmin/rcssmin.py deleted file mode 100755 index 5f590bc..0000000 --- a/compressor/filters/cssmin/rcssmin.py +++ /dev/null @@ -1,374 +0,0 @@ -#!/usr/bin/env python -# -*- coding: ascii -*- -r""" -============== - CSS Minifier -============== - -CSS Minifier. - -The minifier is based on the semantics of the `YUI compressor`_\\, which -itself is based on `the rule list by Isaac Schlueter`_\\. - -:Copyright: - - Copyright 2011 - 2015 - Andr\xe9 Malo or his licensors, as applicable - -:License: - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -This module is a re-implementation aiming for speed instead of maximum -compression, so it can be used at runtime (rather than during a preprocessing -step). RCSSmin does syntactical compression only (removing spaces, comments -and possibly semicolons). It does not provide semantic compression (like -removing empty blocks, collapsing redundant properties etc). It does, however, -support various CSS hacks (by keeping them working as intended). - -Here's a feature list: - -- Strings are kept, except that escaped newlines are stripped -- Space/Comments before the very end or before various characters are - stripped: ``:{});=>],!`` (The colon (``:``) is a special case, a single - space is kept if it's outside a ruleset.) -- Space/Comments at the very beginning or after various characters are - stripped: ``{}(=:>[,!`` -- Optional space after unicode escapes is kept, resp. replaced by a simple - space -- whitespaces inside ``url()`` definitions are stripped -- Comments starting with an exclamation mark (``!``) can be kept optionally. -- All other comments and/or whitespace characters are replaced by a single - space. -- Multiple consecutive semicolons are reduced to one -- The last semicolon within a ruleset is stripped -- CSS Hacks supported: - - - IE7 hack (``>/**/``) - - Mac-IE5 hack (``/*\\*/.../**/``) - - The boxmodelhack is supported naturally because it relies on valid CSS2 - strings - - Between ``:first-line`` and the following comma or curly brace a space is - inserted. (apparently it's needed for IE6) - - Same for ``:first-letter`` - -rcssmin.c is a reimplementation of rcssmin.py in C and improves runtime up to -factor 100 or so (depending on the input). docs/BENCHMARKS in the source -distribution contains the details. - -Both python 2 (>= 2.4) and python 3 are supported. - -.. _YUI compressor: https://github.com/yui/yuicompressor/ - -.. _the rule list by Isaac Schlueter: https://github.com/isaacs/cssmin/ -""" -if __doc__: - # pylint: disable = W0622 - __doc__ = __doc__.encode('ascii').decode('unicode_escape') -__author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape') -__docformat__ = "restructuredtext en" -__license__ = "Apache License, Version 2.0" -__version__ = '1.0.6' -__all__ = ['cssmin'] - -import re as _re - - -def _make_cssmin(python_only=False): - """ - Generate CSS minifier. - - :Parameters: - `python_only` : ``bool`` - Use only the python variant. If true, the c extension is not even - tried to be loaded. - - :Return: Minifier - :Rtype: ``callable`` - """ - # pylint: disable = R0912, R0914, W0612 - - if not python_only: - try: - import _rcssmin - except ImportError: - pass - else: - return _rcssmin.cssmin - - nl = r'(?:[\n\f]|\r\n?)' # pylint: disable = C0103 - spacechar = r'[\r\n\f\040\t]' - - unicoded = r'[0-9a-fA-F]{1,6}(?:[\040\n\t\f]|\r\n?)?' - escaped = r'[^\n\r\f0-9a-fA-F]' - escape = r'(?:\\(?:%(unicoded)s|%(escaped)s))' % locals() - - nmchar = r'[^\000-\054\056\057\072-\100\133-\136\140\173-\177]' - # nmstart = r'[^\000-\100\133-\136\140\173-\177]' - # ident = (r'(?:' - # r'-?(?:%(nmstart)s|%(escape)s)%(nmchar)s*(?:%(escape)s%(nmchar)s*)*' - # r')') % locals() - - comment = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)' - - # only for specific purposes. The bang is grouped: - _bang_comment = r'(?:/\*(!?)[^*]*\*+(?:[^/*][^*]*\*+)*/)' - - string1 = \ - r'(?:\047[^\047\\\r\n\f]*(?:\\[^\r\n\f][^\047\\\r\n\f]*)*\047)' - string2 = r'(?:"[^"\\\r\n\f]*(?:\\[^\r\n\f][^"\\\r\n\f]*)*")' - strings = r'(?:%s|%s)' % (string1, string2) - - nl_string1 = \ - r'(?:\047[^\047\\\r\n\f]*(?:\\(?:[^\r]|\r\n?)[^\047\\\r\n\f]*)*\047)' - nl_string2 = r'(?:"[^"\\\r\n\f]*(?:\\(?:[^\r]|\r\n?)[^"\\\r\n\f]*)*")' - nl_strings = r'(?:%s|%s)' % (nl_string1, nl_string2) - - uri_nl_string1 = r'(?:\047[^\047\\]*(?:\\(?:[^\r]|\r\n?)[^\047\\]*)*\047)' - uri_nl_string2 = r'(?:"[^"\\]*(?:\\(?:[^\r]|\r\n?)[^"\\]*)*")' - uri_nl_strings = r'(?:%s|%s)' % (uri_nl_string1, uri_nl_string2) - - nl_escaped = r'(?:\\%(nl)s)' % locals() - - space = r'(?:%(spacechar)s|%(comment)s)' % locals() - - ie7hack = r'(?:>/\*\*/)' - - uri = (r'(?:' - # noqa pylint: disable = C0330 - r'(?:[^\000-\040"\047()\\\177]*' - r'(?:%(escape)s[^\000-\040"\047()\\\177]*)*)' - r'(?:' - r'(?:%(spacechar)s+|%(nl_escaped)s+)' - r'(?:' - r'(?:[^\000-\040"\047()\\\177]|%(escape)s|%(nl_escaped)s)' - r'[^\000-\040"\047()\\\177]*' - r'(?:%(escape)s[^\000-\040"\047()\\\177]*)*' - r')+' - r')*' - r')') % locals() - - nl_unesc_sub = _re.compile(nl_escaped).sub - - uri_space_sub = _re.compile(( - r'(%(escape)s+)|%(spacechar)s+|%(nl_escaped)s+' - ) % locals()).sub - uri_space_subber = lambda m: m.groups()[0] or '' - - space_sub_simple = _re.compile(( - r'[\r\n\f\040\t;]+|(%(comment)s+)' - ) % locals()).sub - space_sub_banged = _re.compile(( - r'[\r\n\f\040\t;]+|(%(_bang_comment)s+)' - ) % locals()).sub - - post_esc_sub = _re.compile(r'[\r\n\f\t]+').sub - - main_sub = _re.compile(( - # noqa pylint: disable = C0330 - r'([^\\"\047u>@\r\n\f\040\t/;:{}+]+)' # 1 - r'|(?<=[{}(=:>[,!])(%(space)s+)' # 2 - r'|^(%(space)s+)' # 3 - r'|(%(space)s+)(?=(([:{});=>\],!])|$)?)' # 4, 5, 6 - r'|;(%(space)s*(?:;%(space)s*)*)(?=(\})?)' # 7, 8 - r'|(\{)' # 9 - r'|(\})' # 10 - r'|(%(strings)s)' # 11 - r'|(?<!%(nmchar)s)url\(%(spacechar)s*(' # 12 - r'%(uri_nl_strings)s' - r'|%(uri)s' - r')%(spacechar)s*\)' - r'|(@(?:' # 13 - r'[mM][eE][dD][iI][aA]' - r'|[sS][uU][pP][pP][oO][rR][tT][sS]' - r'|[dD][oO][cC][uU][mM][eE][nN][tT]' - r'|(?:-(?:' - r'[wW][eE][bB][kK][iI][tT]|[mM][oO][zZ]|[oO]|[mM][sS]' - r')-)?' - r'[kK][eE][yY][fF][rR][aA][mM][eE][sS]' - r'))(?!%(nmchar)s)' - r'|(%(ie7hack)s)(%(space)s*)' # 14, 15 - r'|(:[fF][iI][rR][sS][tT]-[lL]' # 16 - r'(?:[iI][nN][eE]|[eE][tT][tT][eE][rR]))' - r'(%(space)s*)(?=[{,])' # 17 - r'|(%(nl_strings)s)' # 18 - r'|(%(escape)s[^\\"\047u>@\r\n\f\040\t/;:{}+]*)' # 19 - ) % locals()).sub - - # print main_sub.__self__.pattern - - def main_subber(keep_bang_comments): - """ Make main subber """ - in_macie5, in_rule, at_group = [0], [0], [0] - - if keep_bang_comments: - space_sub = space_sub_banged - - def space_subber(match): - """ Space|Comment subber """ - if match.lastindex: - group1, group2 = match.group(1, 2) - if group2: - if group1.endswith(r'\*/'): - in_macie5[0] = 1 - else: - in_macie5[0] = 0 - return group1 - elif group1: - if group1.endswith(r'\*/'): - if in_macie5[0]: - return '' - in_macie5[0] = 1 - return r'/*\*/' - elif in_macie5[0]: - in_macie5[0] = 0 - return '/**/' - return '' - else: - space_sub = space_sub_simple - - def space_subber(match): - """ Space|Comment subber """ - if match.lastindex: - if match.group(1).endswith(r'\*/'): - if in_macie5[0]: - return '' - in_macie5[0] = 1 - return r'/*\*/' - elif in_macie5[0]: - in_macie5[0] = 0 - return '/**/' - return '' - - def fn_space_post(group): - """ space with token after """ - if group(5) is None or ( - group(6) == ':' and not in_rule[0] and not at_group[0]): - return ' ' + space_sub(space_subber, group(4)) - return space_sub(space_subber, group(4)) - - def fn_semicolon(group): - """ ; handler """ - return ';' + space_sub(space_subber, group(7)) - - def fn_semicolon2(group): - """ ; handler """ - if in_rule[0]: - return space_sub(space_subber, group(7)) - return ';' + space_sub(space_subber, group(7)) - - def fn_open(_): - """ { handler """ - if at_group[0]: - at_group[0] -= 1 - else: - in_rule[0] = 1 - return '{' - - def fn_close(_): - """ } handler """ - in_rule[0] = 0 - return '}' - - def fn_at_group(group): - """ @xxx group handler """ - at_group[0] += 1 - return group(13) - - def fn_ie7hack(group): - """ IE7 Hack handler """ - if not in_rule[0] and not at_group[0]: - in_macie5[0] = 0 - return group(14) + space_sub(space_subber, group(15)) - return '>' + space_sub(space_subber, group(15)) - - table = ( - # noqa pylint: disable = C0330 - None, - None, - None, - None, - fn_space_post, # space with token after - fn_space_post, # space with token after - fn_space_post, # space with token after - fn_semicolon, # semicolon - fn_semicolon2, # semicolon - fn_open, # { - fn_close, # } - lambda g: g(11), # string - lambda g: 'url(%s)' % uri_space_sub(uri_space_subber, g(12)), - # url(...) - fn_at_group, # @xxx expecting {...} - None, - fn_ie7hack, # ie7hack - None, - lambda g: g(16) + ' ' + space_sub(space_subber, g(17)), - # :first-line|letter followed - # by [{,] (apparently space - # needed for IE6) - lambda g: nl_unesc_sub('', g(18)), # nl_string - lambda g: post_esc_sub(' ', g(19)), # escape - ) - - def func(match): - """ Main subber """ - idx, group = match.lastindex, match.group - if idx > 3: - return table[idx](group) - - # shortcuts for frequent operations below: - elif idx == 1: # not interesting - return group(1) - # else: # space with token before or at the beginning - return space_sub(space_subber, group(idx)) - - return func - - def cssmin(style, keep_bang_comments=False): # pylint: disable = W0621 - """ - Minify CSS. - - :Parameters: - `style` : ``str`` - CSS to minify - - `keep_bang_comments` : ``bool`` - Keep comments starting with an exclamation mark? (``/*!...*/``) - - :Return: Minified style - :Rtype: ``str`` - """ - return main_sub(main_subber(keep_bang_comments), style) - - return cssmin - -cssmin = _make_cssmin() - - -if __name__ == '__main__': - def main(): - """ Main """ - import sys as _sys - keep_bang_comments = ( - '-b' in _sys.argv[1:] - or '-bp' in _sys.argv[1:] - or '-pb' in _sys.argv[1:] - ) - if '-p' in _sys.argv[1:] or '-bp' in _sys.argv[1:] \ - or '-pb' in _sys.argv[1:]: - global cssmin # pylint: disable = W0603 - cssmin = _make_cssmin(python_only=True) - _sys.stdout.write(cssmin( - _sys.stdin.read(), keep_bang_comments=keep_bang_comments - )) - main() diff --git a/compressor/filters/jsmin/__init__.py b/compressor/filters/jsmin/__init__.py index f8fbbdf..ceca6ac 100644 --- a/compressor/filters/jsmin/__init__.py +++ b/compressor/filters/jsmin/__init__.py @@ -4,7 +4,8 @@ from compressor.filters.jsmin.slimit import SlimItFilter # noqa class rJSMinFilter(CallbackOutputFilter): - callback = "compressor.filters.jsmin.rjsmin.jsmin" + callback = "rjsmin.jsmin" + dependencies = ["rjsmin"] kwargs = { "keep_bang_comments": True } diff --git a/compressor/filters/jsmin/rjsmin.py b/compressor/filters/jsmin/rjsmin.py deleted file mode 100755 index 4d24b46..0000000 --- a/compressor/filters/jsmin/rjsmin.py +++ /dev/null @@ -1,514 +0,0 @@ -#!/usr/bin/env python -# -*- coding: ascii -*- -r""" -===================== - Javascript Minifier -===================== - -rJSmin is a javascript minifier written in python. - -The minifier is based on the semantics of `jsmin.c by Douglas Crockford`_\\. - -:Copyright: - - Copyright 2011 - 2015 - Andr\xe9 Malo or his licensors, as applicable - -:License: - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -The module is a re-implementation aiming for speed, so it can be used at -runtime (rather than during a preprocessing step). Usually it produces the -same results as the original ``jsmin.c``. It differs in the following ways: - -- there is no error detection: unterminated string, regex and comment - literals are treated as regular javascript code and minified as such. -- Control characters inside string and regex literals are left untouched; they - are not converted to spaces (nor to \\n) -- Newline characters are not allowed inside string and regex literals, except - for line continuations in string literals (ECMA-5). -- "return /regex/" is recognized correctly. -- Line terminators after regex literals are handled more sensibly -- "+ +" and "- -" sequences are not collapsed to '++' or '--' -- Newlines before ! operators are removed more sensibly -- Comments starting with an exclamation mark (``!``) can be kept optionally -- rJSmin does not handle streams, but only complete strings. (However, the - module provides a "streamy" interface). - -Since most parts of the logic are handled by the regex engine it's way faster -than the original python port of ``jsmin.c`` by Baruch Even. The speed factor -varies between about 6 and 55 depending on input and python version (it gets -faster the more compressed the input already is). Compared to the -speed-refactored python port by Dave St.Germain the performance gain is less -dramatic but still between 3 and 50 (for huge inputs). See the docs/BENCHMARKS -file for details. - -rjsmin.c is a reimplementation of rjsmin.py in C and speeds it up even more. - -Both python 2 and python 3 are supported. - -.. _jsmin.c by Douglas Crockford: - http://www.crockford.com/javascript/jsmin.c -""" -if __doc__: - # pylint: disable = redefined-builtin - __doc__ = __doc__.encode('ascii').decode('unicode_escape') -__author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape') -__docformat__ = "restructuredtext en" -__license__ = "Apache License, Version 2.0" -__version__ = '1.0.12' -__all__ = ['jsmin'] - -import re as _re - - -def _make_jsmin(python_only=False): - """ - Generate JS minifier based on `jsmin.c by Douglas Crockford`_ - - .. _jsmin.c by Douglas Crockford: - http://www.crockford.com/javascript/jsmin.c - - :Parameters: - `python_only` : ``bool`` - Use only the python variant. If true, the c extension is not even - tried to be loaded. - - :Return: Minifier - :Rtype: ``callable`` - """ - # pylint: disable = unused-variable - # pylint: disable = too-many-locals - - if not python_only: - try: - import _rjsmin - except ImportError: - pass - else: - return _rjsmin.jsmin - try: - xrange - except NameError: - xrange = range # pylint: disable = redefined-builtin - - space_chars = r'[\000-\011\013\014\016-\040]' - - line_comment = r'(?://[^\r\n]*)' - space_comment = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)' - space_comment_nobang = r'(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/)' - bang_comment = r'(?:/\*![^*]*\*+(?:[^/*][^*]*\*+)*/)' - - string1 = \ - r'(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^\047\\\r\n]*)*\047)' - string2 = r'(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^"\\\r\n]*)*")' - strings = r'(?:%s|%s)' % (string1, string2) - - charclass = r'(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\])' - nospecial = r'[^/\\\[\r\n]' - regex = r'(?:/(?![\r\n/*])%s*(?:(?:\\[^\r\n]|%s)%s*)*/)' % ( - nospecial, charclass, nospecial - ) - space = r'(?:%s|%s)' % (space_chars, space_comment) - newline = r'(?:%s?[\r\n])' % line_comment - - def fix_charclass(result): - """ Fixup string of chars to fit into a regex char class """ - pos = result.find('-') - if pos >= 0: - result = r'%s%s-' % (result[:pos], result[pos + 1:]) - - def sequentize(string): - """ - Notate consecutive characters as sequence - - (1-4 instead of 1234) - """ - first, last, result = None, None, [] - for char in map(ord, string): - if last is None: - first = last = char - elif last + 1 == char: - last = char - else: - result.append((first, last)) - first = last = char - if last is not None: - result.append((first, last)) - return ''.join(['%s%s%s' % ( - chr(first), - last > first + 1 and '-' or '', - last != first and chr(last) or '' - ) for first, last in result]) # noqa - - return _re.sub( - r'([\000-\040\047])', # \047 for better portability - lambda m: '\\%03o' % ord(m.group(1)), ( - sequentize(result) - .replace('\\', '\\\\') - .replace('[', '\\[') - .replace(']', '\\]') - ) - ) - - def id_literal_(what): - """ Make id_literal like char class """ - match = _re.compile(what).match - result = ''.join([ - chr(c) for c in xrange(127) if not match(chr(c)) - ]) - return '[^%s]' % fix_charclass(result) - - def not_id_literal_(keep): - """ Make negated id_literal like char class """ - match = _re.compile(id_literal_(keep)).match - result = ''.join([ - chr(c) for c in xrange(127) if not match(chr(c)) - ]) - return r'[%s]' % fix_charclass(result) - - not_id_literal = not_id_literal_(r'[a-zA-Z0-9_$]') - preregex1 = r'[(,=:\[!&|?{};\r\n]' - preregex2 = r'%(not_id_literal)sreturn' % locals() - - id_literal = id_literal_(r'[a-zA-Z0-9_$]') - id_literal_open = id_literal_(r'[a-zA-Z0-9_${\[(!+-]') - id_literal_close = id_literal_(r'[a-zA-Z0-9_$}\])"\047+-]') - post_regex_off = id_literal_(r'[^\000-\040}\])?:|,;.&=+-]') - - dull = r'[^\047"/\000-\040]' - - space_sub_simple = _re.compile(( - # noqa pylint: disable = bad-continuation - - r'(%(dull)s+)' # 0 - r'|(%(strings)s%(dull)s*)' # 1 - r'|(?<=%(preregex1)s)' - r'%(space)s*(?:%(newline)s%(space)s*)*' - r'(%(regex)s)' # 2 - r'(%(space)s*(?:%(newline)s%(space)s*)+' # 3 - r'(?=%(post_regex_off)s))?' - r'|(?<=%(preregex2)s)' - r'%(space)s*(?:(%(newline)s)%(space)s*)*' # 4 - r'(%(regex)s)' # 5 - r'(%(space)s*(?:%(newline)s%(space)s*)+' # 6 - r'(?=%(post_regex_off)s))?' - r'|(?<=%(id_literal_close)s)' - r'%(space)s*(?:(%(newline)s)%(space)s*)+' # 7 - r'(?=%(id_literal_open)s)' - r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)' # 8 - r'|(?<=\+)(%(space)s)+(?=\+)' # 9 - r'|(?<=-)(%(space)s)+(?=-)' # 10 - r'|%(space)s+' - r'|(?:%(newline)s%(space)s*)+' - ) % locals()).sub - - # print space_sub_simple.__self__.pattern - - def space_subber_simple(match): - """ Substitution callback """ - # pylint: disable = too-many-return-statements - - groups = match.groups() - if groups[0]: - return groups[0] - elif groups[1]: - return groups[1] - elif groups[2]: - if groups[3]: - return groups[2] + '\n' - return groups[2] - elif groups[5]: - return "%s%s%s" % ( - groups[4] and '\n' or '', - groups[5], - groups[6] and '\n' or '', - ) - elif groups[7]: - return '\n' - elif groups[8] or groups[9] or groups[10]: - return ' ' - else: - return '' - - space_sub_banged = _re.compile(( - # noqa pylint: disable = bad-continuation - - r'(%(dull)s+)' # 0 - r'|(%(strings)s%(dull)s*)' # 1 - r'|(?<=%(preregex1)s)' - r'(%(space)s*(?:%(newline)s%(space)s*)*)' # 2 - r'(%(regex)s)' # 3 - r'(%(space)s*(?:%(newline)s%(space)s*)+' # 4 - r'(?=%(post_regex_off)s))?' - r'|(?<=%(preregex2)s)' - r'(%(space)s*(?:(%(newline)s)%(space)s*)*)' # 5, 6 - r'(%(regex)s)' # 7 - r'(%(space)s*(?:%(newline)s%(space)s*)+' # 8 - r'(?=%(post_regex_off)s))?' - r'|(?<=%(id_literal_close)s)' - r'(%(space)s*(?:%(newline)s%(space)s*)+)' # 9 - r'(?=%(id_literal_open)s)' - r'|(?<=%(id_literal)s)(%(space)s+)(?=%(id_literal)s)' # 10 - r'|(?<=\+)(%(space)s+)(?=\+)' # 11 - r'|(?<=-)(%(space)s+)(?=-)' # 12 - r'|(%(space)s+)' # 13 - r'|((?:%(newline)s%(space)s*)+)' # 14 - ) % locals()).sub - - # print space_sub_banged.__self__.pattern - - keep = _re.compile(( - r'%(space_chars)s+|%(space_comment_nobang)s+|%(newline)s+' - r'|(%(bang_comment)s+)' - ) % locals()).sub - keeper = lambda m: m.groups()[0] or '' - - # print keep.__self__.pattern - - def space_subber_banged(match): - """ Substitution callback """ - # pylint: disable = too-many-return-statements - - groups = match.groups() - if groups[0]: - return groups[0] - elif groups[1]: - return groups[1] - elif groups[3]: - return "%s%s%s%s" % ( - keep(keeper, groups[2]), - groups[3], - keep(keeper, groups[4] or ''), - groups[4] and '\n' or '', - ) - elif groups[7]: - return "%s%s%s%s%s" % ( - keep(keeper, groups[5]), - groups[6] and '\n' or '', - groups[7], - keep(keeper, groups[8] or ''), - groups[8] and '\n' or '', - ) - elif groups[9]: - return keep(keeper, groups[9]) + '\n' - elif groups[10] or groups[11] or groups[12]: - return keep(keeper, groups[10] or groups[11] or groups[12]) or ' ' - else: - return keep(keeper, groups[13] or groups[14]) - - def jsmin(script, keep_bang_comments=False): - r""" - Minify javascript based on `jsmin.c by Douglas Crockford`_\. - - Instead of parsing the stream char by char, it uses a regular - expression approach which minifies the whole script with one big - substitution regex. - - .. _jsmin.c by Douglas Crockford: - http://www.crockford.com/javascript/jsmin.c - - :Parameters: - `script` : ``str`` - Script to minify - - `keep_bang_comments` : ``bool`` - Keep comments starting with an exclamation mark? (``/*!...*/``) - - :Return: Minified script - :Rtype: ``str`` - """ - # pylint: disable = redefined-outer-name - - if keep_bang_comments: - return space_sub_banged( - space_subber_banged, '\n%s\n' % script - ).strip() - else: - return space_sub_simple( - space_subber_simple, '\n%s\n' % script - ).strip() - - return jsmin - -jsmin = _make_jsmin() - - -def jsmin_for_posers(script, keep_bang_comments=False): - r""" - Minify javascript based on `jsmin.c by Douglas Crockford`_\. - - Instead of parsing the stream char by char, it uses a regular - expression approach which minifies the whole script with one big - substitution regex. - - .. _jsmin.c by Douglas Crockford: - http://www.crockford.com/javascript/jsmin.c - - :Warning: This function is the digest of a _make_jsmin() call. It just - utilizes the resulting regexes. It's here for fun and may - vanish any time. Use the `jsmin` function instead. - - :Parameters: - `script` : ``str`` - Script to minify - - `keep_bang_comments` : ``bool`` - Keep comments starting with an exclamation mark? (``/*!...*/``) - - :Return: Minified script - :Rtype: ``str`` - """ - if not keep_bang_comments: - rex = ( - r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]' - r'|\r?\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]' - r'|\r?\n|\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?' - r'{};\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*' - r'][^*]*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\0' - r'14\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*((?:/(?![\r' - r'\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r' - r'\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/))((?:[\000-\011\013\014' - r'\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:(?:(?://[^\r' - r'\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:' - r'[^/*][^*]*\*+)*/))*)+(?=[^\000-\040&)+,.:;=?\]|}-]))?|(?<=[\00' - r'0-#%-,./:-@\[-^`{-~-]return)(?:[\000-\011\013\014\016-\040]|(?' - r':/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:((?:(?://[^\r\n]*)?[\r\n]' - r'))(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*' - r'\*+)*/))*)*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[' - r'[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/))((' - r'?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)' - r'*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\04' - r'0]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040&)+,.:;' - r'=?\]|}-]))?|(?<=[^\000-!#%&(*,./:-@\[\\^`{|~])(?:[\000-\011\01' - r'3\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:((?:(?:' - r'//[^\r\n]*)?[\r\n]))(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]' - r'*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040"#%-\047)*,./:-@\\-^' - r'`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-\011\013\014\0' - r'16-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=[^\000-#%-,./' - r':-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]|(?:/\*[' - r'^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=\+)|(?<=-)((?:[\000-\011\013' - r'\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=-)|(?:[' - r'\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)' - r')+|(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]' - r'|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+' - ) - - def subber(match): - """ Substitution callback """ - groups = match.groups() - return ( - groups[0] or - groups[1] or - (groups[3] and (groups[2] + '\n')) or - groups[2] or - (groups[5] and "%s%s%s" % ( - groups[4] and '\n' or '', - groups[5], - groups[6] and '\n' or '', - )) or - (groups[7] and '\n') or - (groups[8] and ' ') or - (groups[9] and ' ') or - (groups[10] and ' ') or - '' - ) - else: - rex = ( - r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]' - r'|\r?\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]' - r'|\r?\n|\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?' - r'{};\r\n])((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/' - r'*][^*]*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013' - r'\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*)((?:/(?!' - r'[\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^' - r'\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/))((?:[\000-\011\013\01' - r'4\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:(?:(?://[^' - r'\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(' - r'?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040&)+,.:;=?\]|}-]))?|(?<=[' - r'\000-#%-,./:-@\[-^`{-~-]return)((?:[\000-\011\013\014\016-\040' - r']|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:((?:(?://[^\r\n]*)?[' - r'\r\n]))(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][' - r'^*]*\*+)*/))*)*)((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|' - r'(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*' - r'/))((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]' - r'*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\01' - r'6-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040&)' - r'+,.:;=?\]|}-]))?|(?<=[^\000-!#%&(*,./:-@\[\\^`{|~])((?:[\000-' - r'\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:' - r'(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/' - r'\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+)(?=[^\000-\040"#%-\047)*,./' - r':-@\\-^`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-\011\01' - r'3\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+)(?=[^\000' - r'-#%-,./:-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]|' - r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+)(?=\+)|(?<=-)((?:[\000-\0' - r'11\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+)(?=-' - r')|((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*' - r'\*+)*/))+)|((?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014' - r'\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+)' - ) - - keep = _re.compile(( - r'[\000-\011\013\014\016-\040]+|(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*' - r'\*+)*/)+|(?:(?://[^\r\n]*)?[\r\n])+|((?:/\*![^*]*\*+(?:[^/*][^' - r'*]*\*+)*/)+)' - ) % locals()).sub - keeper = lambda m: m.groups()[0] or '' - - def subber(match): - """ Substitution callback """ - groups = match.groups() - return ( - groups[0] or - groups[1] or - (groups[3] and "%s%s%s%s" % ( - keep(keeper, groups[2]), - groups[3], - keep(keeper, groups[4] or ''), - groups[4] and '\n' or '', - )) or - (groups[7] and "%s%s%s%s%s" % ( - keep(keeper, groups[5]), - groups[6] and '\n' or '', - groups[7], - keep(keeper, groups[8] or ''), - groups[8] and '\n' or '', - )) or - (groups[9] and keep(keeper, groups[9] + '\n')) or - (groups[10] and keep(keeper, groups[10]) or ' ') or - (groups[11] and keep(keeper, groups[11]) or ' ') or - (groups[12] and keep(keeper, groups[12]) or ' ') or - keep(keeper, groups[13] or groups[14]) - ) - - return _re.sub(rex, subber, '\n%s\n' % script).strip() - - -if __name__ == '__main__': - def main(): - """ Main """ - import sys as _sys - - argv = _sys.argv[1:] - keep_bang_comments = '-b' in argv or '-bp' in argv or '-pb' in argv - if '-p' in argv or '-bp' in argv or '-pb' in argv: - xjsmin = _make_jsmin(python_only=True) - else: - xjsmin = jsmin - - _sys.stdout.write(xjsmin( - _sys.stdin.read(), keep_bang_comments=keep_bang_comments - )) - - main() diff --git a/docs/settings.txt b/docs/settings.txt index 34995d5..b175bff 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -130,7 +130,7 @@ Backend settings - ``compressor.filters.cssmin.rCSSMinFilter`` A filter that uses the cssmin implementation rCSSmin_ to compress CSS - (included, no external dependencies). + (installed by default). - ``compressor.filters.cleancss.CleanCSSFilter`` @@ -173,7 +173,7 @@ Backend settings - ``compressor.filters.jsmin.JSMinFilter`` A filter that uses the jsmin implementation rJSmin_ to compress - JavaScript code (included, no external dependencies). + JavaScript code (installed by default). .. _slimit_filter: diff --git a/requirements/tests.txt b/requirements/tests.txt index 3d42158..1c6a3ff 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -11,3 +11,5 @@ jingo==0.7 django-sekizai==0.8.2 django-overextends==0.4.0 csscompressor==0.9.4 +rcssmin==1.0.6 +rjsmin==1.0.12 @@ -142,5 +142,7 @@ setup( zip_safe=False, install_requires=[ 'django-appconf >= 0.4', + 'rcssmin == 1.0.6', + 'rjsmin == 1.0.12', ], ) @@ -13,6 +13,8 @@ two = django-sekizai==0.8.2 django-overextends==0.4.0 csscompressor==0.9.4 + rcssmin==1.0.6 + rjsmin==1.0.12 two_six = flake8==2.4.0 coverage==3.7.1 @@ -27,6 +29,8 @@ two_six = django-sekizai==0.8.2 django-overextends==0.4.0 csscompressor==0.9.4 + rcssmin==1.0.6 + rjsmin==1.0.12 three = flake8==2.4.0 coverage==3.7.1 @@ -40,6 +44,8 @@ three = django-sekizai==0.8.2 django-overextends==0.4.0 csscompressor==0.9.4 + rcssmin==1.0.6 + rjsmin==1.0.12 three_two = flake8==2.4.0 coverage==3.7.1 @@ -53,6 +59,8 @@ three_two = django-sekizai==0.8.2 django-overextends==0.4.0 csscompressor==0.9.4 + rcssmin==1.0.6 + rjsmin==1.0.12 [tox] envlist = {py26,py27}-1.4.X, |