-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlektor_jsminify.py
141 lines (115 loc) · 4.76 KB
/
lektor_jsminify.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
# make print compatible with python2
from __future__ import print_function
import os
import errno
import re
import fnmatch
import rjsmin
from lektor.pluginsystem import Plugin
from lektor.utils import comma_delimited
from termcolor import colored
import threading
import time
MINIFY_FLAG = "jsminify"
def any_fnmatch(filename, patterns):
for pat in patterns:
if fnmatch.fnmatch(filename, pat):
return True
return False
class JsminifyPlugin(Plugin):
name = u'Lektor JSminify'
description = u'JS minifier for Lektor. Based on rjsmin.'
def __init__(self, *args, **kwargs):
Plugin.__init__(self, *args, **kwargs)
config = self.get_config()
self.source_dir = config.get('source_dir', 'asset_sources/js/')
self.output_dir = config.get('output_dir', 'assets/js/')
self.name_prefix = config.get('name_prefix', '')
self.keep_bang_comments = config.get('keep_bang_comments', 'False')
self.included_assets = list(comma_delimited(config.get('included_assets', '')))
self.excluded_assets = list(comma_delimited(config.get('excluded_assets', '')))
self.watcher = None
self.run_watcher = False
def is_enabled(self, build_flags):
return bool(build_flags.get(MINIFY_FLAG))
def is_uninteresting_source_name(self, filename):
"""These files are ignored when sources are built into artifacts."""
if any_fnmatch(filename, self.included_assets):
# Included by the user's project config, thus not uninteresting.
return False
return any_fnmatch(filename, self.excluded_assets)
def minify_file(self, target, output):
"""
Minifies the target js file.
"""
filename = os.path.basename(target)
output_file = os.path.join(output, filename)
file_end = self.name_prefix + '.js'
if not output_file.endswith(file_end):
output_file = output_file.replace('.js', file_end)
rebuild = False
config_file = os.path.join(self.env.root_path, 'configs/jsminify.ini')
# when input file changed
if os.path.isfile(output_file):
if ( os.path.getmtime(target) > os.path.getmtime(output_file)
# when config file exists and changed
or os.path.isfile(config_file) and os.path.getmtime(config_file) > os.path.getmtime(output_file)):
rebuild = True
else:
rebuild = True
if not rebuild:
return
result = None
with open(target, 'r') as fr:
result = rjsmin.jsmin(fr.read(), self.keep_bang_comments.lower()=='true')
if result == None:
return
with open(output_file, 'w') as fw:
fw.write(result)
print(colored('js', 'green'), self.source_dir + os.path.basename(target), '\u27a1', self.output_dir + os.path.basename(output_file))
def find_source_files(self, destination):
"""
Finds all js files in the given destination.
"""
for root, dirs, files in os.walk(destination):
for f in files:
if f.endswith('.js') and not self.is_uninteresting_source_name(f):
yield os.path.join(root, f)
def thread(self, filenames, output):
while True:
if not self.run_watcher:
self.watcher = None
break
for filename in filenames:
self.minify_file(filename, output)
time.sleep(1)
def on_server_spawn(self, **extra):
self.run_watcher = True
def on_server_stop(self, **extra):
if self.watcher is not None:
self.run_watcher = False
def make_sure_path_exists(self, path):
try:
os.makedirs(path)
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
def on_before_build_all(self, builder, **extra):
try: # lektor 3+
is_enabled = self.is_enabled(builder.extra_flags)
except AttributeError: # lektor 2+
is_enabled = self.is_enabled(builder.build_flags)
if not is_enabled:
return
root_js = os.path.join(self.env.root_path, self.source_dir )
output = os.path.join(self.env.root_path, self.output_dir )
# output path has to exist
#os.makedirs(output, exist_ok=True) when python2 finally runs out
self.make_sure_path_exists(output)
if self.run_watcher:
filenames = self.find_source_files(root_js)
self.watcher = threading.Thread(target=self.thread, args=(filenames, output))
self.watcher.start()
else:
for filename in self.find_source_files(root_js):
self.minify_file(filename, output)