distil 0.10.4 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -1
- data/VERSION +1 -1
- data/assets/distil.js +359 -0
- data/bin/distil +33 -10
- data/distil.gemspec +35 -24
- data/lib/distil/configurable/file-set.rb +86 -0
- data/lib/distil/configurable/interpolated.rb +36 -0
- data/lib/distil/configurable/output-path.rb +25 -0
- data/lib/distil/configurable/project-path.rb +25 -0
- data/lib/distil/configurable.rb +164 -0
- data/lib/distil/error-reporter.rb +63 -0
- data/lib/distil/product/concatenated.rb +85 -0
- data/lib/distil/product/css-product.rb +37 -0
- data/lib/distil/product/debug.rb +34 -0
- data/lib/distil/product/javascript-base-product.rb +35 -0
- data/lib/distil/product/javascript-doc-product.rb +61 -0
- data/lib/distil/product/javascript-product.rb +131 -0
- data/lib/distil/product/minified.rb +32 -0
- data/lib/distil/product.rb +97 -0
- data/lib/distil/project/distil-project.rb +99 -0
- data/lib/distil/project/external-project.rb +53 -0
- data/lib/distil/project.rb +78 -0
- data/lib/distil/source-file/css-file.rb +14 -0
- data/lib/distil/source-file/html-file.rb +14 -0
- data/lib/distil/source-file/javascript-file.rb +17 -0
- data/lib/distil/source-file/json-file.rb +16 -0
- data/lib/distil/source-file.rb +172 -0
- data/lib/distil/target.rb +235 -0
- data/lib/distil/task/css-dependency-task.rb +64 -0
- data/lib/distil/task/jsl-dependency-task.rb +49 -0
- data/lib/distil/task/nib-task.rb +72 -0
- data/lib/distil/task/validate-js-task.rb +75 -0
- data/lib/distil/task.rb +50 -0
- data/lib/distil.rb +72 -0
- data/lib/jsl.conf +4 -0
- data/vendor/jsl-0.3.0/src/Makefile.ref +16 -6
- data/vendor/jsl-0.3.0/src/config.mk +32 -2
- data/vendor/jsl-0.3.0/src/fdlibm/Makefile.ref +1 -2
- data/vendor/jsl-0.3.0/src/jsl.c +124 -13
- data/vendor/jsl-0.3.0/src/rules.mk +2 -1
- metadata +49 -28
- data/lib/bootstrap-template.js +0 -58
- data/lib/configurable.rb +0 -161
- data/lib/file-set.rb +0 -49
- data/lib/file-types/css-file.rb +0 -12
- data/lib/file-types/html-file.rb +0 -11
- data/lib/file-types/javascript-file.rb +0 -24
- data/lib/file-types/json-file.rb +0 -17
- data/lib/filter.rb +0 -41
- data/lib/filters/css-filter.rb +0 -58
- data/lib/filters/file-reference-filter.rb +0 -30
- data/lib/filters/jsl-dependency-filter.rb +0 -44
- data/lib/project.rb +0 -174
- data/lib/source-file.rb +0 -197
- data/lib/target.rb +0 -102
- data/lib/task.rb +0 -212
- data/lib/tasks/copy-task.rb +0 -17
- data/lib/tasks/css-task.rb +0 -12
- data/lib/tasks/javascript-task.rb +0 -206
- data/lib/tasks/multiple-output-task.rb +0 -140
- data/lib/tasks/output-task.rb +0 -76
- data/lib/tasks/single-output-task.rb +0 -104
- data/lib/tasks/test-task.rb +0 -280
@@ -0,0 +1,97 @@
|
|
1
|
+
module Distil
|
2
|
+
|
3
|
+
class Product < Configurable
|
4
|
+
option :concatenated_name, OutputPath, "$(name)-uncompressed.$(extension)", :aliases=>['concatenated']
|
5
|
+
option :debug_name, OutputPath, "$(name)-debug.$(extension)", :aliases=>['debug']
|
6
|
+
option :minified_name, OutputPath, "$(name).$(extension)", :aliases=>['minified']
|
7
|
+
option :compressed_name, OutputPath, "$(name).$(extension).gz", :aliases=>['compressed']
|
8
|
+
|
9
|
+
option :force, false
|
10
|
+
|
11
|
+
attr_accessor :assets, :target, :join_string
|
12
|
+
class_attr :extension
|
13
|
+
|
14
|
+
def initialize(settings, target)
|
15
|
+
@target= target
|
16
|
+
@files= []
|
17
|
+
@assets= Set.new
|
18
|
+
super(settings, target)
|
19
|
+
end
|
20
|
+
|
21
|
+
def can_embed_file?(file)
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def handles_file?(file)
|
26
|
+
[extension].include?(file.extension)
|
27
|
+
end
|
28
|
+
|
29
|
+
def files
|
30
|
+
@files
|
31
|
+
end
|
32
|
+
|
33
|
+
def files=(fileset)
|
34
|
+
fileset.each { |f|
|
35
|
+
next if !handles_file?(f)
|
36
|
+
@files << f
|
37
|
+
@assets.merge(f.assets)
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def up_to_date
|
42
|
+
return @up_to_date if !@up_to_date.nil?
|
43
|
+
return false if force
|
44
|
+
|
45
|
+
return @up_to_date=false if !File.exists?(filename)
|
46
|
+
|
47
|
+
output_modified= File.stat(filename).mtime
|
48
|
+
max_asset_modified= File.stat(target.project.project_file).mtime
|
49
|
+
|
50
|
+
assets.each { |f|
|
51
|
+
max_asset_modified= f.last_modified if f.last_modified > max_asset_modified
|
52
|
+
}
|
53
|
+
|
54
|
+
return @up_to_date=false if (output_modified < max_asset_modified)
|
55
|
+
|
56
|
+
external_files.each { |f|
|
57
|
+
next if !File.exist?(f)
|
58
|
+
last_modified= File.stat(f).mtime
|
59
|
+
max_asset_modified= last_modified if last_modified > max_asset_modified
|
60
|
+
}
|
61
|
+
|
62
|
+
return @up_to_date=false if (output_modified < max_asset_modified)
|
63
|
+
|
64
|
+
@up_to_date= true
|
65
|
+
end
|
66
|
+
|
67
|
+
def filename
|
68
|
+
raise NotImplementedError.new("This product does not implement the filename method.")
|
69
|
+
end
|
70
|
+
|
71
|
+
def write_output
|
72
|
+
raise NotImplementedError.new("No write_output method has been defined for this product.")
|
73
|
+
end
|
74
|
+
|
75
|
+
def relative_path(file)
|
76
|
+
file=SourceFile.from_path(file) if file.is_a?(String)
|
77
|
+
|
78
|
+
file_path= file.full_path
|
79
|
+
output_folder= target.project.output_folder
|
80
|
+
source_folder= target.project.source_folder
|
81
|
+
|
82
|
+
path=file.relative_to_folder(source_folder) if 0==file_path.index(source_folder)
|
83
|
+
path=file.relative_to_folder(output_folder) if 0==file_path.index(output_folder)
|
84
|
+
path
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
require 'distil/product/concatenated'
|
92
|
+
require 'distil/product/debug'
|
93
|
+
require 'distil/product/minified'
|
94
|
+
require 'distil/product/css-product'
|
95
|
+
require 'distil/product/javascript-base-product'
|
96
|
+
require 'distil/product/javascript-product'
|
97
|
+
require 'distil/product/javascript-doc-product'
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Distil
|
2
|
+
|
3
|
+
class DistilProject < Project
|
4
|
+
|
5
|
+
attr_reader :project_file
|
6
|
+
|
7
|
+
option :ignore_warnings, false
|
8
|
+
|
9
|
+
option :external_projects, [], :aliases=>['external']
|
10
|
+
option :distileries, Array, :aliases=>['distilleries', 'distilery', 'distillery']
|
11
|
+
|
12
|
+
|
13
|
+
def initialize(project_file, settings={}, parent=nil)
|
14
|
+
@project_file= File.expand_path(project_file)
|
15
|
+
@projects_by_name={}
|
16
|
+
|
17
|
+
project_info= YAML.load_file(@project_file)
|
18
|
+
project_info.merge!(settings)
|
19
|
+
project_info["path"]= File.dirname(@project_file)
|
20
|
+
|
21
|
+
begin
|
22
|
+
super(project_info, parent)
|
23
|
+
rescue ValidationError
|
24
|
+
$stderr.print "#{APP_NAME}: #{project_file}: #{$!}\n"
|
25
|
+
exit 1
|
26
|
+
end
|
27
|
+
|
28
|
+
load_external_projects
|
29
|
+
end
|
30
|
+
|
31
|
+
def targets
|
32
|
+
@targets if @targets
|
33
|
+
|
34
|
+
@targets= []
|
35
|
+
target_list= @extras['targets']
|
36
|
+
|
37
|
+
if !target_list
|
38
|
+
@targets << Target.new(@extras.clone, self)
|
39
|
+
return @targets
|
40
|
+
end
|
41
|
+
|
42
|
+
@targets= target_list.map { |target|
|
43
|
+
Target.new(target, self)
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def load_external_projects
|
48
|
+
return if !external_projects
|
49
|
+
|
50
|
+
self.external_projects= external_projects.map { |config|
|
51
|
+
project= Project.from_config(config, self)
|
52
|
+
next if !project
|
53
|
+
@projects_by_name[project.name]= project
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def external_project_with_name(name)
|
58
|
+
@projects_by_name[name]
|
59
|
+
end
|
60
|
+
|
61
|
+
def build
|
62
|
+
FileUtils.mkdir_p(output_folder)
|
63
|
+
load_distileries
|
64
|
+
build_external_projects
|
65
|
+
build_targets
|
66
|
+
end
|
67
|
+
|
68
|
+
def load_distileries
|
69
|
+
return if distileries.nil?
|
70
|
+
|
71
|
+
distileries.each { |d|
|
72
|
+
if (File.exists?(d))
|
73
|
+
require d
|
74
|
+
next
|
75
|
+
end
|
76
|
+
path= Gem.required_location(d, 'distilery.rb')
|
77
|
+
if (path.nil?)
|
78
|
+
puts "Missing distilery: #{d}"
|
79
|
+
end
|
80
|
+
next if path.nil?
|
81
|
+
require path
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_external_projects
|
86
|
+
external_projects.each { |project|
|
87
|
+
project.build
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
def build_targets
|
92
|
+
targets.each { |target|
|
93
|
+
target.build
|
94
|
+
}
|
95
|
+
report
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Distil
|
2
|
+
|
3
|
+
class ExternalProject < Project
|
4
|
+
|
5
|
+
option :name, String
|
6
|
+
option :repository
|
7
|
+
option :build_command, String, :aliases=>['build']
|
8
|
+
option :linkage, WEAK_LINKAGE, :valid_values=> [WEAK_LINKAGE, STRONG_LINKAGE, LAZY_LINKAGE]
|
9
|
+
|
10
|
+
option :import_name, OutputPath, "$(name)-debug.$(extension)", :aliases=>['import']
|
11
|
+
option :concatenated_name, OutputPath, "$(name)-uncompressed.$(extension)", :aliases=>['concatenated']
|
12
|
+
option :debug_name, OutputPath, "$(name)-debug.$(extension)", :aliases=>['debug']
|
13
|
+
option :minified_name, OutputPath, "$(name).$(extension)", :aliases=>['minified']
|
14
|
+
option :compressed_name, OutputPath, "$(name).$(extension).gz", :aliases=>['compressed']
|
15
|
+
|
16
|
+
def initialize(config, parent=nil)
|
17
|
+
if !config.has_key?("source_folder")
|
18
|
+
config["source_folder"]= "build/$(mode)"
|
19
|
+
end
|
20
|
+
super(config, parent)
|
21
|
+
|
22
|
+
@options.output_folder= File.join(parent.output_folder, name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def product_name(product_type, extension)
|
26
|
+
info= Struct.new(:extension).new(extension)
|
27
|
+
name= self.send("#{product_type.to_s}_name")
|
28
|
+
Interpolated.value_of(name, info)
|
29
|
+
end
|
30
|
+
|
31
|
+
def build
|
32
|
+
wd= Dir.getwd
|
33
|
+
Dir.chdir(path)
|
34
|
+
system build_command
|
35
|
+
Dir.chdir(wd)
|
36
|
+
|
37
|
+
# external projects aren't included in the output when weak linked,
|
38
|
+
# they are just expected to be there, somehow. Like magic.
|
39
|
+
return if WEAK_LINKAGE==linkage
|
40
|
+
|
41
|
+
FileUtils.rm_r(output_folder) if File.directory?(output_folder)
|
42
|
+
FileUtils.unlink(output_folder) if File.symlink?(output_folder)
|
43
|
+
|
44
|
+
if DEBUG_MODE==mode
|
45
|
+
FileUtils.symlink(File.expand_path(source_folder), output_folder)
|
46
|
+
else
|
47
|
+
FileUtils.cp_r(File.expand_path(source_folder), output_folder)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Distil
|
2
|
+
|
3
|
+
class Project < Configurable
|
4
|
+
|
5
|
+
include ErrorReporter
|
6
|
+
|
7
|
+
option :output_folder, ProjectPath, "build/$(mode)", :aliases=>['output']
|
8
|
+
option :source_folder, ProjectPath, ""
|
9
|
+
|
10
|
+
option :path, String
|
11
|
+
option :mode, DEBUG_MODE, :valid_values=>[DEBUG_MODE, RELEASE_MODE]
|
12
|
+
option :force
|
13
|
+
|
14
|
+
def build
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.from_config(config, parent=nil)
|
18
|
+
|
19
|
+
if config.is_a?(String)
|
20
|
+
string= config
|
21
|
+
config= { "name" => File.basename(config, ".*") }
|
22
|
+
full_path= File.expand_path(string)
|
23
|
+
if File.exist?(full_path) && File.file?(full_path)
|
24
|
+
config["path"]= File.dirname(full_path)
|
25
|
+
else
|
26
|
+
config["path"]= full_path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
config["mode"]||= parent.mode if parent
|
31
|
+
|
32
|
+
path= config["path"]
|
33
|
+
if !path
|
34
|
+
ErrorReporter.error "No path for project: #{config["name"]}"
|
35
|
+
return nil
|
36
|
+
end
|
37
|
+
|
38
|
+
if !File.directory?(path)
|
39
|
+
ErrorReporter.error "Path is not valid for project: #{config["name"]}"
|
40
|
+
return nil
|
41
|
+
end
|
42
|
+
|
43
|
+
basename= File.basename(path)
|
44
|
+
|
45
|
+
case
|
46
|
+
when exist?(path, "#{basename}.jsproj")
|
47
|
+
project_file= File.join(path, "#{basename}.jsproj")
|
48
|
+
project_info= YAML.load_file(project_file)
|
49
|
+
project_info.merge!(config)
|
50
|
+
project_info["path"]= path
|
51
|
+
project= ExternalProject.new(project_info, parent)
|
52
|
+
if parent
|
53
|
+
project.build_command ||= "distil --mode=#{parent.mode} --force=#{parent.force}"
|
54
|
+
else
|
55
|
+
project.build_command ||= "distil"
|
56
|
+
end
|
57
|
+
when exist?(path, "Makefile") || exist?(path, "makefile")
|
58
|
+
project= ExternalProject.new(config, parent)
|
59
|
+
project.build_command ||= "make"
|
60
|
+
when exist?(path, "Rakefile") || exist?(path, "rakefile")
|
61
|
+
project= ExternalProject.new(config, parent)
|
62
|
+
project.build_command ||= "rake"
|
63
|
+
when exist?(path, "Jakefile") || exist?(path, "jakefile")
|
64
|
+
project= ExternalProject.new(config, parent)
|
65
|
+
project.build_command ||= "jake"
|
66
|
+
else
|
67
|
+
ErrorReporter.error "Could not determine type for project: #{config["name"]}"
|
68
|
+
end
|
69
|
+
return project
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
require 'distil/project/external-project'
|
78
|
+
require 'distil/project/distil-project'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Distil
|
2
|
+
|
3
|
+
class JavascriptFile < SourceFile
|
4
|
+
extension 'js'
|
5
|
+
content_type 'js'
|
6
|
+
|
7
|
+
def can_embed_as_content(file)
|
8
|
+
[".css", ".html", ".json"].include?(file.extension)
|
9
|
+
end
|
10
|
+
|
11
|
+
def escape_embeded_content(content)
|
12
|
+
content.gsub("\\", "\\\\").gsub("\n", "\\n").gsub("\"", "\\\"").gsub("'", "\\\\'")
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
$compressor = File.expand_path("#{VENDOR_DIR}/yuicompressor-2.4.2.jar")
|
4
|
+
|
5
|
+
module Distil
|
6
|
+
|
7
|
+
class SourceFile
|
8
|
+
attr_accessor :parent_folder, :full_path
|
9
|
+
class_attr :extension
|
10
|
+
class_attr :content_type
|
11
|
+
|
12
|
+
include ErrorReporter
|
13
|
+
|
14
|
+
def initialize(filepath)
|
15
|
+
@full_path= File.expand_path(filepath)
|
16
|
+
|
17
|
+
@parent_folder= File.dirname(@full_path)
|
18
|
+
@dependencies= []
|
19
|
+
@assets= []
|
20
|
+
|
21
|
+
@@file_cache[@full_path]= self
|
22
|
+
end
|
23
|
+
|
24
|
+
def can_embed_as_content(file)
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def warning(message, line=nil)
|
29
|
+
super(message, self, line)
|
30
|
+
end
|
31
|
+
|
32
|
+
def error(message, line=nil)
|
33
|
+
super(message, self, line)
|
34
|
+
end
|
35
|
+
|
36
|
+
@@file_types= []
|
37
|
+
def self.inherited(subclass)
|
38
|
+
@@file_types << subclass
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.file_types
|
42
|
+
@@file_types
|
43
|
+
end
|
44
|
+
|
45
|
+
@@file_cache= Hash.new
|
46
|
+
def self.from_path(filepath)
|
47
|
+
full_path= File.expand_path(filepath)
|
48
|
+
file= @@file_cache[full_path]
|
49
|
+
return file if file
|
50
|
+
|
51
|
+
extension= File.extname(filepath)[1..-1]
|
52
|
+
|
53
|
+
@@file_types.each { |handler|
|
54
|
+
next if (handler.extension != extension)
|
55
|
+
|
56
|
+
return handler.new(filepath)
|
57
|
+
}
|
58
|
+
|
59
|
+
return SourceFile.new(filepath)
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_s
|
63
|
+
@full_path
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_str
|
67
|
+
@full_path
|
68
|
+
end
|
69
|
+
|
70
|
+
def basename(suffix=extension)
|
71
|
+
File.basename(@full_path, suffix)
|
72
|
+
end
|
73
|
+
|
74
|
+
def file_path
|
75
|
+
@file_path
|
76
|
+
end
|
77
|
+
|
78
|
+
def file_path=(path)
|
79
|
+
@file_path=path
|
80
|
+
end
|
81
|
+
|
82
|
+
def load_content
|
83
|
+
File.read(@full_path)
|
84
|
+
end
|
85
|
+
|
86
|
+
def escape_embeded_content(content)
|
87
|
+
content
|
88
|
+
end
|
89
|
+
|
90
|
+
def content
|
91
|
+
@content ||= load_content
|
92
|
+
end
|
93
|
+
|
94
|
+
def last_modified
|
95
|
+
@last_modified ||= File.stat(@full_path).mtime
|
96
|
+
end
|
97
|
+
|
98
|
+
def minified_content(source)
|
99
|
+
# Run the Y!UI Compressor
|
100
|
+
return source if !content_type
|
101
|
+
buffer= ""
|
102
|
+
|
103
|
+
IO.popen("java -jar #{$compressor} --type #{content_type}", "r+") { |pipe|
|
104
|
+
pipe.puts(source)
|
105
|
+
pipe.close_write
|
106
|
+
buffer= pipe.read
|
107
|
+
}
|
108
|
+
|
109
|
+
buffer
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.path_relative_to_folder(path, folder)
|
113
|
+
outputFolder= File.expand_path(folder).to_s
|
114
|
+
|
115
|
+
# Remove leading slash and split into parts
|
116
|
+
file_parts= path.slice(1..-1).split('/');
|
117
|
+
output_parts= outputFolder.slice(1..-1).split('/');
|
118
|
+
|
119
|
+
common_prefix_length= 0
|
120
|
+
|
121
|
+
file_parts.each_index { |i|
|
122
|
+
common_prefix_length= i
|
123
|
+
break if file_parts[i]!=output_parts[i]
|
124
|
+
}
|
125
|
+
|
126
|
+
return '../'*(output_parts.length-common_prefix_length) + file_parts[common_prefix_length..-1].join('/')
|
127
|
+
end
|
128
|
+
|
129
|
+
def relative_to_folder(output_folder)
|
130
|
+
self.class.path_relative_to_folder(@full_path, output_folder)
|
131
|
+
end
|
132
|
+
|
133
|
+
def relative_to_file(source_file)
|
134
|
+
folder= File.dirname(File.expand_path(source_file))
|
135
|
+
self.relative_to_folder(folder)
|
136
|
+
end
|
137
|
+
|
138
|
+
def dependencies
|
139
|
+
content
|
140
|
+
@dependencies
|
141
|
+
end
|
142
|
+
|
143
|
+
def add_dependency(file)
|
144
|
+
return if @dependencies.include?(file)
|
145
|
+
@dependencies << file
|
146
|
+
end
|
147
|
+
|
148
|
+
def assets
|
149
|
+
content
|
150
|
+
@assets
|
151
|
+
end
|
152
|
+
|
153
|
+
def add_asset(file)
|
154
|
+
@assets << file
|
155
|
+
end
|
156
|
+
|
157
|
+
def copy_to(folder, prefix)
|
158
|
+
file_path= self.file_path || relative_to_folder(prefix||"")
|
159
|
+
final_target_folder= File.join(folder, File.dirname(file_path))
|
160
|
+
FileUtils.mkdir_p final_target_folder
|
161
|
+
FileUtils.cp self.full_path, final_target_folder
|
162
|
+
File.join(final_target_folder, File.basename(file_path))
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
# load all the other file types
|
170
|
+
Dir.glob("#{LIB_DIR}/distil/source-file/*-file.rb") { |file|
|
171
|
+
require file
|
172
|
+
}
|