1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
# -*- coding: utf-8 -*-
"""JavaScript Minifier functions for CSS-JS-Minify."""
import re
from io import StringIO # pure-Python StringIO supports unicode.
from .css_minifer import condense_semicolons
__all__ = ('js_minify', )
def remove_commented_lines(js):
"""Force remove commented out lines from Javascript."""
result = ""
for line in js.splitlines():
line = re.sub(r"/\*.*\*/", "", line) # (/*COMMENT */)
line = re.sub(r"//.*", "", line) # (//COMMENT)
result += '\n'+line
return result
def simple_replacer_js(js):
"""Force strip simple replacements from Javascript."""
return condense_semicolons(js.replace("debugger;", ";").replace(
";}", "}").replace("; ", ";").replace(" ;", ";").rstrip("\n;"))
def js_minify_keep_comments(js):
"""Return a minified version of the Javascript string."""
ins, outs = StringIO(js), StringIO()
JavascriptMinify(ins, outs).minify()
return force_single_line_js(outs.getvalue())
def force_single_line_js(js):
"""Force Javascript to a single line, even if need to add semicolon."""
return ";".join(js.splitlines()) if len(js.splitlines()) > 1 else js
class JavascriptMinify(object):
"""Minify an input stream of Javascript, writing to an output stream."""
def __init__(self, instream=None, outstream=None):
"""Init class."""
self.ins, self.outs = instream, outstream
def minify(self, instream=None, outstream=None):
"""Minify Javascript using StringIO."""
if instream and outstream:
self.ins, self.outs = instream, outstream
write, read = self.outs.write, self.ins.read
space_strings = ("abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$\\")
starters, enders = '{[(+-', '}])+-"\''
newlinestart_strings = starters + space_strings
newlineend_strings = enders + space_strings
do_newline, do_space = False, False
doing_single_comment, doing_multi_comment = False, False
previous_before_comment, in_quote = '', ''
in_re, quote_buf = False, []
previous = read(1)
next1 = read(1)
if previous == '/':
if next1 == '/':
doing_single_comment = True
elif next1 == '*':
doing_multi_comment = True
else:
write(previous)
elif not previous:
return
elif previous >= '!':
if previous in "'\"":
in_quote = previous
write(previous)
previous_non_space = previous
else:
previous_non_space = ' '
if not next1:
return
while True:
next2 = read(1)
if not next2:
last = next1.strip()
conditional_1 = (doing_single_comment or doing_multi_comment)
if not conditional_1 and last not in ('', '/'):
write(last)
break
if doing_multi_comment:
if next1 == '*' and next2 == '/':
doing_multi_comment = False
next2 = read(1)
elif doing_single_comment:
if next1 in '\r\n':
doing_single_comment = False
while next2 in '\r\n':
next2 = read(1)
if not next2:
break
if previous_before_comment in ')}]':
do_newline = True
elif previous_before_comment in space_strings:
write('\n')
elif in_quote:
quote_buf.append(next1)
if next1 == in_quote:
numslashes = 0
for c in reversed(quote_buf[:-1]):
if c != '\\':
break
else:
numslashes += 1
if numslashes % 2 == 0:
in_quote = ''
write(''.join(quote_buf))
elif next1 in '\r\n':
conditional_2 = previous_non_space in newlineend_strings
if conditional_2 or previous_non_space > '~':
while 1:
if next2 < '!':
next2 = read(1)
if not next2:
break
else:
conditional_3 = next2 in newlinestart_strings
if conditional_3 or next2 > '~' or next2 == '/':
do_newline = True
break
elif next1 < '!' and not in_re:
conditional_4 = next2 in space_strings or next2 > '~'
conditional_5 = previous_non_space in space_strings
conditional_6 = previous_non_space > '~'
if (conditional_5 or conditional_6) and (conditional_4):
do_space = True
elif next1 == '/':
if in_re:
if previous != '\\':
in_re = False
write('/')
elif next2 == '/':
doing_single_comment = True
previous_before_comment = previous_non_space
elif next2 == '*':
doing_multi_comment = True
else:
in_re = previous_non_space in '(,=:[?!&|'
write('/')
else:
if do_space:
do_space = False
write(' ')
if do_newline:
write('\n')
do_newline = False
write(next1)
if not in_re and next1 in "'\"":
in_quote = next1
quote_buf = []
previous = next1
next1 = next2
if previous >= '!':
previous_non_space = previous
def js_minify(js):
"""Minify a JavaScript string."""
js = remove_commented_lines(js)
js = js_minify_keep_comments(js)
return js.strip()
|