1::
2
3 _____ _ _____ __ __ _____ _______
4 / ____| | |_ _| \/ |_ _|__ __|
5 | (___ | | | | | \ / | | | | |
6 \___ \| | | | | |\/| | | | | |
7 ____) | |____ _| |_| | | |_| |_ | |
8 |_____/|______|_____|_| |_|_____| |_|
9
10
11Welcome to SlimIt
12==================================
13
14`SlimIt` is a JavaScript minifier written in Python.
15It compiles JavaScript into more compact code so that it downloads
16and runs faster.
17
18`SlimIt` also provides a library that includes a JavaScript parser,
19lexer, pretty printer and a tree visitor.
20
21`http://slimit.readthedocs.org/ <http://slimit.readthedocs.org/>`_
22
23Installation
24------------
25
26::
27
28 $ [sudo] pip install slimit
29
30Or the bleeding edge version from the git master branch:
31
32::
33
34 $ [sudo] pip install git+https://github.com/rspivak/slimit.git#egg=slimit
35
36
37There is also an official DEB package available at
38`http://packages.debian.org/sid/slimit <http://packages.debian.org/sid/slimit>`_
39
40
41Let's minify some code
42----------------------
43
44From the command line:
45
46::
47
48 $ slimit -h
49 Usage: slimit [options] [input file]
50
51 If no input file is provided STDIN is used by default.
52 Minified JavaScript code is printed to STDOUT.
53
54 Options:
55 -h, --help show this help message and exit
56 -m, --mangle mangle names
57 -t, --mangle-toplevel
58 mangle top level scope (defaults to False)
59
60 $ cat test.js
61 var foo = function( obj ) {
62 for ( var name in obj ) {
63 return false;
64 }
65 return true;
66 };
67 $
68 $ slimit --mangle < test.js
69 var foo=function(a){for(var b in a)return false;return true;};
70
71Or using library API:
72
73>>> from slimit import minify
74>>> text = """
75... var foo = function( obj ) {
76... for ( var name in obj ) {
77... return false;
78... }
79... return true;
80... };
81... """
82>>> print minify(text, mangle=True, mangle_toplevel=True)
83var a=function(a){for(var b in a)return false;return true;};
84
85
86Iterate over, modify a JavaScript AST and pretty print it
87---------------------------------------------------------
88
89>>> from slimit.parser import Parser
90>>> from slimit.visitors import nodevisitor
91>>> from slimit import ast
92>>>
93>>> parser = Parser()
94>>> tree = parser.parse('for(var i=0; i<10; i++) {var x=5+i;}')
95>>> for node in nodevisitor.visit(tree):
96... if isinstance(node, ast.Identifier) and node.value == 'i':
97... node.value = 'hello'
98...
99>>> print tree.to_ecma() # print awesome javascript :)
100for (var hello = 0; hello < 10; hello++) {
101 var x = 5 + hello;
102}
103>>>
104
105Writing custom node visitor
106---------------------------
107
108>>> from slimit.parser import Parser
109>>> from slimit.visitors.nodevisitor import ASTVisitor
110>>>
111>>> text = """
112... var x = {
113... "key1": "value1",
114... "key2": "value2"
115... };
116... """
117>>>
118>>> class MyVisitor(ASTVisitor):
119... def visit_Object(self, node):
120... """Visit object literal."""
121... for prop in node:
122... left, right = prop.left, prop.right
123... print 'Property key=%s, value=%s' % (left.value, right.value)
124... # visit all children in turn
125... self.visit(prop)
126...
127>>>
128>>> parser = Parser()
129>>> tree = parser.parse(text)
130>>> visitor = MyVisitor()
131>>> visitor.visit(tree)
132Property key="key1", value="value1"
133Property key="key2", value="value2"
134
135Using lexer in your project
136---------------------------
137
138>>> from slimit.lexer import Lexer
139>>> lexer = Lexer()
140>>> lexer.input('a = 1;')
141>>> for token in lexer:
142... print token
143...
144LexToken(ID,'a',1,0)
145LexToken(EQ,'=',1,2)
146LexToken(NUMBER,'1',1,4)
147LexToken(SEMI,';',1,5)
148
149You can get one token at a time using ``token`` method:
150
151>>> lexer.input('a = 1;')
152>>> while True:
153... token = lexer.token()
154... if not token:
155... break
156... print token
157...
158LexToken(ID,'a',1,0)
159LexToken(EQ,'=',1,2)
160LexToken(NUMBER,'1',1,4)
161LexToken(SEMI,';',1,5)
162
163`LexToken` instance has different attributes:
164
165>>> lexer.input('a = 1;')
166>>> token = lexer.token()
167>>> token.type, token.value, token.lineno, token.lexpos
168('ID', 'a', 1, 0)
169
170Benchmarks
171----------
172
173**SAM** - JQuery size after minification in bytes (the smaller number the better)
174
175+-------------------------------+------------+------------+------------+
176| Original jQuery 1.6.1 (bytes) | SlimIt SAM | rJSmin SAM | jsmin SAM |
177+===============================+============+============+============+
178| 234,995 | 94,290 | 134,215 | 134,819 |
179+-------------------------------+------------+------------+------------+
180
181Roadmap
182-------
183- when doing name mangling handle cases with 'eval' and 'with'
184- foo["bar"] ==> foo.bar
185- consecutive declarations: var a = 10; var b = 20; ==> var a=10,b=20;
186- reduce simple constant expressions if the result takes less space:
187 1 +2 * 3 ==> 7
188- IF statement optimizations
189
190 1. if (foo) bar(); else baz(); ==> foo?bar():baz();
191 2. if (!foo) bar(); else baz(); ==> foo?baz():bar();
192 3. if (foo) bar(); ==> foo&&bar();
193 4. if (!foo) bar(); ==> foo||bar();
194 5. if (foo) return bar(); else return baz(); ==> return foo?bar():baz();
195 6. if (foo) return bar(); else something(); ==> {if(foo)return bar();something()}
196
197- remove unreachable code that follows a return, throw, break or
198 continue statement, except function/variable declarations
199- parsing speed improvements
200
201Acknowledgments
202---------------
203- The lexer and parser are built with `PLY <http://www.dabeaz.com/ply/>`_
204- Several test cases and regexes from `jslex <https://bitbucket.org/ned/jslex>`_
205- Some visitor ideas - `pycparser <http://code.google.com/p/pycparser/>`_
206- Many grammar rules are taken from `rkelly <https://github.com/tenderlove/rkelly>`_
207- Name mangling and different optimization ideas - `UglifyJS <https://github.com/mishoo/UglifyJS>`_
208- ASI implementation was inspired by `pyjsparser <http://bitbucket.org/mvantellingen/pyjsparser>`_
209
210License
211-------
212The MIT License (MIT)