Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/jsmin/__init__.py: 70%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

155 statements  

1# vim: set fileencoding=utf-8 : 

2 

3# This code is original from jsmin by Douglas Crockford, it was translated to 

4# Python by Baruch Even. It was rewritten by Dave St.Germain for speed. 

5# 

6# The MIT License (MIT) 

7#  

8# Copyright (c) 2013 Dave St.Germain 

9#  

10# Permission is hereby granted, free of charge, to any person obtaining a copy 

11# of this software and associated documentation files (the "Software"), to deal 

12# in the Software without restriction, including without limitation the rights 

13# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 

14# copies of the Software, and to permit persons to whom the Software is 

15# furnished to do so, subject to the following conditions: 

16#  

17# The above copyright notice and this permission notice shall be included in 

18# all copies or substantial portions of the Software. 

19#  

20# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 

21# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

22# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 

23# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 

24# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 

25# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 

26# THE SOFTWARE. 

27 

28 

29import io 

30 

31__all__ = ['jsmin', 'JavascriptMinify'] 

32__version__ = '3.0.1' 

33 

34 

35def jsmin(js, **kwargs): 

36 """ 

37 returns a minified version of the javascript string 

38 """ 

39 klass = io.StringIO 

40 ins = klass(js) 

41 outs = klass() 

42 JavascriptMinify(ins, outs, **kwargs).minify() 

43 return outs.getvalue() 

44 

45 

46class JavascriptMinify(object): 

47 """ 

48 Minify an input stream of javascript, writing 

49 to an output stream 

50 """ 

51 

52 def __init__(self, instream=None, outstream=None, quote_chars="'\""): 

53 self.ins = instream 

54 self.outs = outstream 

55 self.quote_chars = quote_chars 

56 

57 def minify(self, instream=None, outstream=None): 

58 if instream and outstream: 

59 self.ins, self.outs = instream, outstream 

60 

61 self.is_return = False 

62 self.return_buf = '' 

63 

64 def write(char): 

65 # all of this is to support literal regular expressions. 

66 # sigh 

67 if char in 'return': 

68 self.return_buf += char 

69 self.is_return = self.return_buf == 'return' 

70 else: 

71 self.return_buf = '' 

72 self.is_return = self.is_return and char < '!' 

73 self.outs.write(char) 

74 if self.is_return: 

75 self.return_buf = '' 

76 

77 read = self.ins.read 

78 

79 space_strings = "abcdefghijklmnopqrstuvwxyz"\ 

80 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$\\" 

81 self.space_strings = space_strings 

82 starters, enders = '{[(+-', '}])+-/' + self.quote_chars 

83 newlinestart_strings = starters + space_strings + self.quote_chars 

84 newlineend_strings = enders + space_strings + self.quote_chars 

85 self.newlinestart_strings = newlinestart_strings 

86 self.newlineend_strings = newlineend_strings 

87 

88 do_newline = False 

89 do_space = False 

90 escape_slash_count = 0 

91 in_quote = '' 

92 quote_buf = [] 

93 

94 previous = ';' 

95 previous_non_space = ';' 

96 next1 = read(1) 

97 

98 while next1: 

99 next2 = read(1) 

100 if in_quote: 

101 quote_buf.append(next1) 

102 

103 if next1 == in_quote: 

104 numslashes = 0 

105 for c in reversed(quote_buf[:-1]): 

106 if c != '\\': 

107 break 

108 else: 

109 numslashes += 1 

110 if numslashes % 2 == 0: 

111 in_quote = '' 

112 write(''.join(quote_buf)) 

113 elif next1 in '\r\n': 

114 next2, do_newline = self.newline( 

115 previous_non_space, next2, do_newline) 

116 elif next1 < '!': 

117 if (previous_non_space in space_strings \ 

118 or previous_non_space > '~') \ 

119 and (next2 in space_strings or next2 > '~'): 

