coyote 1.1.0 → 1.2.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +8 -0
- data/Guardfile +21 -0
- data/Manifest +73 -8
- data/README.md +110 -1
- data/Rakefile +15 -5
- data/bin/coyote +2 -1
- data/coyote.gemspec +12 -12
- data/lib/coyote.rb +7 -45
- data/lib/coyote/assets.rb +24 -0
- data/lib/coyote/assets/base.rb +33 -0
- data/lib/coyote/assets/coffeescript.rb +10 -16
- data/lib/coyote/assets/javascript.rb +4 -4
- data/lib/coyote/assets/less.rb +21 -16
- data/lib/coyote/bundles.rb +22 -0
- data/lib/coyote/bundles/base.rb +112 -0
- data/lib/coyote/bundles/css.rb +19 -0
- data/lib/coyote/bundles/javascript.rb +21 -0
- data/lib/coyote/cli.rb +4 -5
- data/lib/coyote/compiler.rb +46 -0
- data/lib/coyote/dsl.rb +29 -0
- data/lib/coyote/fs_listeners.rb +42 -0
- data/lib/coyote/fs_listeners/base.rb +46 -0
- data/lib/coyote/fs_listeners/darwin.rb +4 -4
- data/lib/coyote/fs_listeners/linux.rb +4 -4
- data/lib/coyote/fs_listeners/polling.rb +2 -2
- data/lib/coyote/fs_listeners/windows.rb +3 -3
- data/lib/coyote/notifier.rb +43 -0
- data/lib/coyote/rake.rb +15 -36
- data/lib/coyote/version.rb +3 -0
- data/spec/assets/asset/script.coffee +0 -0
- data/spec/assets/asset/script.js +0 -0
- data/spec/assets/asset/stylesheet.less +0 -0
- data/spec/assets/asset/weird.asdf +0 -0
- data/spec/assets/assets/coffeescript/script1.coffee +4 -0
- data/spec/assets/assets/coffeescript/script2.coffee +1 -0
- data/spec/assets/assets/coffeescript/script3.js +1 -0
- data/spec/assets/assets/coffeescript/script4.coffee +1 -0
- data/spec/assets/assets/javascript/script1.js +3 -0
- data/spec/assets/assets/javascript/script2.js +3 -0
- data/spec/assets/assets/javascript/script3.js +1 -0
- data/spec/assets/assets/javascript/script4.js +0 -0
- data/spec/assets/assets/javascript/script5.js +1 -0
- data/spec/assets/assets/javascript/scripts/script6.js +0 -0
- data/spec/assets/assets/javascript/scripts/script7.js +1 -0
- data/spec/assets/assets/javascript/scripts2/script8.js +0 -0
- data/spec/assets/assets/less/stylesheet1.less +5 -0
- data/spec/assets/assets/less/stylesheet2.less +3 -0
- data/spec/assets/assets/less/stylesheet3.less +0 -0
- data/spec/assets/assets/less/stylesheet4.less +0 -0
- data/spec/assets/assets/less/stylesheet5.css +0 -0
- data/spec/assets/bundle/script.js +0 -0
- data/spec/assets/bundle/stylesheet.css +0 -0
- data/spec/assets/bundle/weird.asdf +0 -0
- data/spec/assets/bundles/base/directory1/directory1_script.js +4 -0
- data/spec/assets/bundles/base/directory1/directory2/directory2_script.js +0 -0
- data/spec/assets/bundles/base/jquery.js +1 -0
- data/spec/assets/bundles/base/output.js +3 -0
- data/spec/assets/bundles/base/script1.js +3 -0
- data/spec/assets/bundles/base/script2.js +3 -0
- data/spec/assets/bundles/base/script3.js +1 -0
- data/spec/assets/bundles/base/script4.js +0 -0
- data/spec/assets/bundles/base/script5.js +3 -0
- data/spec/assets/bundles/base/script6.js +3 -0
- data/spec/assets/bundles/css/stylesheet1.css +0 -0
- data/spec/assets/bundles/css/weird_file.asdf +1 -0
- data/spec/assets/bundles/javascript/script1.js +0 -0
- data/spec/assets/bundles/javascript/weird_file.asdf +1 -0
- data/spec/assets/compiler/javascript/output.js +3 -0
- data/spec/assets/compiler/javascript/script3.js +3 -0
- data/spec/assets/compiler/javascript/script4.js +1 -0
- data/spec/assets/integration/javascript/output.js +3 -0
- data/spec/assets/integration/javascript/script1.js +3 -0
- data/spec/assets/integration/javascript/script2.js +1 -0
- data/spec/coyote/assets/base_spec.rb +54 -0
- data/spec/coyote/assets/coffeescript_spec.rb +20 -0
- data/spec/coyote/assets/less_spec.rb +22 -0
- data/spec/coyote/assets_spec.rb +27 -0
- data/spec/coyote/bundles/base_spec.rb +123 -0
- data/spec/coyote/bundles/css_spec.rb +17 -0
- data/spec/coyote/bundles/javascript_spec.rb +17 -0
- data/spec/coyote/bundles_spec.rb +26 -0
- data/spec/coyote/cli_spec.rb +1 -0
- data/spec/coyote/compiler_spec.rb +113 -0
- data/spec/coyote/dsl_spec.rb +21 -0
- data/spec/coyote/fs_listeners_spec.rb +38 -0
- data/spec/coyote/notifier_spec.rb +25 -0
- data/spec/coyote_spec.rb +21 -0
- data/spec/spec_helper.rb +6 -0
- metadata +108 -29
- data/test/scripts/input1.js +0 -1
- data/test/scripts/input2.js +0 -1
- data/test/scripts/input3.js +0 -1
- data/test/scripts/input4.js +0 -1
- data/test/scripts/lib/input5.coffee +0 -3
- data/test/scripts/lib/input6.js +0 -1
- data/test/scripts/lib/keystone/input7.js +0 -2
- data/test/scripts/vendor/input8.coffee +0 -1
@@ -1,22 +1,16 @@
|
|
1
|
-
module Coyote
|
2
|
-
class CoffeeScript <
|
1
|
+
module Coyote::Assets
|
2
|
+
class CoffeeScript < Base
|
3
|
+
require_pattern Regexp.new(/#=\s*require\s(.*)$/i)
|
4
|
+
|
3
5
|
def update!
|
4
6
|
super
|
5
7
|
compile!
|
6
8
|
end
|
7
|
-
|
8
|
-
protected
|
9
|
-
|
10
|
-
# Defines the regex pattern for scanning the contents of the
|
11
|
-
# file to look for require directives
|
12
|
-
def require_pattern
|
13
|
-
Regexp.new(/#=\s*require\s(.*)$/i) # '#= require a/b/c.coffee' => 'a/b/c.coffee'
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
9
|
+
|
18
10
|
def compile!
|
19
|
-
@contents = `cat #{@
|
11
|
+
@contents = `cat #{@path} | coffee -sc`
|
12
|
+
self
|
20
13
|
end
|
21
|
-
|
22
|
-
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module Coyote
|
2
|
-
class JavaScript <
|
3
|
-
end
|
4
|
-
end
|
1
|
+
module Coyote::Assets
|
2
|
+
class JavaScript < Base
|
3
|
+
end
|
4
|
+
end
|
data/lib/coyote/assets/less.rb
CHANGED
@@ -1,22 +1,27 @@
|
|
1
|
-
module Coyote
|
2
|
-
class Less <
|
3
|
-
|
4
|
-
|
5
|
-
compile!
|
6
|
-
end
|
1
|
+
module Coyote::Assets
|
2
|
+
class Less < Base
|
3
|
+
require_pattern Regexp.new(/@import\s*['|"](.*)['|"]\s*;$/i)
|
4
|
+
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
def find_dependencies
|
7
|
+
super
|
8
|
+
@dependencies.map! do |file|
|
9
|
+
path = File.expand_path(File.join(File.dirname(@relative_path), file))
|
10
|
+
if File.exists? path
|
11
|
+
file
|
12
|
+
elsif File.exists?(path + ".less")
|
13
|
+
file + ".less"
|
14
|
+
else
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end.compact!
|
14
18
|
end
|
15
19
|
|
16
|
-
private
|
17
20
|
|
18
21
|
def compile!
|
19
|
-
@contents = `lessc #{@
|
22
|
+
@contents = `lessc #{@path}`
|
23
|
+
self
|
20
24
|
end
|
21
|
-
|
22
|
-
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'coyote/bundles/base'
|
2
|
+
require 'coyote/bundles/javascript'
|
3
|
+
require 'coyote/bundles/css'
|
4
|
+
|
5
|
+
module Coyote
|
6
|
+
|
7
|
+
module Bundle
|
8
|
+
def self.new(path)
|
9
|
+
Coyote::Bundles.choose(path)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Bundles
|
14
|
+
def self.choose(path)
|
15
|
+
case File.extname(path)
|
16
|
+
when /\.js/i ; JavaScript.new(path)
|
17
|
+
when /\.css/i ; CSS.new(path)
|
18
|
+
else ; Base.new(path)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'coyote/assets'
|
2
|
+
|
3
|
+
module Coyote::Bundles
|
4
|
+
class Base
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def filetypes(*args)
|
8
|
+
@filetypes ||= args.map { |arg| arg.to_s.gsub('.','') } || []
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
filetypes :js, :coffee, :css, :less
|
13
|
+
|
14
|
+
attr_accessor :contents
|
15
|
+
attr_reader :assets, :target
|
16
|
+
|
17
|
+
def initialize(target)
|
18
|
+
@target = target
|
19
|
+
empty!
|
20
|
+
end
|
21
|
+
|
22
|
+
def add(input)
|
23
|
+
path = File.expand_path(input)
|
24
|
+
if File.directory? path
|
25
|
+
add_directory path
|
26
|
+
elsif File.exists? path
|
27
|
+
add_file path
|
28
|
+
else
|
29
|
+
notify "Could not find #{path}", :failure
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def add_file(path)
|
35
|
+
return false unless path_is_directory_or_kosher_file?(path)
|
36
|
+
asset = Coyote::Asset.new(path)
|
37
|
+
@files.delete(path)
|
38
|
+
@files << path
|
39
|
+
@assets[path] = asset
|
40
|
+
add_dependencies(asset)
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def add_directory(dir_path)
|
45
|
+
Dir.foreach(dir_path) do |path|
|
46
|
+
next if path == '.' or path == '..'
|
47
|
+
path = "#{dir_path}/#{path}"
|
48
|
+
add path if path_is_directory_or_kosher_file?(path)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def add_dependencies(asset)
|
54
|
+
asset.dependencies.each do |dependency_path|
|
55
|
+
relative_directory = File.dirname asset.relative_path
|
56
|
+
add File.join relative_directory, dependency_path
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def path_is_directory_or_kosher_file?(path)
|
62
|
+
return true if File.directory?(path)
|
63
|
+
self.class.filetypes.include? File.extname(path).gsub('.','')
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def empty!
|
68
|
+
@assets = {}
|
69
|
+
@files = []
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def files
|
74
|
+
@files ||= []
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def contents
|
79
|
+
@contents ||= files.reverse.map { |path| assets[path].contents }.join
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def update!(changed_files=[])
|
84
|
+
reset!
|
85
|
+
changed_files.each { |path| assets[path].update! }
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def manifest
|
90
|
+
files.reverse.map { |path| "+ #{path}" }.join("\n")
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
def reset!
|
95
|
+
@contents = nil
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
def save!
|
100
|
+
File.open target, 'w+' do |file|
|
101
|
+
file.write contents
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def compress!
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Coyote::Bundles
|
2
|
+
class CSS < Base
|
3
|
+
filetypes :css, :less
|
4
|
+
|
5
|
+
def compress!
|
6
|
+
notify "Compressing bundle...", :warning, :timestamp
|
7
|
+
tmp = "#{@target}.#{Time.now.to_i}.less"
|
8
|
+
File.open(tmp, 'w+') { |f| f.write contents }
|
9
|
+
self.contents = `lessc #{tmp} -x`
|
10
|
+
File.delete tmp
|
11
|
+
end
|
12
|
+
|
13
|
+
def contents
|
14
|
+
@contents ||= @assets[files.first].compile!.contents
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'coyote/closure_compiler'
|
2
|
+
|
3
|
+
module Coyote::Bundles
|
4
|
+
class JavaScript < Base
|
5
|
+
filetypes :js, :coffee
|
6
|
+
|
7
|
+
def compress!
|
8
|
+
notify "Compressing bundle...", :warning, :timestamp
|
9
|
+
compiler = Coyote::ClosureCompiler.new.compile(self.contents)
|
10
|
+
if compiler.success?
|
11
|
+
self.contents = compiler.compiled_code
|
12
|
+
elsif compiler.file_too_big?
|
13
|
+
notify "Input code too big for API, creating uncompressed file\n", :failure
|
14
|
+
elsif compiler.errors
|
15
|
+
notify "Google closure API failed to compile, creating uncompressed file\n", :failure
|
16
|
+
notify "Errors:", :failure
|
17
|
+
notify "#{compiler.errors.to_s}", :failure
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/coyote/cli.rb
CHANGED
@@ -1,18 +1,17 @@
|
|
1
|
-
require 'coyote'
|
2
1
|
require 'optparse'
|
3
2
|
|
4
3
|
options = {}
|
5
4
|
OptionParser.new do |opts|
|
6
|
-
|
5
|
+
['compress', 'watch', 'quiet', 'version'].each do |name|
|
7
6
|
opts.on("-#{name[0]}", "--#{name}", "#{name.capitalize}") { |o| options[name.to_sym] = o }
|
8
7
|
end
|
9
8
|
end.parse!
|
10
9
|
|
11
10
|
if options[:version]
|
12
|
-
|
11
|
+
notify "Coyote #{Coyote::VERSION}"
|
13
12
|
exit 0
|
14
13
|
end
|
15
14
|
|
16
|
-
input, output = ARGV
|
15
|
+
input, output = ARGV.fetch(0, "").split(':', 2)
|
17
16
|
|
18
|
-
Coyote
|
17
|
+
Coyote.compile(input, output, options)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'coyote/bundles'
|
2
|
+
require 'coyote/fs_listeners'
|
3
|
+
|
4
|
+
module Coyote
|
5
|
+
class Compiler
|
6
|
+
attr_reader :bundle
|
7
|
+
|
8
|
+
def initialize(input, output, options={})
|
9
|
+
@options = options || {}
|
10
|
+
@output = output
|
11
|
+
@bundle = Coyote::Bundle.new(output)
|
12
|
+
@bundle.add(input)
|
13
|
+
end
|
14
|
+
|
15
|
+
def compile!
|
16
|
+
save!
|
17
|
+
watch if @options.fetch(:watch, false)
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def save!
|
22
|
+
notify "\n" + @bundle.manifest unless @options.fetch(:quiet, false)
|
23
|
+
@bundle.compress! if @options.fetch(:compress, false)
|
24
|
+
@bundle.save!
|
25
|
+
notify "Saved bundle to #{@output} [#{@bundle.files.length} files]", :timestamp, :success
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def watch
|
30
|
+
listener = Coyote::FSListener.new
|
31
|
+
|
32
|
+
listener.on_change do |changed_files|
|
33
|
+
changed_files = @bundle.files & changed_files.map {|file| File.expand_path file }
|
34
|
+
|
35
|
+
if changed_files.length > 0
|
36
|
+
notify "Detected change, recompiling...", :warning, :timestamp
|
37
|
+
@bundle.update! changed_files
|
38
|
+
save!
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
notify "Watching for changes to your bundle. ctrl+c to stop.", :timestamp
|
43
|
+
listener.start
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/coyote/dsl.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'coyote'
|
2
|
+
|
3
|
+
module Coyote
|
4
|
+
class DSL
|
5
|
+
attr_accessor :input, :output, :options
|
6
|
+
|
7
|
+
def initialize(&block)
|
8
|
+
config = Struct.new(:input, :output, :options).new
|
9
|
+
config.options = {}
|
10
|
+
yield config if block_given?
|
11
|
+
instance_eval(&block)
|
12
|
+
@input = config.input
|
13
|
+
@output = config.output
|
14
|
+
@options = config.options
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
Coyote.compile(@input, @output, @options)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def coyote(*args, &block)
|
25
|
+
runner = Coyote::DSL.new(&block)
|
26
|
+
runner.run
|
27
|
+
end
|
28
|
+
|
29
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'coyote/fs_listeners/base'
|
2
|
+
require 'coyote/fs_listeners/darwin'
|
3
|
+
require 'coyote/fs_listeners/linux'
|
4
|
+
require 'coyote/fs_listeners/windows'
|
5
|
+
require 'coyote/fs_listeners/polling'
|
6
|
+
|
7
|
+
|
8
|
+
module Coyote
|
9
|
+
|
10
|
+
module FSListener
|
11
|
+
def self.new
|
12
|
+
Coyote::FSListeners.choose
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module FSListeners
|
17
|
+
def self.choose
|
18
|
+
if mac? && Darwin.usable?
|
19
|
+
Darwin.new
|
20
|
+
elsif linux? && Linux.usable?
|
21
|
+
Linux.new
|
22
|
+
elsif windows? && Windows.usable?
|
23
|
+
Windows.new
|
24
|
+
else
|
25
|
+
notify "Using polling (Please help us to support your system better than that.)", :failure
|
26
|
+
Polling.new
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.mac?
|
31
|
+
Config::CONFIG['target_os'] =~ /darwin/i
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.linux?
|
35
|
+
Config::CONFIG['target_os'] =~ /linux/i
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.windows?
|
39
|
+
Config::CONFIG['target_os'] =~ /mswin|mingw/i
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
3
|
+
module Coyote::FSListeners
|
4
|
+
class Base
|
5
|
+
attr_reader :last_event, :sha1_checksums_hash
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@sha1_checksums_hash = {}
|
9
|
+
update_last_event
|
10
|
+
end
|
11
|
+
|
12
|
+
def update_last_event
|
13
|
+
@last_event = Time.now
|
14
|
+
end
|
15
|
+
|
16
|
+
def modified_files(dirs, options = {})
|
17
|
+
files = potentially_modified_files(dirs, options).select { |path| File.file?(path) && file_modified?(path) && file_content_modified?(path) }
|
18
|
+
files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def potentially_modified_files(dirs, options = {})
|
24
|
+
match = options[:all] ? "**/*" : "*"
|
25
|
+
Dir.glob(dirs.map { |dir| "#{dir}#{match}" })
|
26
|
+
end
|
27
|
+
|
28
|
+
def file_modified?(path)
|
29
|
+
# Depending on the filesystem, mtime is probably only precise to the second, so round
|
30
|
+
# both values down to the second for the comparison.
|
31
|
+
File.mtime(path).to_i >= last_event.to_i
|
32
|
+
rescue
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
def file_content_modified?(path)
|
37
|
+
sha1_checksum = Digest::SHA1.file(path).to_s
|
38
|
+
if @sha1_checksums_hash[path] != sha1_checksum
|
39
|
+
@sha1_checksums_hash[path] = sha1_checksum
|
40
|
+
true
|
41
|
+
else
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|