Simone Marzola avatar Simone Marzola committed 96c603a Draft

more efficient js minifier, js obfuscation

Comments (0)

Files changed (4)

 About tgext.minify
 -------------------------
 
-tgext.minify is a Turbogears 2 middleware that minifies all css and javascript
-files in your public directory before serving them.
-Based on rCSSmin and rJSmin by André Malo.
+tgext.minify is a Turbogears 2 middleware that automatically minifies your javascript
+and CSS and cache it. It's able to obfuscate your js too.
+CSS minifier based on rCSSmin by André Malo.
+JS minifier/obfuscator based on `SlimIt <https://github.com/rspivak/slimit>`_ by Ruslan Spivak
 The middleware is based on `tgext.scss <https://bitbucket.org/_amol_/tgext.scss>`_ by Alessandro Molina
 
 Installing
 
 Now you just have to put your beautiful .css and .js files inside *public/* and they will be served as minified.
 
+JS obfuscation
+---------------
+
+If you need to obfuscate your javascript, just add `mangle_js = true` to your `development.ini`.
+If you want to obfuscate also the global scope, add `mangle_js_toplevel = true`. Keep in mind that
+if you obfuscate your js, you have to join js files that depends on each other, or the namespace will
+be messed up.
+
 Performance boost
 -----------------------------------
 
 from setuptools import setup, find_packages
 import sys, os
 
-version = '0.0.1'
+version = '0.0.2'
 
 here = os.path.abspath(os.path.dirname(__file__))
 try:
 
 setup(name='tgext.minify',
       version=version,
-      description="CSS and JS minifier for TurboGears2",
+      description="CSS and JS minifier / obfuscator for TurboGears 2",
       long_description=README,
       classifiers=[
         "Environment :: Web Environment",
         "Topic :: Software Development :: Libraries :: Python Modules",
         "Framework :: TurboGears"
         ],
-      keywords='turbogears2.extension CSS JS minify WSGI',
+      keywords='turbogears2.extension CSS JS minify obfuscate mangle WSGI',
       author='Simone Marzola',
       author_email='simone.marzola@axant.it',
       url='http://bitbucket.org/simock85/tgext.minify',
       zip_safe=False,
       install_requires=[
         "TurboGears2 >= 2.0b7",
+        "slimit"
       ],
       entry_points="""
       # -*- Entry points: -*-

tgext/minify/middleware.py

 from webob import Response
 from webob.exc import status_map
 from rcssmin import cssmin
-from rjsmin import jsmin
+from slimit import minify
 from tg import config
 
 class MinifyMiddleware(object):
     def __init__(self, app):
+        self.mangle = config.get('mangle_js', False)
+        self.mangle_toplevel = config.get('mangle_js_toplevel', False)
         self.app = app
         self.cache = {}
         self.convert = {'js': self._convert_js,
             self.root_directory = os.path.normcase(os.path.abspath((config['pylons.paths']['static_files'])))            
 
     def _convert_js(self, text):
-        return jsmin(text)
+        return minify(text, mangle=self.mangle, mangle_toplevel=self.mangle_toplevel)
 
     def _convert_css(self, text):
         return cssmin(text)

tgext/minify/rjsmin.py

-#!/usr/bin/env python
-# -*- coding: ascii -*-
-#
-# Copyright 2011, 2012
-# Andr\xe9 Malo or his licensors, as applicable
-#
-# 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.
-r"""
-=====================
- Javascript Minifier
-=====================
-
-rJSmin is a javascript minifier written in python.
-
-The minifier is based on the semantics of `jsmin.c by Douglas Crockford`_\.
-
-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.
-- "+ ++" and "- --" sequences are not collapsed to '+++' or '---'
-- 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 1.2 and 7. 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
-"""
-__author__ = "Andr\xe9 Malo"
-__author__ = getattr(__author__, 'decode', lambda x: __author__)('latin-1')
-__docformat__ = "restructuredtext en"
-__license__ = "Apache License, Version 2.0"
-__version__ = '1.0.3'
-__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 = R0912, R0914, W0612
-    if not python_only:
-        try:
-            import _rjsmin
-        except ImportError:
-            pass
-        else:
-            return _rjsmin.jsmin
-    try:
-        xrange
-    except NameError:
-        xrange = range # pylint: disable = W0622
-
-    space_chars = r'[\000-\011\013\014\016-\040]'
-
-    line_comment = r'(?://[^\r\n]*)'
-    space_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])
-
-        return _re.sub(r'([\000-\040\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+-]')
-
-    space_sub = _re.compile((
-        r'([^\047"/\000-\040]+)'
-        r'|(%(strings)s[^\047"/\000-\040]*)'
-        r'|(?:(?<=%(preregex1)s)%(space)s*(%(regex)s[^\047"/\000-\040]*))'
-        r'|(?:(?<=%(preregex2)s)%(space)s*(%(regex)s[^\047"/\000-\040]*))'
-        r'|(?<=%(id_literal_close)s)'
-            r'%(space)s*(?:(%(newline)s)%(space)s*)+'
-            r'(?=%(id_literal_open)s)'
-        r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)'
-        r'|(?<=\+)(%(space)s)+(?=\+\+)'
-        r'|(?<=-)(%(space)s)+(?=--)'
-        r'|%(space)s+'
-        r'|(?:%(newline)s%(space)s*)+'
-    ) % locals()).sub
-    #print space_sub.__self__.pattern
-
-    def space_subber(match):
-        """ Substitution callback """
-        # pylint: disable = C0321, R0911
-        groups = match.groups()
-        if groups[0]: return groups[0]
-        elif groups[1]: return groups[1]
-        elif groups[2]: return groups[2]
-        elif groups[3]: return groups[3]
-        elif groups[4]: return '\n'
-        elif groups[5] or groups[6] or groups[7]: return ' '
-        else: return ''
-
-    def jsmin(script): # pylint: disable = W0621
-        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
-
-        :Return: Minified script
-        :Rtype: ``str``
-        """
-        return space_sub(space_subber, '\n%s\n' % script).strip()
-
-    return jsmin
-
-jsmin = _make_jsmin()
-
-
-def jsmin_for_posers(script):
-    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 regex. It's just for fun here and may
-              vanish any time. Use the `jsmin` function instead.
-
-    :Parameters:
-      `script` : ``str``
-        Script to minify
-
-    :Return: Minified script
-    :Rtype: ``str``
-    """
-    def subber(match):
-        """ Substitution callback """
-        groups = match.groups()
-        return (
-            groups[0] or
-            groups[1] or
-            groups[2] or
-            groups[3] or
-            (groups[4] and '\n') or
-            (groups[5] and ' ') or
-            (groups[6] and ' ') or
-            (groups[7] and ' ') or
-            ''
-        )
-
-    return _re.sub(
-        r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?'
-        r'\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|'
-        r'\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?:(?<=[(,=:\[!&|?{};\r\n]'
-        r')(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/'
-        r'))*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*'
-        r'(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/)[^\047"/\000-\040]*'
-        r'))|(?:(?<=[\000-#%-,./:-@\[-^`{-~-]return)(?:[\000-\011\013\014\01'
-        r'6-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*((?:/(?![\r\n/*])[^/'
-        r'\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]'
-        r'*)*\]))[^/\\\[\r\n]*)*/)[^\047"/\000-\040]*))|(?<=[^\000-!#%&(*,./'
-        r':-@\[\\^`{|~])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/'
-        r'*][^*]*\*+)*/))*(?:((?:(?://[^\r\n]*)?[\r\n]))(?:[\000-\011\013\01'
-        r'4\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-#%-\04'
-        r'7)*,./:-@\\-^`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-\011'
-        r'\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=[^\000-'
-        r'#%-,./:-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]|(?:/'
-        r'\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=\+\+)|(?<=-)((?:[\000-\011\013'
-        r'\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=--)|(?:[\00'
-        r'0-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+|(?:('
-        r'?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]'
-        r'*\*+(?:[^/*][^*]*\*+)*/))*)+', subber, '\n%s\n' % script
-    ).strip()
-
-
-if __name__ == '__main__':
-    import sys as _sys
-    _sys.stdout.write(jsmin(_sys.stdin.read()))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.