1#!/usr/bin/env python 2# -*- coding: ascii -*- 3r""" 4===================== 5 Javascript Minifier 6===================== 7 8rJSmin is a javascript minifier written in python. 9 10The minifier is based on the semantics of `jsmin.c by Douglas Crockford`_\\. 11 12:Copyright: 13 14 Copyright 2011 - 2015 15 Andr\xe9 Malo or his licensors, as applicable 16 17:License: 18 19 Licensed under the Apache License, Version 2.0 (the "License"); 20 you may not use this file except in compliance with the License. 21 You may obtain a copy of the License at 22 23 http://www.apache.org/licenses/LICENSE-2.0 24 25 Unless required by applicable law or agreed to in writing, software 26 distributed under the License is distributed on an "AS IS" BASIS, 27 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28 See the License for the specific language governing permissions and 29 limitations under the License. 30 31The module is a re-implementation aiming for speed, so it can be used at 32runtime (rather than during a preprocessing step). Usually it produces the 33same results as the original ``jsmin.c``. It differs in the following ways: 34 35- there is no error detection: unterminated string, regex and comment 36 literals are treated as regular javascript code and minified as such. 37- Control characters inside string and regex literals are left untouched; they 38 are not converted to spaces (nor to \\n) 39- Newline characters are not allowed inside string and regex literals, except 40 for line continuations in string literals (ECMA-5). 41- "return /regex/" is recognized correctly. 42- Line terminators after regex literals are handled more sensibly 43- "+ +" and "- -" sequences are not collapsed to '++' or '--' 44- Newlines before ! operators are removed more sensibly 45- Comments starting with an exclamation mark (``!``) can be kept optionally 46- rJSmin does not handle streams, but only complete strings. (However, the 47 module provides a "streamy" interface). 48 49Since most parts of the logic are handled by the regex engine it's way faster 50than the original python port of ``jsmin.c`` by Baruch Even. The speed factor 51varies between about 6 and 55 depending on input and python version (it gets 52faster the more compressed the input already is). Compared to the 53speed-refactored python port by Dave St.Germain the performance gain is less 54dramatic but still between 3 and 50 (for huge inputs). See the docs/BENCHMARKS 55file for details. 56 57rjsmin.c is a reimplementation of rjsmin.py in C and speeds it up even more. 58 59Both python 2 and python 3 are supported. 60 61.. _jsmin.c by Douglas Crockford: 62 http://www.crockford.com/javascript/jsmin.c 63""" 64if __doc__: 65 # pylint: disable = redefined-builtin 66 __doc__ = __doc__.encode('ascii').decode('unicode_escape') 67__author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape') 68__docformat__ = "restructuredtext en" 69__license__ = "Apache License, Version 2.0" 70__version__ = '1.0.12' 71__all__ = ['jsmin'] 72 73import re as _re 74 75 76def _make_jsmin(python_only=False): 77 """ 78 Generate JS minifier based on `jsmin.c by Douglas Crockford`_ 79 80 .. _jsmin.c by Douglas Crockford: 81 http://www.crockford.com/javascript/jsmin.c 82 83 :Parameters: 84 `python_only` : ``bool`` 85 Use only the python variant. If true, the c extension is not even 86 tried to be loaded. 87 88 :Return: Minifier 89 :Rtype: ``callable`` 90 """ 91 # pylint: disable = unused-variable 92 # pylint: disable = too-many-locals 93 94 if not python_only: 95 try: 96 import _rjsmin 97 except ImportError: 98 pass 99 else: 100 return _rjsmin.jsmin 101 try: 102 xrange 103 except NameError: 104 xrange = range # pylint: disable = redefined-builtin 105 106 space_chars = r'[\000-\011\013\014\016-\040]' 107 108 line_comment = r'(?://[^\r\n]*)' 109 space_comment = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)' 110 space_comment_nobang = r'(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/)' 111 bang_comment = r'(?:/\*![^*]*\*+(?:[^/*][^*]*\*+)*/)' 112 113 string1 = \ 114 r'(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^\047\\\r\n]*)*\047)' 115 string2 = r'(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^"\\\r\n]*)*")' 116 string3 = r'(?:`(?:[^`\\]|\\.)*`)' 117 strings = r'(?:%s|%s|%s)' % (string1, string2, string3) 118 119 charclass = r'(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\])' 120 nospecial = r'[^/\\\[\r\n]' 121 regex = r'(?:/(?![\r\n/*])%s*(?:(?:\\[^\r\n]|%s)%s*)*/)' % ( 122 nospecial, charclass, nospecial 123 ) 124 space = r'(?:%s|%s)' % (space_chars, space_comment) 125 newline = r'(?:%s?[\r\n])' % line_comment 126 127 def fix_charclass(result): 128 """ Fixup string of chars to fit into a regex char class """ 129 pos = result.find('-') 130 if pos >= 0: 131 result = r'%s%s-' % (result[:pos], result[pos + 1:]) 132 133 def sequentize(string): 134 """ 135 Notate consecutive characters as sequence 136 137 (1-4 instead of 1234) 138 """ 139 first, last, result = None, None, [] 140 for char in map(ord, string): 141 if last is None: 142 first = last = char 143 elif last + 1 == char: 144 last = char 145 else: 146 result.append((first, last)) 147 first = last = char 148 if last is not None: 149 result.append((first, last)) 150 return ''.join(['%s%s%s' % ( 151 chr(first), 152 last > first + 1 and '-' or '', 153 last != first and chr(last) or '' 154 ) for first, last in result]) # noqa 155 156 return _re.sub( 157 r'([\000-\040\047])', # \047 for better portability 158 lambda m: '\\%03o' % ord(m.group(1)), ( 159 sequentize(result) 160 .replace('\\', '\\\\') 161 .replace('[', '\\[') 162 .replace(']', '\\]') 163 ) 164 ) 165 166 def id_literal_(what): 167 """ Make id_literal like char class """ 168 match = _re.compile(what).match 169 result = ''.join([ 170 chr(c) for c in xrange(127) if not match(chr(c)) 171 ]) 172 return '[^%s]' % fix_charclass(result) 173 174 def not_id_literal_(keep): 175 """ Make negated id_literal like char class """ 176 match = _re.compile(id_literal_(keep)).match 177 result = ''.join([ 178 chr(c) for c in xrange(127) if not match(chr(c)) 179 ]) 180 return r'[%s]' % fix_charclass(result) 181 182 not_id_literal = not_id_literal_(r'[a-zA-Z0-9_$]') 183 preregex1 = r'[(,=:\[!&|?{};\r\n]' 184 preregex2 = r'%(not_id_literal)sreturn' % locals() 185 186 id_literal = id_literal_(r'[a-zA-Z0-9_$]') 187 id_literal_open = id_literal_(r'[a-zA-Z0-9_${\[(!+-]') 188 id_literal_close = id_literal_(r'[a-zA-Z0-9_$}\])"\047+-]') 189 post_regex_off = id_literal_(r'[^\000-\040}\])?:|,;.&=+-]') 190 191 dull = r'[^\047"`/\000-\040]' 192 193 space_sub_simple = _re.compile(( 194 # noqa pylint: disable = bad-continuation 195 196 r'(%(dull)s+)' # 0 197 r'|(%(strings)s%(dull)s*)' # 1 198 r'|(?<=%(preregex1)s)' 199 r'%(space)s*(?:%(newline)s%(space)s*)*' 200 r'(%(regex)s)' # 2 201 r'(%(space)s*(?:%(newline)s%(space)s*)+' # 3 202 r'(?=%(post_regex_off)s))?' 203 r'|(?<=%(preregex2)s)' 204 r'%(space)s*(?:(%(newline)s)%(space)s*)*' # 4 205 r'(%(regex)s)' # 5 206 r'(%(space)s*(?:%(newline)s%(space)s*)+' # 6 207 r'(?=%(post_regex_off)s))?' 208 r'|(?<=%(id_literal_close)s)' 209 r'%(space)s*(?:(%(newline)s)%(space)s*)+' # 7 210 r'(?=%(id_literal_open)s)' 211 r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)' # 8 212 r'|(?<=\+)(%(space)s)+(?=\+)' # 9 213 r'|(?<=-)(%(space)s)+(?=-)' # 10 214 r'|%(space)s+' 215 r'|(?:%(newline)s%(space)s*)+' 216 ) % locals()).sub 217 218 # print space_sub_simple.__self__.pattern 219 220 def space_subber_simple(match): 221 """ Substitution callback """ 222 # pylint: disable = too-many-return-statements 223 224 groups = match.groups() 225 if groups[0]: 226 return groups[0] 227 elif groups[1]: 228 return groups[1] 229 elif groups[2]: 230 if groups[3]: 231 return groups[2] + '\n' 232 return groups[2] 233 elif groups[5]: 234 return "%s%s%s" % ( 235 groups[4] and '\n' or '', 236 groups[5], 237 groups[6] and '\n' or '', 238 ) 239 elif groups[7]: 240 return '\n' 241 elif groups[8] or groups[9] or groups[10]: 242 return ' ' 243 else: 244 return '' 245 246 space_sub_banged = _re.compile(( 247 # noqa pylint: disable = bad-continuation 248 249 r'(%(dull)s+)' # 0 250 r'|(%(strings)s%(dull)s*)' # 1 251 r'|(?<=%(preregex1)s)' 252 r'(%(space)s*(?:%(newline)s%(space)s*)*)' # 2 253 r'(%(regex)s)' # 3 254 r'(%(space)s*(?:%(newline)s%(space)s*)+' # 4 255 r'(?=%(post_regex_off)s))?' 256 r'|(?<=%(preregex2)s)' 257 r'(%(space)s*(?:(%(newline)s)%(space)s*)*)' # 5, 6 258 r'(%(regex)s)' # 7 259 r'(%(space)s*(?:%(newline)s%(space)s*)+' # 8 260 r'(?=%(post_regex_off)s))?' 261 r'|(?<=%(id_literal_close)s)' 262 r'(%(space)s*(?:%(newline)s%(space)s*)+)' # 9 263 r'(?=%(id_literal_open)s)' 264 r'|(?<=%(id_literal)s)(%(space)s+)(?=%(id_literal)s)' # 10 265 r'|(?<=\+)(%(space)s+)(?=\+)' # 11 266 r'|(?<=-)(%(space)s+)(?=-)' # 12 267 r'|(%(space)s+)' # 13 268 r'|((?:%(newline)s%(space)s*)+)' # 14 269 ) % locals()).sub 270 271 # print space_sub_banged.__self__.pattern 272 273 keep = _re.compile(( 274 r'%(space_chars)s+|%(space_comment_nobang)s+|%(newline)s+' 275 r'|(%(bang_comment)s+)' 276 ) % locals()).sub 277 keeper = lambda m: m.groups()[0] or '' 278 279 # print keep.__self__.pattern 280 281 def space_subber_banged(match): 282 """ Substitution callback """ 283 # pylint: disable = too-many-return-statements 284 285 groups = match.groups() 286 if groups[0]: 287 return groups[0] 288 elif groups[1]: 289 return groups[1] 290 elif groups[3]: 291 return "%s%s%s%s" % ( 292 keep(keeper, groups[2]), 293 groups[3], 294 keep(keeper, groups[4] or ''), 295 groups[4] and '\n' or '', 296 ) 297 elif groups[7]: 298 return "%s%s%s%s%s" % ( 299 keep(keeper, groups[5]), 300 groups[6] and '\n' or '', 301 groups[7], 302 keep(keeper, groups[8] or ''), 303 groups[8] and '\n' or '', 304 ) 305 elif groups[9]: 306 return keep(keeper, groups[9]) + '\n' 307 elif groups[10] or groups[11] or groups[12]: 308 return keep(keeper, groups[10] or groups[11] or groups[12]) or ' ' 309 else: 310 return keep(keeper, groups[13] or groups[14]) 311 312 def jsmin(script, keep_bang_comments=False): 313 r""" 314 Minify javascript based on `jsmin.c by Douglas Crockford`_\. 315 316 Instead of parsing the stream char by char, it uses a regular 317 expression approach which minifies the whole script with one big 318 substitution regex. 319 320 .. _jsmin.c by Douglas Crockford: 321 http://www.crockford.com/javascript/jsmin.c 322 323 :Parameters: 324 `script` : ``str`` 325 Script to minify 326 327 `keep_bang_comments` : ``bool`` 328 Keep comments starting with an exclamation mark? (``/*!...*/``) 329 330 :Return: Minified script 331 :Rtype: ``str`` 332 """ 333 # pylint: disable = redefined-outer-name 334 335 if keep_bang_comments: 336 return space_sub_banged( 337 space_subber_banged, '\n%s\n' % script 338 ).strip() 339 else: 340 return space_sub_simple( 341 space_subber_simple, '\n%s\n' % script 342 ).strip() 343 344 return jsmin 345 346jsmin = _make_jsmin() 347 348 349def jsmin_for_posers(script, keep_bang_comments=False): 350 r""" 351 Minify javascript based on `jsmin.c by Douglas Crockford`_\. 352 353 Instead of parsing the stream char by char, it uses a regular 354 expression approach which minifies the whole script with one big 355 substitution regex. 356 357 .. _jsmin.c by Douglas Crockford: 358 http://www.crockford.com/javascript/jsmin.c 359 360 :Warning: This function is the digest of a _make_jsmin() call. It just 361 utilizes the resulting regexes. It's here for fun and may 362 vanish any time. Use the `jsmin` function instead. 363 364 :Parameters: 365 `script` : ``str`` 366 Script to minify 367 368 `keep_bang_comments` : ``bool`` 369 Keep comments starting with an exclamation mark? (``/*!...*/``) 370 371 :Return: Minified script 372 :Rtype: ``str`` 373 """ 374 if not keep_bang_comments: 375 rex = ( 376 r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]' 377 r'|\r?\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]' 378 r'|\r?\n|\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?' 379 r'{};\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*' 380 r'][^*]*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\0' 381 r'14\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*((?:/(?![\r' 382 r'\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r' 383 r'\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/))((?:[\000-\011\013\014' 384 r'\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:(?:(?://[^\r' 385 r'\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:' 386 r'[^/*][^*]*\*+)*/))*)+(?=[^\000-\040&)+,.:;=?\]|}-]))?|(?<=[\00' 387 r'0-#%-,./:-@\[-^`{-~-]return)(?:[\000-\011\013\014\016-\040]|(?' 388 r':/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:((?:(?://[^\r\n]*)?[\r\n]' 389 r'))(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*' 390 r'\*+)*/))*)*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[' 391 r'[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/))((' 392 r'?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)' 393 r'*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\04' 394 r'0]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040&)+,.:;' 395 r'=?\]|}-]))?|(?<=[^\000-!#%&(*,./:-@\[\\^`{|~])(?:[\000-\011\01' 396 r'3\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:((?:(?:' 397 r'//[^\r\n]*)?[\r\n]))(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]' 398 r'*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040"#%-\047)*,./:-@\\-^' 399 r'`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-\011\013\014\0' 400 r'16-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=[^\000-#%-,./' 401 r':-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]|(?:/\*[' 402 r'^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=\+)|(?<=-)((?:[\000-\011\013' 403 r'\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=-)|(?:[' 404 r'\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)' 405 r')+|(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]' 406 r'|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+' 407 ) 408 409 def subber(match): 410 """ Substitution callback """ 411 groups = match.groups() 412 return ( 413 groups[0] or 414 groups[1] or 415 (groups[3] and (groups[2] + '\n')) or 416 groups[2] or 417 (groups[5] and "%s%s%s" % ( 418 groups[4] and '\n' or '', 419 groups[5], 420 groups[6] and '\n' or '', 421 )) or 422 (groups[7] and '\n') or 423 (groups[8] and ' ') or 424 (groups[9] and ' ') or 425 (groups[10] and ' ') or 426 '' 427 ) 428 else: 429 rex = ( 430 r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]' 431 r'|\r?\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]' 432 r'|\r?\n|\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?' 433 r'{};\r\n])((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/' 434 r'*][^*]*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013' 435 r'\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*)((?:/(?!' 436 r'[\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^' 437 r'\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/))((?:[\000-\011\013\01' 438 r'4\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:(?:(?://[^' 439 r'\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(' 440 r'?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040&)+,.:;=?\]|}-]))?|(?<=[' 441 r'\000-#%-,./:-@\[-^`{-~-]return)((?:[\000-\011\013\014\016-\040' 442 r']|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:((?:(?://[^\r\n]*)?[' 443 r'\r\n]))(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][' 444 r'^*]*\*+)*/))*)*)((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|' 445 r'(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*' 446 r'/))((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]' 447 r'*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\01' 448 r'6-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040&)' 449 r'+,.:;=?\]|}-]))?|(?<=[^\000-!#%&(*,./:-@\[\\^`{|~])((?:[\000-' 450 r'\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:' 451 r'(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/' 452 r'\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+)(?=[^\000-\040"#%-\047)*,./' 453 r':-@\\-^`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-\011\01' 454 r'3\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+)(?=[^\000' 455 r'-#%-,./:-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]|' 456 r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+)(?=\+)|(?<=-)((?:[\000-\0' 457 r'11\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+)(?=-' 458 r')|((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*' 459 r'\*+)*/))+)|((?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014' 460 r'\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+)' 461 ) 462 463 keep = _re.compile(( 464 r'[\000-\011\013\014\016-\040]+|(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*' 465 r'\*+)*/)+|(?:(?://[^\r\n]*)?[\r\n])+|((?:/\*![^*]*\*+(?:[^/*][^' 466 r'*]*\*+)*/)+)' 467 ) % locals()).sub 468 keeper = lambda m: m.groups()[0] or '' 469 470 def subber(match): 471 """ Substitution callback """ 472 groups = match.groups() 473 return ( 474 groups[0] or 475 groups[1] or 476 (groups[3] and "%s%s%s%s" % ( 477 keep(keeper, groups[2]), 478 groups[3], 479 keep(keeper, groups[4] or ''), 480 groups[4] and '\n' or '', 481 )) or 482 (groups[7] and "%s%s%s%s%s" % ( 483 keep(keeper, groups[5]), 484 groups[6] and '\n' or '', 485 groups[7], 486 keep(keeper, groups[8] or ''), 487 groups[8] and '\n' or '', 488 )) or 489 (groups[9] and keep(keeper, groups[9] + '\n')) or 490 (groups[10] and keep(keeper, groups[10]) or ' ') or 491 (groups[11] and keep(keeper, groups[11]) or ' ') or 492 (groups[12] and keep(keeper, groups[12]) or ' ') or 493 keep(keeper, groups[13] or groups[14]) 494 ) 495 496 return _re.sub(rex, subber, '\n%s\n' % script).strip() 497 498 499if __name__ == '__main__': 500 def main(): 501 """ Main """ 502 import sys as _sys 503 504 argv = _sys.argv[1:] 505 keep_bang_comments = '-b' in argv or '-bp' in argv or '-pb' in argv 506 if '-p' in argv or '-bp' in argv or '-pb' in argv: 507 xjsmin = _make_jsmin(python_only=True) 508 else: 509 xjsmin = jsmin 510 511 _sys.stdout.write(xjsmin( 512 _sys.stdin.read(), keep_bang_comments=keep_bang_comments 513 )) 514 515 main() 516