120 do_space = True 

121 elif previous_non_space in '-+' and next2 == previous_non_space: 

122 # protect against + ++ or - -- sequences 

123 do_space = True 

124 elif self.is_return and next2 == '/': 

125 # returning a regex... 

126 write(' ') 

127 elif next1 == '/': 

128 if do_space: 

129 write(' ') 

130 if next2 == '/': 

131 # Line comment: treat it as a newline, but skip it 

132 next2 = self.line_comment(next1, next2) 

133 next1 = '\n' 

134 next2, do_newline = self.newline( 

135 previous_non_space, next2, do_newline) 

136 elif next2 == '*': 

137 self.block_comment(next1, next2) 

138 next2 = read(1) 

139 if previous_non_space in space_strings: 

140 do_space = True 

141 next1 = previous 

142 else: 

143 if previous_non_space in '{(,=:[?!&|;' or self.is_return: 

144 self.regex_literal(next1, next2) 

145 # hackish: after regex literal next1 is still / 

146 # (it was the initial /, now it's the last /) 

147 next2 = read(1) 

148 else: 

149 write('/') 

150 else: 

151 if do_newline: 

152 write('\n') 

153 do_newline = False 

154 do_space = False 

155 if do_space: 

156 do_space = False 

157 write(' ') 

158 

159 write(next1) 

160 if next1 in self.quote_chars: 

161 in_quote = next1 

162 quote_buf = [] 

163 

164 if next1 >= '!': 

165 previous_non_space = next1 

166 

167 if next1 == '\\': 

168 escape_slash_count += 1 

169 else: 

170 escape_slash_count = 0 

171 

172 previous = next1 

173 next1 = next2 

174 

175 def regex_literal(self, next1, next2): 

176 assert next1 == '/' # otherwise we should not be called! 

177 

178 self.return_buf = '' 

179 

180 read = self.ins.read 

181 write = self.outs.write 

182 

183 in_char_class = False 

184 

185 write('/') 

186 

187 next = next2 

188 while next and (next != '/' or in_char_class): 

189 write(next) 

190 if next == '\\': 

191 write(read(1)) # whatever is next is escaped 

192 elif next == '[': 

193 write(read(1)) # character class cannot be empty 

194 in_char_class = True 

195 elif next == ']': 

196 in_char_class = False 

197 next = read(1) 

198 

199 write('/') 

200 

201 def line_comment(self, next1, next2): 

202 assert next1 == next2 == '/' 

203 

204 read = self.ins.read 

205 

206 while next1 and next1 not in '\r\n': 

207 next1 = read(1) 

208 while next1 and next1 in '\r\n': 

209 next1 = read(1) 

210 

211 return next1 

212 

213 def block_comment(self, next1, next2): 

214 assert next1 == '/' 

215 assert next2 == '*' 

216 

217 read = self.ins.read 

218 

219 # Skip past first /* and avoid catching on /*/...*/ 

220 next1 = read(1) 

221 next2 = read(1) 

222 

223 comment_buffer = '/*' 

224 while next1 != '*' or next2 != '/': 

225 comment_buffer += next1 

226 next1 = next2 

227 next2 = read(1) 

228 

229 if comment_buffer.startswith("/*!"): 

230 # comment needs preserving 

231 self.outs.write(comment_buffer) 

232 self.outs.write("*/\n") 

233 

234 

235 def newline(self, previous_non_space, next2, do_newline): 

236 read = self.ins.read 

237 

238 if previous_non_space and ( 

239 previous_non_space in self.newlineend_strings 

240 or previous_non_space > '~'): 

241 while 1: 

242 if next2 < '!': 

243 next2 = read(1) 

244 if not next2: 

245 break 

246 else: 

247 if next2 in self.newlinestart_strings \ 

248 or next2 > '~' or next2 == '/': 

249 do_newline = True 

250 break 

251 

252 return next2, do_newline