Index: Source/web/scripts/make-file-arrays.py |
diff --git a/Source/web/scripts/make-file-arrays.py b/Source/web/scripts/make-file-arrays.py |
index 21ee6fbd26ef2e05e97136c710aae8b1e0e10bdf..1cf0b535fe5d9cd712e132b3095942b5b6165c8e 100755 |
--- a/Source/web/scripts/make-file-arrays.py |
+++ b/Source/web/scripts/make-file-arrays.py |
@@ -34,6 +34,8 @@ import re |
import sys |
from optparse import OptionParser |
+# Should be spam in the build log? |
+DEBUG_OUTPUT = False |
def make_variable_name_and_read(file_name): |
result = re.match(r'([\w\d_]+)\.([\w\d_]+)', os.path.basename(file_name)) |
@@ -46,6 +48,76 @@ def make_variable_name_and_read(file_name): |
return variable_name, content |
+def is_identifier_char(c): |
+ assert len(c) == 1 |
+ return c.isalnum() or c in "$_" |
+ |
+ |
+def strip_js_whitespace_not_in_strings(content): |
+ """Removes whitespace we don't need in this javascript |
+ program. Whitespace is relevant inside strings and regexps (not |
+ supported) and sometimes new-lines are needed to trigger the |
+ automatic semi-colon insertion. |
+ |
+ This is _slightly_ worse than Closure at stripping whitespace |
+ but so close that there is no urgent reason to improve it.""" |
+ |
+ out = "" |
+ |
+ quoted_char = False # When True the next char is to be output as is. |
+ string_delimiter = None # When set we're inside a string. |
+ lazy_space = False |
+ lazy_new_line = False |
+ for c in content: |
+ if quoted_char: |
+ quoted_char = False |
+ out += c |
+ elif string_delimiter: |
+ out += c |
+ if c == "\\": |
+ quoted_char = True |
+ elif c == string_delimiter: |
+ string_delimiter = None |
+ else: |
+ if c == "\\": |
+ quoted_char = True |
+ elif c == '"' or c == "'": |
+ string_delimiter = c |
+ |
+ if c in " \t\n\r\f": |
+ lazy_space = True |
+ if c == "\n": |
+ lazy_new_line = True |
+ else: |
+ if lazy_space and out: |
+ output_space = False |
+ if is_identifier_char(out[-1]) and is_identifier_char(c): |
+ # Will prevent "var i" -> "vari" or |
+ # "function $" -> "function$" |
+ output_space = True |
+ elif out[-1] == c and c not in "{}()[]": |
+ # Will prevent "a - -b" -> "a--b" or "a--\n-b" -> "a---b" |
+ output_space = True |
+ if output_space: |
+ if lazy_new_line: |
+ out += "\n" |
+ else: |
+ out += " " |
+ lazy_space = False |
+ lazy_new_line = False |
+ out += c |
+ return out |
+ |
+# Quick self testing. If this fails, we better not proceed with the build. |
+assert strip_js_whitespace_not_in_strings(' var a = \' \\" \\" \' ;') == 'var a=\' \\" \\" \';' |
+ |
+assert strip_js_whitespace_not_in_strings("a-- - \t--b") == "a-- - --b" |
+ |
+assert strip_js_whitespace_not_in_strings("if (a == $) {\n}") == "if(a==$){}" |
+assert strip_js_whitespace_not_in_strings("var \n a") == "var\na" |
+assert strip_js_whitespace_not_in_strings("{ { a( (1+ 2) / 3);}\n} ") == "{{a((1+2)/3);}}" |
+assert strip_js_whitespace_not_in_strings(r" ' \' ' ") == r"' \' '" |
+ |
def strip_whitespace_and_comments(file_name, content): |
result = re.match(r'.*\.([^.]+)', file_name) |
if not result: |
@@ -60,20 +132,28 @@ def strip_whitespace_and_comments(file_name, content): |
leading_space = re.compile(r'^[ \t]+', re.MULTILINE) |
trailing_space = re.compile(r'[ \t]+$', re.MULTILINE) |
empty_line = re.compile(r'\n+') |
+ orig_content = content |
if extension == 'js': |
+ content = orig_content |
content = multi_line_comment.sub('', content) |
content = single_line_comment.sub('', content) |
content = trailing_comment.sub(r'\1', content) |
- content = repeating_space.sub(' ', content) |
- content = leading_space.sub('', content) |
- content = trailing_space.sub('', content) |
- content = empty_line.sub('\n', content) |
+ content = strip_js_whitespace_not_in_strings(content) |
+ optimized_content_content = content |
+ content = orig_content |
+ if len(optimized_content_content) < len(content): |
+ content = optimized_content_content |
elif extension == 'css': |
content = multi_line_comment.sub('', content) |
content = repeating_space.sub(' ', content) |
content = leading_space.sub('', content) |
content = trailing_space.sub('', content) |
content = empty_line.sub('\n', content) |
+ if DEBUG_OUTPUT: |
+ in_size = len(orig_content) |
+ out_size = len(content) |
+ if out_size < in_size: |
+ print("%s: Compaction saved %.1f%% for a total of %d bytes." % (file_name, float(100 * (in_size - out_size)) / in_size, in_size - out_size)) |
return content |
@@ -81,6 +161,10 @@ def process_file(file_name): |
variable_name, content = make_variable_name_and_read(file_name) |
content = strip_whitespace_and_comments(file_name, content) |
size = len(content) |
+ if DEBUG_OUTPUT: |
+ out_debug_name = file_name + ".generated" |
+ with open(out_debug_name, "w") as out_debug_file: |
+ out_debug_file.write(content) |
return variable_name, content |