forked from johtso/CoffeePy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcoffee_watch.py
135 lines (114 loc) · 4.9 KB
/
coffee_watch.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
import PyV8 as v8
import time
import sys
import os
import codecs
import urllib
import optparse
COMPILER_URL = 'https://github.com/jashkenas/coffee-script/raw/master/extras/coffee-script.js'
POLL_INTERVAL = 2
# keep track of when files were last compiled
compiled = {}
def start_watching():
print "Checking %s and subdirectories every %d seconds" % (opts.watch.folder,
opts.interval)
while True:
try:
_check_folder(opts.watch.folder)
time.sleep(opts.interval)
except KeyboardInterrupt:
# allow graceful exit (no stacktrace output)
sys.exit(0)
def _check_folder(watch_path):
for dirpath, dirnames, filenames in os.walk(watch_path):
filepaths = (os.path.join(dirpath, filename)
for filename in filenames if filename.endswith('.coffee'))
for filepath in filepaths:
mtime = os.stat(filepath).st_mtime
compiled_path = _compiled_path(filepath)
if (not filepath in compiled or compiled[filepath] < mtime or
not os.path.isfile(compiled_path)):
_compile_file(filepath, compiled_path)
compiled[filepath] = mtime
def _compiled_path(filepath):
if opts.output:
# make a compiled path that is output_path + source file path relative
# to watch folder
output_path = opts.output.folder
filename = os.path.split(filepath)[1]
compiled_filename = filename[:filename.rfind('.')] + '.js'
rel_path = os.path.relpath(os.path.split(filepath)[0], opts.watch.folder)
return os.path.join(output_path, rel_path, compiled_filename)
else:
return filepath[:filepath.rfind('.')] + '.js'
def _compile_file(path, compiled_path):
# if using --output the destination folder may not yet exist
compiled_folder = os.path.split(compiled_path)[0]
if not os.path.exists(compiled_folder):
os.makedirs(compiled_folder)
try:
codecs.open(compiled_path, 'w', encoding='utf-8')\
.write(_convert(codecs.open(path, 'r', encoding='utf-8').read()))
print "%s - compiled %s" % (time.strftime('%X'),
os.path.relpath(path, opts.watch.folder))
except Exception, e:
msg = "Error parsing %s: %s" % (os.path.relpath(path, opts.watch.folder),
str(e))
print msg
codecs.open(compiled_path, 'w', encoding='utf-8').write(str(e))
def _convert(coffee):
return compiler.compile(coffee, {'bare': opts.bare})
class Path(object):
"""Object represeting a path.
Contains the full path, the folder path and the name of the file if relevant.
Attributes:
path -- the full path.
folder -- the folder path (not including filename).
file -- the filename (or undefined).
"""
def __init__(self, path):
self.path = os.path.realpath(path)
head, tail = os.path.split(path)
if head:
self.folder = os.path.realpath(head)
self.file = tail
else:
# if the path is a single word, it could be a file or a folder
if os.path.isdir(tail):
self.folder = os.path.realpath(tail)
else:
self.folder = os.path.realpath(os.getcwd())
self.file = tail
def _check_folder_exists(folder_path):
if not os.path.isdir(folder_path):
parser.error("%s is not a valid path to an existing folder." % (folder_path,))
def _store_path(option, opt_str, value, parser):
path = Path(value)
_check_folder_exists(path.folder)
setattr(parser.values, option.dest, path)
def _process_args(args):
if len(args) > 1:
op.error('please specify a single folder to be watched')
elif len(args) == 0:
op.error('please specify a folder to be watched')
else:
path = Path(args[0])
_check_folder_exists(path.folder)
setattr(opts, 'watch', path)
if __name__ == '__main__':
op = optparse.OptionParser(usage='Usage: %prog [options] <watch_directory>')
op.add_option('-b', '--bare', action='store_true', default=False,
dest='bare', help='compile without the top-level function wrapper')
op.add_option('-o', '--output', action='callback', callback=_store_path,
dest='output', help='set the output directory for the compiled JavaScript',
metavar='DIR', type='str')
op.add_option('-i', '--interval', action='store', dest='interval',
type='int', default=POLL_INTERVAL, metavar='SECONDS',
help='set the poll interval in seconds (default: 2)')
opts, args = op.parse_args()
_process_args(args)
# create compiler with latest browser js coffeescript compiler from github
ct = v8.JSContext()
ct.enter()
compiler = ct.eval(urllib.urlopen(COMPILER_URL).read())
start_watching()