moco 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.txt +67 -0
- data/Rakefile +15 -0
- data/bin/moco +6 -0
- data/lib/moco/ansi_escape.rb +37 -0
- data/lib/moco/application.rb +109 -0
- data/lib/moco/browser.rb +64 -0
- data/lib/moco/browser_error.rb +105 -0
- data/lib/moco/compile_error.rb +66 -0
- data/lib/moco/compiler.rb +151 -0
- data/lib/moco/compiler_option.rb +60 -0
- data/lib/moco/compiler_register.rb +29 -0
- data/lib/moco/compilers/coffee_compiler.rb +70 -0
- data/lib/moco/compilers/haml_compiler.rb +21 -0
- data/lib/moco/compilers/less_compiler.rb +15 -0
- data/lib/moco/compilers/markdown_compiler.rb +139 -0
- data/lib/moco/compilers/sass_compiler.rb +25 -0
- data/lib/moco/file_util.rb +54 -0
- data/lib/moco/log.rb +108 -0
- data/lib/moco/monitor.rb +83 -0
- data/lib/moco/options.rb +336 -0
- data/lib/moco/source_map.rb +22 -0
- data/lib/moco/support/error/error.css +22 -0
- data/lib/moco/support/error/error.html +7 -0
- data/lib/moco/support/error/error.js +58 -0
- data/lib/moco/support/reload.scpt +0 -0
- data/lib/moco.rb +44 -0
- data/moco.gemspec +35 -0
- data/moco.rb +35 -0
- data/src/error/error.coffee +49 -0
- data/src/reload.applescript +135 -0
- data/test/ansi_escape_test.rb +52 -0
- data/test/application_test.rb +40 -0
- data/test/browser_error_test.rb +101 -0
- data/test/browser_test.rb +29 -0
- data/test/compile_error_test.rb +82 -0
- data/test/compiler_option_test.rb +121 -0
- data/test/compiler_register_test.rb +41 -0
- data/test/compiler_test.rb +243 -0
- data/test/compilers/coffee_compiler_test.rb +117 -0
- data/test/compilers/haml_compiler_test.rb +86 -0
- data/test/compilers/less_compiler_test.rb +72 -0
- data/test/compilers/markdown_compiler_test.rb +211 -0
- data/test/compilers/sass_compiler_test.rb +84 -0
- data/test/file_util_test.rb +37 -0
- data/test/fixtures/_color.scss +1 -0
- data/test/fixtures/color.less +1 -0
- data/test/fixtures/css_lib.rb +2 -0
- data/test/fixtures/html_lib.rb +2 -0
- data/test/fixtures/js_lib.rb +2 -0
- data/test/fixtures/layout.html +13 -0
- data/test/fixtures/moco.rb +8 -0
- data/test/fixtures/options_lib.rb +2 -0
- data/test/fixtures/source.txt +1 -0
- data/test/monitor_test.rb +68 -0
- data/test/options_test.rb +177 -0
- data/test/test_helper.rb +57 -0
- metadata +270 -0
data/lib/moco/options.rb
ADDED
@@ -0,0 +1,336 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'shellwords'
|
3
|
+
|
4
|
+
module MoCo
|
5
|
+
|
6
|
+
class Options
|
7
|
+
|
8
|
+
def self.moco_files
|
9
|
+
files = ['~/.moco', '~/moco.rb', './.moco', './moco.rb']
|
10
|
+
files = files.map { |file| File.expand_path(file) }
|
11
|
+
files.select { |file| File.file?(file) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.args(args)
|
15
|
+
@args ||= []
|
16
|
+
@args += args.shellsplit
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse(args)
|
20
|
+
new.parse(args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse(command_line_args)
|
24
|
+
@options = {}
|
25
|
+
args = moco_file_args + command_line_args
|
26
|
+
display_help if args.empty?
|
27
|
+
option_parser.order(args) { |option| path(option) }
|
28
|
+
validate_options
|
29
|
+
default_options.merge(@options)
|
30
|
+
rescue OptionParser::ParseError => e
|
31
|
+
raise OptionError.new(e)
|
32
|
+
end
|
33
|
+
|
34
|
+
def option_parser
|
35
|
+
OptionParser.new do |op|
|
36
|
+
op.version = MoCo::VERSION
|
37
|
+
op.on( '--[no-]monitor', method(:monitor))
|
38
|
+
op.on('-c', '--[no-]compile [EXT,EXT]', method(:compile), Array)
|
39
|
+
op.on('-f', '--[no-]force', method(:force))
|
40
|
+
op.on('-m', '--[no-]source-map', method(:source_map))
|
41
|
+
op.on('-o', '--option EXT:KEY:VAL', method(:compiler_option))
|
42
|
+
op.on('-r', '--[no-]reload [EXT,EXT]', method(:reload), Array)
|
43
|
+
op.on('-b', '--browser BRO,BRO', method(:browsers), Array)
|
44
|
+
op.on('-u', '--url URL,URL', method(:urls), Array)
|
45
|
+
op.on( '--require LIB', method(:require_lib))
|
46
|
+
op.on('-q', '--[no-]quiet', method(:quiet))
|
47
|
+
op.on('-l', '--list', method(:display_list))
|
48
|
+
op.on('-h', '--help', method(:display_help))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def default_options
|
53
|
+
{
|
54
|
+
:monitor => true,
|
55
|
+
:compile => true,
|
56
|
+
:compile_exts => MoCo.compilers.keys.sort,
|
57
|
+
:force => false,
|
58
|
+
:source_map => false,
|
59
|
+
:reload => true,
|
60
|
+
:reload_exts => Browser.extensions,
|
61
|
+
:browsers => Browser.browsers,
|
62
|
+
:urls => Browser.localhost,
|
63
|
+
:quiet => false,
|
64
|
+
:source_files => [],
|
65
|
+
:source_dirs => [],
|
66
|
+
:compiled_files => {},
|
67
|
+
:compiled_dirs => {}
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def help
|
72
|
+
<<-EOF.gsub(/^ {8}/, '').gsub(/`(.+?)`/, AnsiEscape.green('\1'))
|
73
|
+
Usage:
|
74
|
+
`moco [options] SOURCE ...`
|
75
|
+
`moco [options] SOURCE:COMPILED ...`
|
76
|
+
|
77
|
+
Description:
|
78
|
+
MoCo monitors web templates. On updates the templates are compiled and
|
79
|
+
the browser reloaded. MoCo currently supports CoffeeScript, Sass, LESS,
|
80
|
+
Markdown and Haml.
|
81
|
+
|
82
|
+
Files and directories:
|
83
|
+
The given source files and directories will be monitored for updates.
|
84
|
+
Use the SOURCE:COMPILED format to save the compiled files to another
|
85
|
+
directory or to change the compiled filename:
|
86
|
+
`moco .:/www sass:/www/css README.md:/www/index.html`
|
87
|
+
|
88
|
+
Options:
|
89
|
+
--monitor Keep running until Ctrl-C is pressed [DEFAULT]
|
90
|
+
--no-monitor Exit after the initial compilation
|
91
|
+
|
92
|
+
-c, --compile Compile all the supported file types [DEFAULT]
|
93
|
+
-c, --compile EXT,EXT Compile the given file types
|
94
|
+
--no-compile Disable compilation
|
95
|
+
`moco -c coffee -c sass,scss .`
|
96
|
+
|
97
|
+
-f, --force Force recompilation at startup
|
98
|
+
--no-force Do not compile up-to-date files [DEFAULT]
|
99
|
+
|
100
|
+
-m, --source-map Make source maps if the compiler supports it
|
101
|
+
--no-source-map Do not make source maps [DEFAULT]
|
102
|
+
|
103
|
+
-o, --option EXT:KEY:VAL Set a compiler option
|
104
|
+
`moco -o coffee:header` # header = true
|
105
|
+
` -o haml:ugly:false` # ugly = false
|
106
|
+
` -o haml:format::xhtml` # format = :xhtml
|
107
|
+
` -o md:layout:md.html` # layout = 'md.html'
|
108
|
+
` -o less:paths:css: .` # paths = ['css']
|
109
|
+
|
110
|
+
-r, --reload Reload after css/html/js file updates [DEFAULT]
|
111
|
+
-r, --reload EXT,EXT Set the file types that triggers reloading
|
112
|
+
--no-reload Disable reloading
|
113
|
+
`moco -r rb -r css,html,js .`
|
114
|
+
|
115
|
+
-b, --browser BRO,BRO The browsers to reload [all by DEFAULT]
|
116
|
+
`moco -b safari -b chrome,canary .`
|
117
|
+
|
118
|
+
-u, --url all Reload all active tabs
|
119
|
+
-u, --url localhost Reload active tabs with localhost urls [DEFAULT]
|
120
|
+
-u, --url URL,URL Reload active tabs where the url starts with URL
|
121
|
+
`moco -u localhost -u http://app.dev/ .`
|
122
|
+
|
123
|
+
--require LIB Require the library
|
124
|
+
`moco --require path/to/compiler.rb .`
|
125
|
+
|
126
|
+
-q, --quiet Log errors only
|
127
|
+
--no-quiet Log errors and file updates [DEFAULT]
|
128
|
+
|
129
|
+
-l, --list List the supported file types and browsers
|
130
|
+
|
131
|
+
-h, --help Display this message
|
132
|
+
|
133
|
+
The moco file:
|
134
|
+
MoCo looks for files named '.moco' and 'moco.rb' in the working directory
|
135
|
+
and in the home directory. The purpose of these files is to set options
|
136
|
+
and to define new compilers. The command line options have precedence.
|
137
|
+
|
138
|
+
More information:
|
139
|
+
https://github.com/asharbitz/moco#readme
|
140
|
+
EOF
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def moco_file_args
|
146
|
+
load_moco_files
|
147
|
+
Options.instance_variable_get(:@args) || []
|
148
|
+
end
|
149
|
+
|
150
|
+
def load_moco_files
|
151
|
+
Options.moco_files.each do |file|
|
152
|
+
load file
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
[:monitor, :force, :browsers, :urls].each do |key|
|
157
|
+
define_method(key) do |option|
|
158
|
+
set_option(key, option)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
[:compile, :reload].each do |key|
|
163
|
+
define_method(key) do |option|
|
164
|
+
case option
|
165
|
+
when Array
|
166
|
+
option.map! { |ext| FileUtil.normalized_extension(ext) }
|
167
|
+
set_option("#{key}_exts".to_sym, option)
|
168
|
+
set_option(key, true)
|
169
|
+
when nil
|
170
|
+
set_option(key, true)
|
171
|
+
else
|
172
|
+
set_option(key, option)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def set_option(key, option)
|
178
|
+
case option
|
179
|
+
when Array
|
180
|
+
@options[key] ||= []
|
181
|
+
@options[key] += option
|
182
|
+
@options[key].uniq!
|
183
|
+
when Hash
|
184
|
+
@options[key] ||= {}
|
185
|
+
@options[key].merge!(option)
|
186
|
+
else
|
187
|
+
@options[key] = option
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def compiler_option(option)
|
192
|
+
ext, key, value = option.split(':', 3)
|
193
|
+
compiler = MoCo.compiler_for(ext)
|
194
|
+
raise OptionError.new(:missing_option_key) unless key
|
195
|
+
raise OptionError.new(:invalid_extension, ext) unless compiler
|
196
|
+
key, value = compiler.convert_option(key, value)
|
197
|
+
compiler.set_option(key, value)
|
198
|
+
end
|
199
|
+
|
200
|
+
def source_map(option)
|
201
|
+
MoCo.compilers.each_value do |compiler|
|
202
|
+
if compiler < SourceMap
|
203
|
+
compiler.set_option(compiler.source_map_key, option)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def require_lib(option)
|
209
|
+
require option
|
210
|
+
rescue LoadError
|
211
|
+
require File.expand_path(option)
|
212
|
+
end
|
213
|
+
|
214
|
+
def quiet(option)
|
215
|
+
set_option(:quiet, option)
|
216
|
+
SassCompiler.set_option(:quiet, option)
|
217
|
+
end
|
218
|
+
|
219
|
+
def display_list(option)
|
220
|
+
display(list)
|
221
|
+
end
|
222
|
+
|
223
|
+
def display_help(option = nil)
|
224
|
+
display(help)
|
225
|
+
end
|
226
|
+
|
227
|
+
def display(text)
|
228
|
+
puts
|
229
|
+
puts text
|
230
|
+
puts
|
231
|
+
exit 0
|
232
|
+
end
|
233
|
+
|
234
|
+
def list
|
235
|
+
[ ['Compile:', :compile_exts],
|
236
|
+
['Reload:', :reload_exts],
|
237
|
+
['Browsers:', :browsers],
|
238
|
+
['Localhost:', :urls]
|
239
|
+
].map do |header, key|
|
240
|
+
[header, *default_options[key]].join("\n ")
|
241
|
+
end.join("\n\n")
|
242
|
+
end
|
243
|
+
|
244
|
+
def path(option)
|
245
|
+
source, compiled = option.split(':')
|
246
|
+
source = File.expand_path(source)
|
247
|
+
compiled = File.expand_path(compiled) if compiled
|
248
|
+
if File.directory?(source)
|
249
|
+
set_option(:source_dirs, [source])
|
250
|
+
set_option(:compiled_dirs, source => compiled)
|
251
|
+
elsif File.file?(source)
|
252
|
+
set_option(:source_files, [source])
|
253
|
+
set_option(:compiled_files, source => compiled)
|
254
|
+
else
|
255
|
+
raise OptionError.new(:invalid_file, source)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def validate_options
|
260
|
+
validate_files
|
261
|
+
validate_compilers
|
262
|
+
validate_browsers
|
263
|
+
end
|
264
|
+
|
265
|
+
def validate_files
|
266
|
+
unless @options[:source_dirs] || @options[:source_files]
|
267
|
+
raise OptionError.new(:missing_file)
|
268
|
+
end
|
269
|
+
(@options[:compiled_dirs] || {}).each_value do |dir|
|
270
|
+
if dir && File.file?(dir)
|
271
|
+
raise OptionError.new(:dir_expected, dir)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
(@options[:compiled_files] || {}).each_value do |file|
|
275
|
+
if file && File.directory?(file)
|
276
|
+
raise OptionError.new(:file_expected, file)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def validate_compilers
|
282
|
+
(@options[:compile_exts] || []).each do |ext|
|
283
|
+
unless MoCo.compiler_for(ext)
|
284
|
+
raise OptionError.new(:invalid_extension, ext)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def validate_browsers
|
290
|
+
supported_browsers = Browser.browsers.map(&:downcase)
|
291
|
+
(@options[:browsers] || []).each do |browser|
|
292
|
+
unless supported_browsers.include?(browser.downcase)
|
293
|
+
raise OptionError.new(:invalid_browser, browser)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
end
|
299
|
+
|
300
|
+
class OptionError < Error
|
301
|
+
|
302
|
+
def initialize(error, *args)
|
303
|
+
super(error_message(error) % args)
|
304
|
+
end
|
305
|
+
|
306
|
+
private
|
307
|
+
|
308
|
+
def error_message(error)
|
309
|
+
case error
|
310
|
+
when :missing_file
|
311
|
+
[ 'No directory or file provided. ' +
|
312
|
+
'To monitor files in the current directory:',
|
313
|
+
'moco .' ]
|
314
|
+
when :invalid_file
|
315
|
+
[ "No such file or directory: '%s'" ]
|
316
|
+
when :dir_expected
|
317
|
+
[ "Expected a directory, but got a filename: '%s'" ]
|
318
|
+
when :file_expected
|
319
|
+
[ "Expected a filename, but got a directory: '%s'" ]
|
320
|
+
when :invalid_extension
|
321
|
+
[ "No compiler registered for '%s' files. The supported file types are:",
|
322
|
+
MoCo.compilers.keys.sort.join(' ') ]
|
323
|
+
when :missing_option_key
|
324
|
+
[ 'The option key is missing. Set compiler options like this:',
|
325
|
+
'-o coffee:header -o haml:format::xhtml' ]
|
326
|
+
when :invalid_browser
|
327
|
+
[ "Unknown browser '%s'. The supported browsers are:",
|
328
|
+
Browser.browsers.join(' ') ]
|
329
|
+
when Exception
|
330
|
+
[ error.message ]
|
331
|
+
end.join("\n")
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module MoCo
|
2
|
+
|
3
|
+
module SourceMap
|
4
|
+
|
5
|
+
def self.source_map_key
|
6
|
+
raise NotImplementedError
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :source_map_text
|
10
|
+
|
11
|
+
def compile
|
12
|
+
super
|
13
|
+
write_file(source_map_file, source_map_text) if source_map_text
|
14
|
+
end
|
15
|
+
|
16
|
+
def source_map_file
|
17
|
+
compiled_file + '.map'
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
body:before {
|
2
|
+
content: "%s";
|
3
|
+
white-space: pre-wrap;
|
4
|
+
font-family: Menlo, Monaco, monospace;
|
5
|
+
font-size: 14px;
|
6
|
+
line-height: 1.5;
|
7
|
+
background: white;
|
8
|
+
border: 3px solid red;
|
9
|
+
border-radius: 5px;
|
10
|
+
box-shadow: 5px 5px 15px #CCC;
|
11
|
+
display: block;
|
12
|
+
position: fixed;
|
13
|
+
top: 0;
|
14
|
+
left: 0;
|
15
|
+
padding: 20px;
|
16
|
+
margin: 20px;
|
17
|
+
z-index: 10000;
|
18
|
+
}
|
19
|
+
|
20
|
+
body:hover:before {
|
21
|
+
z-index: 10001;
|
22
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
// Generated by CoffeeScript 1.6.2
|
2
|
+
(function() {
|
3
|
+
var MoCo;
|
4
|
+
|
5
|
+
MoCo = {};
|
6
|
+
|
7
|
+
MoCo.errorMessage = "%s";
|
8
|
+
|
9
|
+
MoCo.css = '\
|
10
|
+
.moco-compile-error {\
|
11
|
+
white-space: pre-wrap;\
|
12
|
+
font-family: Menlo, Monaco, monospace;\
|
13
|
+
font-size: 14px;\
|
14
|
+
line-height: 1.5;\
|
15
|
+
color: black;\
|
16
|
+
background: white;\
|
17
|
+
border: 3px solid red;\
|
18
|
+
border-radius: 5px;\
|
19
|
+
box-shadow: 5px 5px 15px #CCC;\
|
20
|
+
display: block;\
|
21
|
+
position: fixed;\
|
22
|
+
top: 0;\
|
23
|
+
left: 0;\
|
24
|
+
padding: 20px;\
|
25
|
+
margin: 20px;\
|
26
|
+
z-index: 10000;\
|
27
|
+
}\
|
28
|
+
.moco-compile-error:hover {\
|
29
|
+
z-index: 10001;\
|
30
|
+
}\
|
31
|
+
.moco-compile-error span {\
|
32
|
+
color: red;\
|
33
|
+
}\
|
34
|
+
.moco-compile-error a, .moco-compile-error a:visited {\
|
35
|
+
color: black;\
|
36
|
+
text-decoration: none;\
|
37
|
+
}\
|
38
|
+
.moco-compile-error a:hover, .moco-compile-error a:active {\
|
39
|
+
color: red;\
|
40
|
+
text-decoration: none;\
|
41
|
+
}\
|
42
|
+
';
|
43
|
+
|
44
|
+
MoCo.appendErrorElement = function() {
|
45
|
+
var pre, style;
|
46
|
+
|
47
|
+
style = document.createElement('style');
|
48
|
+
style.innerHTML = MoCo.css;
|
49
|
+
pre = document.createElement('pre');
|
50
|
+
pre.innerHTML = MoCo.errorMessage;
|
51
|
+
pre.className = 'moco-compile-error';
|
52
|
+
document.body.appendChild(style);
|
53
|
+
return document.body.appendChild(pre);
|
54
|
+
};
|
55
|
+
|
56
|
+
window.addEventListener('load', MoCo.appendErrorElement, false);
|
57
|
+
|
58
|
+
}).call(this);
|
Binary file
|
data/lib/moco.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module MoCo
|
2
|
+
|
3
|
+
VERSION = '0.1.0'
|
4
|
+
|
5
|
+
class Error < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.compilers
|
9
|
+
CompilerRegister.instance.compilers
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.register(compiler, extension)
|
13
|
+
CompilerRegister.instance.register(compiler, extension)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.compiler_for(file)
|
17
|
+
CompilerRegister.instance.compiler_for(file)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.args(args)
|
21
|
+
Options.args(args)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'moco/ansi_escape'
|
27
|
+
require 'moco/application'
|
28
|
+
require 'moco/browser_error'
|
29
|
+
require 'moco/browser'
|
30
|
+
require 'moco/compile_error'
|
31
|
+
require 'moco/compiler_option'
|
32
|
+
require 'moco/compiler_register'
|
33
|
+
require 'moco/compiler'
|
34
|
+
require 'moco/file_util'
|
35
|
+
require 'moco/log'
|
36
|
+
require 'moco/monitor'
|
37
|
+
require 'moco/options'
|
38
|
+
require 'moco/source_map'
|
39
|
+
|
40
|
+
require 'moco/compilers/coffee_compiler'
|
41
|
+
require 'moco/compilers/haml_compiler'
|
42
|
+
require 'moco/compilers/less_compiler'
|
43
|
+
require 'moco/compilers/markdown_compiler'
|
44
|
+
require 'moco/compilers/sass_compiler'
|
data/moco.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = 'moco'
|
3
|
+
spec.version = '0.1.0'
|
4
|
+
spec.license = 'MIT'
|
5
|
+
|
6
|
+
spec.author = 'AS Harbitz'
|
7
|
+
spec.email = 'asharbitz@gmail.com'
|
8
|
+
spec.homepage = 'https://github.com/asharbitz/moco'
|
9
|
+
|
10
|
+
spec.summary = 'Monitors and compiles web templates. Reloads the browser.'
|
11
|
+
spec.description = 'MoCo monitors web templates. On updates the templates ' +
|
12
|
+
'are compiled and the browser reloaded. MoCo currently ' +
|
13
|
+
'supports CoffeeScript, Sass, LESS, Markdown and Haml. ' +
|
14
|
+
'Mac OS X only.'
|
15
|
+
|
16
|
+
spec.files = Dir['LICENSE', 'moco.gemspec', 'moco.rb', 'Rakefile',
|
17
|
+
'README.txt', '{lib,src}/**/*.*']
|
18
|
+
spec.test_files = Dir['test/**/*.*']
|
19
|
+
spec.executables << 'moco'
|
20
|
+
|
21
|
+
spec.add_dependency 'rb-fsevent', '~> 0.9'
|
22
|
+
|
23
|
+
spec.add_development_dependency 'minitest', '~> 4.0'
|
24
|
+
spec.add_development_dependency 'coffee-script-source', '>= 1.6.2'
|
25
|
+
spec.add_development_dependency 'runjs'
|
26
|
+
spec.add_development_dependency 'haml'
|
27
|
+
spec.add_development_dependency 'less'
|
28
|
+
spec.add_development_dependency 'therubyracer'
|
29
|
+
spec.add_development_dependency 'redcarpet'
|
30
|
+
spec.add_development_dependency 'pygments.rb'
|
31
|
+
spec.add_development_dependency 'sass'
|
32
|
+
|
33
|
+
spec.required_ruby_version = '>= 1.8.7'
|
34
|
+
spec.requirements << 'Mac OS X'
|
35
|
+
end
|
data/moco.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
MoCo.args '--option coffee:header src:lib/moco/support'
|
2
|
+
|
3
|
+
class AppleScriptCompiler < MoCo::Compiler
|
4
|
+
|
5
|
+
require_library 'open3'
|
6
|
+
register 'applescript'
|
7
|
+
|
8
|
+
def self.compiled_extension
|
9
|
+
'scpt'
|
10
|
+
end
|
11
|
+
|
12
|
+
def compile
|
13
|
+
cmd = ['osacompile', '-o', compiled_file, source_file]
|
14
|
+
message = Open3.popen3(*cmd) { |_, _, stderr| stderr.read.strip }
|
15
|
+
unless message.empty?
|
16
|
+
error = StandardError.new(message)
|
17
|
+
error = MoCo::CompileError.new(error, source_file)
|
18
|
+
write_compiled(error_text(error))
|
19
|
+
raise error
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def error_text(error)
|
24
|
+
applescript = <<-EOF
|
25
|
+
tell application "AppleScript Runner"
|
26
|
+
display alert "%s" giving up after 10
|
27
|
+
return
|
28
|
+
end
|
29
|
+
EOF
|
30
|
+
message = "File: #{error.file}\n\nLine: #{error.line}\n\n#{error.message}"
|
31
|
+
message = message.gsub('\\') { '\\\\' }.gsub('"', '\"')
|
32
|
+
applescript % message
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
MoCo = {}
|
2
|
+
|
3
|
+
MoCo.errorMessage = "%s"
|
4
|
+
|
5
|
+
MoCo.css = '
|
6
|
+
.moco-compile-error {
|
7
|
+
white-space: pre-wrap;
|
8
|
+
font-family: Menlo, Monaco, monospace;
|
9
|
+
font-size: 14px;
|
10
|
+
line-height: 1.5;
|
11
|
+
color: black;
|
12
|
+
background: white;
|
13
|
+
border: 3px solid red;
|
14
|
+
border-radius: 5px;
|
15
|
+
box-shadow: 5px 5px 15px #CCC;
|
16
|
+
display: block;
|
17
|
+
position: fixed;
|
18
|
+
top: 0;
|
19
|
+
left: 0;
|
20
|
+
padding: 20px;
|
21
|
+
margin: 20px;
|
22
|
+
z-index: 10000;
|
23
|
+
}
|
24
|
+
.moco-compile-error:hover {
|
25
|
+
z-index: 10001;
|
26
|
+
}
|
27
|
+
.moco-compile-error span {
|
28
|
+
color: red;
|
29
|
+
}
|
30
|
+
.moco-compile-error a, .moco-compile-error a:visited {
|
31
|
+
color: black;
|
32
|
+
text-decoration: none;
|
33
|
+
}
|
34
|
+
.moco-compile-error a:hover, .moco-compile-error a:active {
|
35
|
+
color: red;
|
36
|
+
text-decoration: none;
|
37
|
+
}
|
38
|
+
'
|
39
|
+
|
40
|
+
MoCo.appendErrorElement = ->
|
41
|
+
style = document.createElement('style')
|
42
|
+
style.innerHTML = MoCo.css
|
43
|
+
pre = document.createElement('pre')
|
44
|
+
pre.innerHTML = MoCo.errorMessage
|
45
|
+
pre.className = 'moco-compile-error'
|
46
|
+
document.body.appendChild(style)
|
47
|
+
document.body.appendChild(pre)
|
48
|
+
|
49
|
+
window.addEventListener('load', MoCo.appendErrorElement, false)
|