-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbuild.py
165 lines (131 loc) · 4.54 KB
/
build.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
"""
Python script to minify the code. Call with ``python build.py develop``
to continuously build the sources.
You may need to ``pip install jsmin``.
"""
import sys
import time
from jsmin import jsmin
preamble = """
/*
* faq-div.min.js - Copyright 2019-2023 Almar Klein
* https://github.com/almarklein/faq-div
*
* @license GPLv3 for open source use only
* or FAQ-div Commercial License for commercial use
* http://faq-div.com
* Do not remove this comment.
*/
""".lstrip()
def wordgen():
# This can generate up to 28 * 28 * 27 -> over 20k words.
alphabet = tuple("abcdefghijklmnopqrstuvwxyz")
alphebet_plus_empty = ("",) + tuple(alphabet)
for pre1 in alphebet_plus_empty:
for pre2 in alphebet_plus_empty:
for c in alphabet:
yield pre1 + pre2 + c
def remove_comments_and_trailing_ws(text):
lines = text.splitlines()
lines = [line.split("//")[0].strip() for line in lines]
lines = [
line for line in lines if not (line.startswith("/*") and line.endswith("*/"))
]
lines = [line for line in lines if line]
return "\n".join(lines)
def rename_variabes_js(text):
# Note that this breaks on multi-line comments
words = {}
words_that_can_be_renamed = set()
# First examine all words
word_start = None
next_word_is_var_definition = False
in_something = None
for i, c in enumerate(text):
if in_something is not None:
# In a string or comment, see if this char ends it
if c == in_something:
in_something = None
elif word_start is not None:
# In a word, see if this char ends it
if not (c == "_" or c.isalnum()):
word = text[word_start:i]
if word in ("var", "let", "function"):
next_word_is_var_definition = i
else:
words.setdefault(word, []).append((word_start, i))
if next_word_is_var_definition == word_start - 1:
words_that_can_be_renamed.add(word)
next_word_is_var_definition = False
word_start = None
else:
if c == "_" or c.isalpha():
if not (i > 0 and text[i - 1] == "."):
word_start = i
elif c == "'":
in_something = "'"
elif c == '"':
in_something = '"'
elif c == "/" and i > 0 and text[i - 1] == "/":
in_something = "\n"
# Some words are protected
protected = "css", "version"
for word in protected:
words_that_can_be_renamed.discard(word)
# Collect all replacements
new_words = wordgen()
replacements = []
for word, occurances in words.items():
if word in words_that_can_be_renamed:
new_word = new_words.__next__()
for i1, i2 in occurances:
replacements.append((i1, i2, new_word))
# Sort the replacements and apply them
replacements.sort(reverse=True)
for i1, i2, new_word in replacements:
text = text[:i1] + new_word + text[i2:]
return text
def minify_js(text):
text = remove_comments_and_trailing_ws(text)
text = rename_variabes_js(text)
text = jsmin(text)
return text
def minify_css(text):
text = remove_comments_and_trailing_ws(text)
text = text.replace("\n", " ")
text = text.replace("{ ", "{").replace(" }", "}").replace(" {", "{")
text = text.replace(": ", ":").replace(", ", ",").replace("; ", ";")
text = text.replace(" > ", ">")
text = text.replace('"', "'") # for consistency and embedding
return text
last_result = ""
def main():
global last_result
css_ori = open("src/faq-div.css", "rb").read().decode()
css = minify_css(css_ori)
js_ori = open("src/faq-div.js", "rb").read().decode()
js = minify_js(js_ori)
minified = (
preamble
+ "(function() {\n"
+ js.replace('css="";', f'css="{css}";\n')
+ "\n})();"
)
if minified == last_result:
return
last_result = minified
print(
f"Minimizing: original css and js are {len(css_ori)}+{len(js_ori)}"
+ f"={len(js_ori) + len(css_ori)} chars. Minified: {len(minified)}"
)
with open("dist/faq-div.min.js", "wb") as f:
f.write(minified.encode())
if __name__ == "__main__":
if "help" in sys.argv or "-h" in sys.argv or "--help" in sys.argv:
print(__doc__)
elif "develop" in sys.argv:
while True:
main()
time.sleep(1)
else:
main()