]> git.saurik.com Git - apple/javascriptcore.git/blob - inspector/scripts/jsmin.py
JavaScriptCore-7600.1.4.17.5.tar.gz
[apple/javascriptcore.git] / inspector / scripts / jsmin.py
1 # This code is original from jsmin by Douglas Crockford, it was translated to
2 # Python by Baruch Even. It was rewritten by Dave St.Germain for speed.
3 #
4 # The MIT License (MIT)
5 #
6 # Copyright (c) 2013 Dave St.Germain
7 #
8 # Permission is hereby granted, free of charge, to any person obtaining a copy
9 # of this software and associated documentation files (the "Software"), to deal
10 # in the Software without restriction, including without limitation the rights
11 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 # copies of the Software, and to permit persons to whom the Software is
13 # furnished to do so, subject to the following conditions:
14 #
15 # The above copyright notice and this permission notice shall be included in
16 # all copies or substantial portions of the Software.
17 #
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 # THE SOFTWARE.
25
26
27 import sys
28 is_3 = sys.version_info >= (3, 0)
29 if is_3:
30 import io
31 else:
32 import StringIO
33 try:
34 import cStringIO
35 except ImportError:
36 cStringIO = None
37
38
39 __all__ = ['jsmin', 'JavascriptMinify']
40 __version__ = '2.0.9'
41
42
43 def jsmin(js):
44 """
45 returns a minified version of the javascript string
46 """
47 if not is_3:
48 if cStringIO and not isinstance(js, unicode):
49 # strings can use cStringIO for a 3x performance
50 # improvement, but unicode (in python2) cannot
51 klass = cStringIO.StringIO
52 else:
53 klass = StringIO.StringIO
54 else:
55 klass = io.StringIO
56 ins = klass(js)
57 outs = klass()
58 JavascriptMinify(ins, outs).minify()
59 return outs.getvalue()
60
61
62 class JavascriptMinify(object):
63 """
64 Minify an input stream of javascript, writing
65 to an output stream
66 """
67
68 def __init__(self, instream=None, outstream=None):
69 self.ins = instream
70 self.outs = outstream
71
72 def minify(self, instream=None, outstream=None):
73 if instream and outstream:
74 self.ins, self.outs = instream, outstream
75
76 self.is_return = False
77 self.return_buf = ''
78
79 def write(char):
80 # all of this is to support literal regular expressions.
81 # sigh
82 if char in 'return':
83 self.return_buf += char
84 self.is_return = self.return_buf == 'return'
85 self.outs.write(char)
86 if self.is_return:
87 self.return_buf = ''
88
89 read = self.ins.read
90
91 space_strings = "abcdefghijklmnopqrstuvwxyz"\
92 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$\\"
93 starters, enders = '{[(+-', '}])+-"\''
94 newlinestart_strings = starters + space_strings
95 newlineend_strings = enders + space_strings
96 do_newline = False
97 do_space = False
98 escape_slash_count = 0
99 doing_single_comment = False
100 previous_before_comment = ''
101 doing_multi_comment = False
102 in_re = False
103 in_quote = ''
104 quote_buf = []
105
106 previous = read(1)
107 if previous == '\\':
108 escape_slash_count += 1
109 next1 = read(1)
110 if previous == '/':
111 if next1 == '/':
112 doing_single_comment = True
113 elif next1 == '*':
114 doing_multi_comment = True
115 previous = next1
116 next1 = read(1)
117 else:
118 write(previous)
119 elif not previous:
120 return
121 elif previous >= '!':
122 if previous in "'\"":
123 in_quote = previous
124 write(previous)
125 previous_non_space = previous
126 else:
127 previous_non_space = ' '
128 if not next1:
129 return
130
131 while 1:
132 next2 = read(1)
133 if not next2:
134 last = next1.strip()
135 if not (doing_single_comment or doing_multi_comment)\
136 and last not in ('', '/'):
137 if in_quote:
138 write(''.join(quote_buf))
139 write(last)
140 break
141 if doing_multi_comment:
142 if next1 == '*' and next2 == '/':
143 doing_multi_comment = False
144 next2 = read(1)
145 elif doing_single_comment:
146 if next1 in '\r\n':
147 doing_single_comment = False
148 while next2 in '\r\n':
149 next2 = read(1)
150 if not next2:
151 break
152 if previous_before_comment in ')}]':
153 do_newline = True
154 elif previous_before_comment in space_strings:
155 write('\n')
156 elif in_quote:
157 quote_buf.append(next1)
158
159 if next1 == in_quote:
160 numslashes = 0
161 for c in reversed(quote_buf[:-1]):
162 if c != '\\':
163 break
164 else:
165 numslashes += 1
166 if numslashes % 2 == 0:
167 in_quote = ''
168 write(''.join(quote_buf))
169 elif next1 in '\r\n':
170 if previous_non_space in newlineend_strings \
171 or previous_non_space > '~':
172 while 1:
173 if next2 < '!':
174 next2 = read(1)
175 if not next2:
176 break
177 else:
178 if next2 in newlinestart_strings \
179 or next2 > '~' or next2 == '/':
180 do_newline = True
181 break
182 elif next1 < '!' and not in_re:
183 if (previous_non_space in space_strings \
184 or previous_non_space > '~') \
185 and (next2 in space_strings or next2 > '~'):
186 do_space = True
187 elif previous_non_space in '-+' and next2 == previous_non_space:
188 # protect against + ++ or - -- sequences
189 do_space = True
190 elif self.is_return and next2 == '/':
191 # returning a regex...
192 write(' ')
193 elif next1 == '/':
194 if do_space:
195 write(' ')
196 if in_re:
197 if previous != '\\' or (not escape_slash_count % 2) or next2 in 'gimy':
198 in_re = False
199 write('/')
200 elif next2 == '/':
201 doing_single_comment = True
202 previous_before_comment = previous_non_space
203 elif next2 == '*':
204 doing_multi_comment = True
205 previous = next1
206 next1 = next2
207 next2 = read(1)
208 else:
209 in_re = previous_non_space in '(,=:[?!&|' or self.is_return # literal regular expression
210 write('/')
211 else:
212 if do_space:
213 do_space = False
214 write(' ')
215 if do_newline:
216 write('\n')
217 do_newline = False
218
219 write(next1)
220 if not in_re and next1 in "'\"":
221 in_quote = next1
222 quote_buf = []
223
224 previous = next1
225 next1 = next2
226
227 if previous >= '!':
228 previous_non_space = previous
229
230 if previous == '\\':
231 escape_slash_count += 1
232 else:
233 escape_slash_count = 0
234
235 if __name__ == '__main__':
236 minifier = JavascriptMinify(sys.stdin, sys.stdout)
237 minifier.minify()
238 sys.stdout.write('\n')