3 # Copyright (C) 2010 Google Inc. All rights reserved.
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following disclaimer
13 # in the documentation and/or other materials provided with the
15 # * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 # This is a Python port of Douglas Crockford's jsmin.cc. See original
32 # copyright notice below.
34 # Copyright (c) 2002 Douglas Crockford (www.crockford.com)
36 # Permission is hereby granted, free of charge, to any person obtaining a copy of
37 # this software and associated documentation files (the "Software"), to deal in
38 # the Software without restriction, including without limitation the rights to
39 # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
40 # of the Software, and to permit persons to whom the Software is furnished to do
41 # so, subject to the following conditions:
43 # The above copyright notice and this permission notice shall be included in all
44 # copies or substantial portions of the Software.
46 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
47 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
48 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
49 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
50 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
51 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
55 from cStringIO import StringIO
59 class UnterminatedComment(Exception):
63 class UnterminatedStringLiteral(Exception):
67 class UnterminatedRegularExpression(Exception):
75 minifier = JavaScriptMinifier()
76 minifier.input = StringIO(text)
77 minifier.output = StringIO()
79 return minifier.output.getvalue()
82 class JavaScriptMinifier(object):
84 def isAlphanum(self, c):
85 """ return true if the character is a letter, digit, underscore,
86 dollar sign, or non-ASCII character.
88 return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or
89 (c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or
93 """ return the next character from stdin. Watch out for lookahead. If
94 the character is a control character, translate it to a space or
98 self.theLookahead = EOF
100 c = self.input.read(1)
101 if c >= ' ' or c == '\n' or c == EOF:
108 """ get the next character without getting it. """
109 self.theLookahead = self.get()
110 return self.theLookahead
113 """ get the next character, excluding comments. peek() is used to see
114 if a '/' is followed by a '/' or '*'.
129 if self.peek() == '/':
133 raise UnterminatedComment()
142 """ do something! What you do is determined by the argument:
143 1 Output A. Copy B to A. Get the next B.
144 2 Copy B to A. Get the next B. (Delete A).
145 3 Get the next B. (Delete B).
146 action treats a string as a single character. Wow!
147 action recognizes a regular expression if it is preceded by ( or , or =.
152 self.theA = self.theB
153 if self.theA == '\'' or self.theA == '"':
156 self.theA = self.get()
157 if self.theA == self.theB:
159 if self.theA == '\\':
161 self.theA = self.get()
163 raise UnterminatedString()
165 self.theB = self.next()
166 if self.theB == '/' and self.theA in ['(', ',', '=', ':', '[', '!', '&', '|', '?', '{', '}', ';', '\n']:
170 self.theA = self.get()
173 if self.theA == '\\':
175 self.theA = self.get()
177 raise UnterminatedRegularExpression()
179 self.theB = self.next()
182 """ Copy the input to the output, deleting the characters which are
183 insignificant to JavaScript. Comments will be removed. Tabs will be
184 replaced with spaces. Carriage returns will be replaced with linefeeds.
185 Most spaces and linefeeds will be removed.
188 self.theLookahead = EOF
190 while self.theA != EOF:
192 if self.isAlphanum(self.theB):
196 elif self.theA == '\n':
197 if self.theB in ['{', '[', '(', '+', '-']:
199 elif self.theB == ' ':
202 if self.isAlphanum(self.theB):
208 if self.isAlphanum(self.theA):
212 elif self.theB == '\n':
213 if self.theA in ['}', ']', ')', '+', '-', '"', '\'']:
216 if self.isAlphanum(self.theA):
224 if __name__ == '__main__':
225 minifier = JavaScriptMinifier()
226 minifier.input = sys.stdin
227 minifier.output = sys.stdout