/
minifyme.py
executable file
·215 lines (178 loc) · 5.82 KB
/
minifyme.py
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
import sys
from helper import compose, read_input, write_output
#doesn't work in all cases
def remove_line_feeds(input):
return input.replace('\n', '')
def remove_leading_and_trailing_whitespaces(input):
return '\n'.join([line.strip(' \r\t') for line in input.split('\n')])
def remove_line_comments(input):
output = ""
inside_string = False
string_delimiter = ''
inside_line_comment = False
inside_multiline_comment = False
inside_regex = False
end_of_multiline_comment = False
#XXX: perhaps use stack?
for index, char in enumerate(input):
if end_of_multiline_comment:
end_of_multiline_comment = False
output += char
continue
#end of line comment
if inside_line_comment:
if (input [index - 1] != '\\' and
char == '\n'):
inside_line_comment = False
output += char
continue
#end of a multiline comment
if inside_multiline_comment:
if (input[index - 1] != '\\' and
char == '*' and
index + 1 < len(input) and
input[index + 1] == "/"):
inside_multiline_comment = False
end_of_multiline_comment = True
output += char
continue
#end of regex
if inside_regex:
if (input[index - 1] != '\\' and
char == '/'):
inside_regex = False
output += char
continue
#end of string
if inside_string:
if (input[index -1] != '\\' and
char == string_delimiter):
inside_string = False
output += char
continue
#start of regex, line comment or multiline comment
if char == '/':
if index + 1 < len(input):
if input[index + 1] == '/':
inside_line_comment = True
continue
elif input[index + 1] == '*':
inside_multiline_comment = True
output += char
continue
else:
inside_regex = True
output += char
continue
#start of string
if (char == "'" or char == '"'):
inside_string = True
string_delimiter = char
output += char
continue
#otherwise
output += char
return output
#XXX: duplicated code!
def remove_multiline_comments(input):
output = ""
inside_string = False
string_delimiter = ''
inside_line_comment = False
inside_multiline_comment = False
inside_regex = False
not_output_next = False
#XXX: perhaps use stack?
for index, char in enumerate(input):
if not_output_next:
not_output_next = False
continue
#end of line comment
if inside_line_comment:
if (input [index - 1] != '\\' and
char == '\n'):
inside_line_comment = False
output += char
continue
#end of a multiline comment
if inside_multiline_comment:
if (input[index - 1] != '\\' and
char == '*' and
index + 1 < len(input) and
input[index + 1] == "/"):
inside_multiline_comment = False
not_output_next = True
continue
#end of regex
if inside_regex:
if (input[index - 1] != '\\' and
char == '/'):
inside_regex = False
output += char
continue
#end of string
if inside_string:
if (input[index -1] != '\\' and
char == string_delimiter):
inside_string = False
output += char
continue
#start of regex, line comment or multiline comment
if char == '/':
if index + 1 < len(input):
if input[index + 1] == '/':
inside_line_comment = True
output += char
continue
elif input[index + 1] == '*':
inside_multiline_comment = True
continue
else:
inside_regex = True
output += char
continue
#start of string
if (char == "'" or char == '"'):
inside_string = True
string_delimiter = char
output += char
continue
#otherwise
output += char
return output
def minifyme(input):
return compose([remove_multiline_comments,
remove_line_comments,
remove_leading_and_trailing_whitespaces], input)
def print_statistics(input, output):
input_size = len(input)
output_size = len(output)
removed = 100*(input_size - output_size)/float(input_size)
print """
Statistics
==========
Original file size: %s bytes
Minified file size: %s bytes
Removed: %s%% bytes
""" % (input_size, output_size, removed)
if __name__ == "__main__":
if len(sys.argv) > 1:
try:
filename = sys.argv[1]
if os.path.getsize(filename) == 0:
print "C'mon, don't bug me! Empty file?!"
exit(-1)
input = read_input(filename)
output = minifyme(input)
output_filename = "%s.min.js" % sys.argv[1][:-3]
write_output(output_filename, output)
print_statistics(input, output)
print "File %s written." % output_filename
except Exception, e:
print "Something wrong."
print e
else:
print "Usage: minifyme file.js"