From f35a7b0e70032de2feec9f3bda09da44cf0e1073 Mon Sep 17 00:00:00 2001 From: srv Date: Mon, 28 Apr 2025 17:11:28 -0500 Subject: first commit --- plugins/compressor/js_minifer.py | 171 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 plugins/compressor/js_minifer.py (limited to 'plugins/compressor/js_minifer.py') diff --git a/plugins/compressor/js_minifer.py b/plugins/compressor/js_minifer.py new file mode 100644 index 0000000..6654b76 --- /dev/null +++ b/plugins/compressor/js_minifer.py @@ -0,0 +1,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() -- cgit v1.2.3