distil 0.13.6 → 0.14.0.b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/assets/distil.js +9 -7
- data/bin/distil +36 -60
- data/distil.gemspec +17 -32
- data/distil.tmproj +46 -15
- data/lib/distil/browser.rb +30 -26
- data/lib/distil/configurable.rb +64 -153
- data/lib/distil/error-reporter.rb +22 -20
- data/lib/distil/file-vendor.rb +29 -0
- data/lib/distil/hash-additions.rb +45 -0
- data/lib/distil/javascript-code.rb +12 -0
- data/lib/distil/{task/validate-js-task.rb → javascript-file-validator.rb} +19 -23
- data/lib/distil/library.rb +243 -0
- data/lib/distil/product/cache-manifest-product.rb +21 -0
- data/lib/distil/product/css-product.rb +41 -23
- data/lib/distil/product/html-product.rb +20 -0
- data/lib/distil/product/javascript-product.rb +122 -111
- data/lib/distil/product.rb +90 -76
- data/lib/distil/project.rb +370 -104
- data/lib/distil/recursive-http-fetcher.rb +72 -0
- data/lib/distil/server.rb +43 -0
- data/lib/distil/source-file/css-file.rb +56 -3
- data/lib/distil/source-file/html-file.rb +5 -6
- data/lib/distil/source-file/javascript-file.rb +96 -8
- data/lib/distil/source-file/json-file.rb +2 -4
- data/lib/distil/source-file/yui-minifiable-file.rb +19 -0
- data/lib/distil/source-file.rb +50 -92
- data/lib/distil/subclass-tracker.rb +13 -0
- data/lib/distil.rb +21 -37
- metadata +40 -39
- data/assets/mime.types +0 -1240
- data/lib/distil/configurable/file-set.rb +0 -85
- data/lib/distil/configurable/interpolated.rb +0 -36
- data/lib/distil/configurable/output-path.rb +0 -25
- data/lib/distil/configurable/project-path.rb +0 -25
- data/lib/distil/product/concatenated.rb +0 -83
- data/lib/distil/product/debug.rb +0 -32
- data/lib/distil/product/javascript-base-product.rb +0 -35
- data/lib/distil/product/javascript-doc-product.rb +0 -61
- data/lib/distil/product/minified.rb +0 -41
- data/lib/distil/product/page-product.rb +0 -27
- data/lib/distil/product/pdoc-product.rb +0 -42
- data/lib/distil/project/distil-project.rb +0 -157
- data/lib/distil/project/external-project.rb +0 -58
- data/lib/distil/project/remote-project.rb +0 -43
- data/lib/distil/target.rb +0 -251
- data/lib/distil/task/css-dependency-task.rb +0 -64
- data/lib/distil/task/jsl-dependency-task.rb +0 -50
- data/lib/distil/task/nib-task.rb +0 -72
- data/lib/distil/task.rb +0 -50
- data/lib/jsdoc.conf +0 -18
- data/lib/test/HtmlTestReporter.js +0 -127
- data/lib/test/Test.js +0 -248
- data/lib/test/TestReporter.js +0 -79
- data/lib/test/TestRunner.js +0 -132
- data/lib/test/browser.rb +0 -97
- data/lib/test/scriptwrapper.html +0 -10
- data/lib/test/unittest.html +0 -127
data/lib/distil/project.rb
CHANGED
@@ -1,144 +1,410 @@
|
|
1
1
|
module Distil
|
2
2
|
|
3
|
-
|
3
|
+
BUILD_FILE= 'Buildfile'
|
4
|
+
DEFAULT_OUTPUT_FOLDER= 'build'
|
5
|
+
DEFAULT_LANGUAGE= 'en'
|
4
6
|
|
7
|
+
APPLICATION_TYPE= 'application'
|
8
|
+
FRAMEWORK_TYPE= 'framework'
|
9
|
+
|
10
|
+
class Project < Configurable
|
5
11
|
include ErrorReporter
|
12
|
+
include FileVendor
|
13
|
+
include JavascriptFileValidator
|
14
|
+
|
15
|
+
attr_reader :name, :path, :folder, :source_folder, :output_folder, :include_paths
|
16
|
+
attr_reader :assets, :asset_aliases
|
17
|
+
attr_reader :libraries_by_name, :libraries
|
18
|
+
attr_reader :languages, :project_type
|
19
|
+
attr_reader :source_files
|
20
|
+
attr_reader :global_export
|
21
|
+
attr_reader :additional_globals
|
22
|
+
attr_reader :subprojects
|
23
|
+
attr_reader :dependency_aliases
|
24
|
+
|
25
|
+
alias_config_key :project_type, :type
|
26
|
+
|
27
|
+
def self.find(dir=nil)
|
28
|
+
cwd= Dir.pwd
|
29
|
+
dir ||= Dir.pwd
|
30
|
+
while dir.length > 1
|
31
|
+
return from_file(File.join(dir, BUILD_FILE)) if File.exists?(File.join(dir, BUILD_FILE))
|
32
|
+
|
33
|
+
projects= Dir.glob(File.join(dir, "*.jsproj"))
|
34
|
+
return from_file(projects.first) if 1==projects.size
|
35
|
+
|
36
|
+
unless 0==projects.size
|
37
|
+
puts "More than one candidate for Project:"
|
38
|
+
projects.each { |e|
|
39
|
+
puts " #{path_relative_to_folder(e, cwd)}"
|
40
|
+
}
|
41
|
+
exit 1
|
42
|
+
end
|
43
|
+
|
44
|
+
dir= File.dirname(dir)
|
45
|
+
end
|
46
|
+
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.from_file(file)
|
51
|
+
yaml= YAML::load_file(file)
|
52
|
+
if File.exists?("#{file}.local")
|
53
|
+
local_yaml= YAML::load_file("#{file}.local")
|
54
|
+
yaml.deep_merge!(local_yaml)
|
55
|
+
end
|
56
|
+
new(file, yaml)
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize(path, config={}, parent=nil)
|
60
|
+
@path= path
|
61
|
+
@folder= File.dirname(@path)
|
62
|
+
@source_folder= @folder
|
63
|
+
@output_folder= DEFAULT_OUTPUT_FOLDER
|
64
|
+
@include_paths= [@folder]
|
65
|
+
@include_files= []
|
66
|
+
@asset_aliases= {}
|
67
|
+
@dependency_aliases= {}
|
68
|
+
@assets= Set.new
|
69
|
+
@source_files= Set.new
|
70
|
+
@subprojects= []
|
71
|
+
@libraries= parent ? parent.libraries : []
|
72
|
+
@libraries_by_name= parent ? parent.libraries_by_name : {}
|
73
|
+
@languages= []
|
74
|
+
@additional_globals= []
|
75
|
+
@name= File.basename(@folder, ".*")
|
76
|
+
|
77
|
+
ignore_warnings= false
|
78
|
+
|
79
|
+
child_config= config.dup
|
80
|
+
child_config.delete("targets")
|
81
|
+
child_config.delete("require")
|
82
|
+
|
83
|
+
Dir.chdir(@folder) do
|
84
|
+
configure_with config do |c|
|
85
|
+
|
86
|
+
c.with :name do |name|
|
87
|
+
@name= name
|
88
|
+
end
|
89
|
+
|
90
|
+
c.with :output_folder do |output_folder|
|
91
|
+
@output_folder= output_folder
|
92
|
+
end
|
93
|
+
FileUtils.mkdir_p output_folder
|
94
|
+
|
95
|
+
c.with :source_folder do |source_folder|
|
96
|
+
@source_folder= source_folder
|
97
|
+
end
|
98
|
+
|
99
|
+
c.with :export do |export|
|
100
|
+
export=@name.as_identifier if true==export
|
101
|
+
@global_export= export
|
102
|
+
end
|
103
|
+
|
104
|
+
c.with_each :globals do |global|
|
105
|
+
@additional_globals << global
|
106
|
+
end
|
107
|
+
|
108
|
+
c.with_each :languages do |language|
|
109
|
+
@languages << language
|
110
|
+
end
|
111
|
+
|
112
|
+
c.with_each :require do |asset|
|
113
|
+
asset= Library.new(asset, self)
|
114
|
+
@libraries << asset
|
115
|
+
@libraries_by_name[asset.name]= asset
|
116
|
+
end
|
117
|
+
|
118
|
+
c.with_each :source do |file|
|
119
|
+
include_file(file)
|
120
|
+
end
|
121
|
+
|
122
|
+
c.with_each :targets do |target|
|
123
|
+
target_config= child_config.dup
|
124
|
+
target_config.deep_merge!(target)
|
125
|
+
@subprojects << Project.new(path, target_config, self)
|
126
|
+
end
|
127
|
+
|
128
|
+
end # configure_with
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
def validate_files
|
134
|
+
validate_javascript_files
|
135
|
+
end
|
6
136
|
|
7
|
-
|
8
|
-
|
137
|
+
def compute_source_files
|
138
|
+
return if @source_files_computed
|
139
|
+
@source_files_computed= true
|
140
|
+
|
141
|
+
inspected= Set.new
|
142
|
+
ordered_files= []
|
9
143
|
|
10
|
-
|
11
|
-
|
12
|
-
|
144
|
+
add_file= lambda { |f|
|
145
|
+
return unless include_files.include?(f)
|
146
|
+
return if inspected.include?(f)
|
147
|
+
inspected << f
|
148
|
+
|
149
|
+
if f.respond_to? :dependencies
|
150
|
+
f.dependencies.each { |d|
|
151
|
+
add_file.call d
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
ordered_files << f
|
156
|
+
}
|
157
|
+
|
158
|
+
include_files.each { |f| add_file.call(f) }
|
159
|
+
ordered_files.each { |f|
|
160
|
+
next if f.is_a?(SourceFile) && f.is_asset
|
161
|
+
|
162
|
+
used= false
|
163
|
+
products.each { |p|
|
164
|
+
used= true if p.include_file(f)
|
165
|
+
}
|
166
|
+
|
167
|
+
next if !used
|
168
|
+
|
169
|
+
if !f.is_a?(Library)
|
170
|
+
@source_files << f
|
171
|
+
@assets.merge(f.assets) if f.assets
|
172
|
+
end
|
173
|
+
}
|
174
|
+
end
|
13
175
|
|
14
|
-
def up_to_date
|
15
|
-
|
176
|
+
def up_to_date?
|
177
|
+
products.each { |product|
|
178
|
+
return false if !product.up_to_date?
|
179
|
+
}
|
180
|
+
return true
|
16
181
|
end
|
17
182
|
|
18
183
|
def build
|
19
|
-
|
184
|
+
subprojects.each { |subproject|
|
185
|
+
subproject.build
|
186
|
+
}
|
20
187
|
|
188
|
+
compute_source_files
|
189
|
+
return if up_to_date?
|
190
|
+
|
191
|
+
validate_files
|
192
|
+
build_assets
|
193
|
+
|
194
|
+
products.each { |product|
|
195
|
+
product.build
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
21
199
|
def clean
|
200
|
+
compute_source_files
|
201
|
+
products.each { |product|
|
202
|
+
product.clean
|
203
|
+
}
|
22
204
|
end
|
23
205
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
206
|
+
def inspect
|
207
|
+
"<#{self.class}:0x#{object_id.to_s(16)} name=#{name}>"
|
208
|
+
end
|
209
|
+
|
210
|
+
def output_path
|
211
|
+
@output_path ||= File.join(folder, output_folder)
|
212
|
+
end
|
213
|
+
|
214
|
+
def relative_path_for(thing)
|
215
|
+
Project.path_relative_to_folder(thing.is_a?(String) ? thing : thing.full_path, path)
|
216
|
+
end
|
217
|
+
|
218
|
+
def relative_output_path_for(thing)
|
219
|
+
return nil if !thing
|
220
|
+
# puts "relative_output_path_for: #{thing} #{output_path}"
|
221
|
+
Project.path_relative_to_folder(thing.is_a?(String) ? thing : thing.output_path, output_path)
|
222
|
+
end
|
223
|
+
|
224
|
+
def notice_text
|
28
225
|
begin
|
29
|
-
|
226
|
+
@notice_text ||= File.read(File.join(@folder, @notice)).strip
|
30
227
|
rescue
|
31
|
-
|
32
|
-
end
|
33
|
-
if $?.nil? || !$?.success?
|
34
|
-
raise ValidationError.new("The git version control tool is required to pull this repository: #{uri}")
|
228
|
+
@notice_text ||= ""
|
35
229
|
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def products
|
233
|
+
return @products unless @products.nil?
|
36
234
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
235
|
+
@products= []
|
236
|
+
langs= languages.empty? ? [nil] : languages
|
237
|
+
|
238
|
+
Product.subclasses.each { |klass|
|
239
|
+
langs.each { |lang|
|
240
|
+
klass.variants.each { |v|
|
241
|
+
@products << klass.new(self, lang, v)
|
242
|
+
}
|
243
|
+
}
|
244
|
+
}
|
245
|
+
|
246
|
+
@products
|
46
247
|
end
|
47
248
|
|
48
|
-
def
|
249
|
+
def symlink_assets
|
250
|
+
Dir.chdir(output_path) do
|
251
|
+
folders= []
|
49
252
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
config["path"]= uri.to_s
|
63
|
-
|
64
|
-
full_path= File.expand_path(config["path"])
|
65
|
-
|
66
|
-
if File.exist?(full_path) && File.file?(full_path)
|
67
|
-
config["path"]= File.dirname(full_path)
|
68
|
-
else
|
69
|
-
config["path"]= full_path
|
253
|
+
files= assets+source_files
|
254
|
+
files.each { |a|
|
255
|
+
next if (a.full_path).starts_with?(output_path)
|
256
|
+
|
257
|
+
path= relative_output_path_for(a)
|
258
|
+
|
259
|
+
parts= File.dirname(path).split(File::SEPARATOR)
|
260
|
+
if ('.'==parts[0])
|
261
|
+
product_path= File.join(output_folder, path)
|
262
|
+
FileUtils.rm product_path if File.exists? product_path
|
263
|
+
File.symlink path, product_path
|
264
|
+
next
|
70
265
|
end
|
71
|
-
end
|
72
|
-
end
|
73
266
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
else
|
81
|
-
raise ValidationError.new("External project has neither name, path nor repository")
|
82
|
-
end
|
83
|
-
end
|
267
|
+
folders << parts[0] if !folders.include?(parts[0])
|
268
|
+
}
|
269
|
+
|
270
|
+
folders.each { |f|
|
271
|
+
target= f
|
272
|
+
source= relative_output_path_for(File.join(source_folder, f))
|
84
273
|
|
85
|
-
|
86
|
-
|
274
|
+
FileUtils.rm target if File.symlink?(target)
|
275
|
+
next if File.directory?(target)
|
276
|
+
File.symlink source, target
|
277
|
+
}
|
87
278
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
279
|
+
end
|
280
|
+
|
281
|
+
def copy_assets
|
282
|
+
assets.each { |a|
|
283
|
+
a.copy_to(output_folder, source_folder)
|
284
|
+
}
|
285
|
+
end
|
286
|
+
|
287
|
+
def build_assets
|
288
|
+
symlink_assets
|
289
|
+
# if (RELEASE_MODE==mode)
|
290
|
+
# copy_assets
|
291
|
+
# else
|
292
|
+
# symlink_assets
|
293
|
+
# end
|
294
|
+
end
|
295
|
+
|
296
|
+
def add_alias_for_asset(alias_name, asset)
|
297
|
+
if asset_aliases.include?(alias_name)
|
298
|
+
error "Attempt to register asset with the same alias as another asset: #{alias_name}"
|
299
|
+
return
|
93
300
|
end
|
301
|
+
asset_aliases[asset]= alias_name
|
302
|
+
end
|
303
|
+
|
304
|
+
def include_files
|
305
|
+
@include_files
|
306
|
+
end
|
307
|
+
|
308
|
+
def include_file(file)
|
309
|
+
return if file.nil?
|
94
310
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
ErrorReporter.error "No path for project: #{config["name"]}"
|
100
|
-
return nil
|
311
|
+
asset= @libraries_by_name[file]
|
312
|
+
if (asset)
|
313
|
+
@include_files << asset unless @include_files.include?(asset)
|
314
|
+
return
|
101
315
|
end
|
102
316
|
|
103
|
-
|
104
|
-
|
105
|
-
|
317
|
+
matches= glob(file)
|
318
|
+
matches= glob(File.join(source_folder, file)) if matches.empty?
|
319
|
+
|
320
|
+
if (matches.empty?)
|
321
|
+
error("No matching files found for: #{file}")
|
322
|
+
return
|
106
323
|
end
|
107
324
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
when exist?(path, "#{basename}.jsproj")
|
112
|
-
project_file= File.join(path, "#{basename}.jsproj")
|
113
|
-
project_info= YAML.load_file(project_file)
|
114
|
-
project_info.merge!(config)
|
115
|
-
project_info["path"]= path
|
116
|
-
project= ExternalProject.new(project_info, parent)
|
117
|
-
if parent
|
118
|
-
project.build_command ||= "distil --mode=#{parent.mode} --force=#{parent.force}"
|
325
|
+
matches.each { |m|
|
326
|
+
if File.directory?(m)
|
327
|
+
include_file(File.join(m, "**/*"))
|
119
328
|
else
|
120
|
-
|
329
|
+
f= file_from_path(m)
|
330
|
+
unless @include_files.include?(f)
|
331
|
+
@include_files << f
|
332
|
+
# determine language
|
333
|
+
f.language= languages.find { |l| File.fnmatch?("**/#{l}/**", f.full_path) }
|
334
|
+
end
|
121
335
|
end
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
336
|
+
}
|
337
|
+
end
|
338
|
+
|
339
|
+
def glob(path)
|
340
|
+
return path if File.exists?(path)
|
341
|
+
|
342
|
+
files= []
|
343
|
+
|
344
|
+
parts= path.split(File::SEPARATOR)
|
345
|
+
asset_name= parts[0]
|
346
|
+
file_path= File.join(parts.slice(1..-1))
|
347
|
+
|
348
|
+
if (@libraries_by_name.include?(asset_name))
|
349
|
+
asset= @libraries_by_name[asset_name]
|
350
|
+
return Dir.glob(File.join(asset.output_path, file_path))
|
133
351
|
end
|
134
|
-
return project
|
135
352
|
|
353
|
+
files.concat(Dir.glob(path));
|
354
|
+
|
355
|
+
include_paths.each { |i|
|
356
|
+
files.concat(Dir.glob(File.join(i, path)))
|
357
|
+
}
|
358
|
+
return files
|
359
|
+
end
|
360
|
+
|
361
|
+
def add_alias_for_file(alias_name, file)
|
362
|
+
@dependency_aliases[alias_name]= file
|
363
|
+
end
|
364
|
+
|
365
|
+
def find_file(path, content_type=nil, mode=nil)
|
366
|
+
return path if File.exists?(path)
|
367
|
+
|
368
|
+
include_paths.each { |i|
|
369
|
+
f= File.join(i, path)
|
370
|
+
return f if File.exists?(f)
|
371
|
+
}
|
372
|
+
|
373
|
+
# Check remote assets
|
374
|
+
parts= path.split(File::SEPARATOR)
|
375
|
+
asset_name= parts[0]
|
376
|
+
file_path= File.join(parts.slice(1..-1))
|
377
|
+
|
378
|
+
return nil unless @libraries_by_name.include?(asset_name)
|
379
|
+
asset= @libraries_by_name[asset_name]
|
380
|
+
|
381
|
+
return asset.file_for(content_type, nil, mode) if 1==parts.length
|
382
|
+
|
383
|
+
f= File.join(asset.output_path, file_path)
|
384
|
+
return f if File.exists?(f)
|
385
|
+
|
386
|
+
nil
|
387
|
+
end
|
388
|
+
|
389
|
+
def self.path_relative_to_folder(path, folder)
|
390
|
+
path= File.expand_path(path)
|
391
|
+
outputFolder= File.expand_path(folder).to_s
|
392
|
+
|
393
|
+
# Remove leading slash and split into parts
|
394
|
+
file_parts= path.slice(1..-1).split('/');
|
395
|
+
output_parts= outputFolder.slice(1..-1).split('/');
|
396
|
+
|
397
|
+
common_prefix_length= 0
|
398
|
+
|
399
|
+
file_parts.each_index { |i|
|
400
|
+
common_prefix_length= i
|
401
|
+
break if file_parts[i]!=output_parts[i]
|
402
|
+
}
|
403
|
+
|
404
|
+
return '../'*(output_parts.length-common_prefix_length) + file_parts[common_prefix_length..-1].join('/')
|
136
405
|
end
|
137
406
|
|
138
407
|
end
|
139
408
|
|
409
|
+
|
140
410
|
end
|
141
|
-
|
142
|
-
require 'distil/project/remote-project'
|
143
|
-
require 'distil/project/external-project'
|
144
|
-
require 'distil/project/distil-project'
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Distil
|
2
|
+
|
3
|
+
class RecursiveHTTPFetcher
|
4
|
+
attr_accessor :quiet
|
5
|
+
|
6
|
+
def initialize(urls_to_fetch, level = 1, cwd = ".")
|
7
|
+
@level = level
|
8
|
+
@cwd = cwd
|
9
|
+
@urls_to_fetch = RUBY_VERSION >= '1.9' ? urls_to_fetch.to_s.lines : urls_to_fetch.to_s.to_a
|
10
|
+
@quiet = true
|
11
|
+
end
|
12
|
+
|
13
|
+
def ls
|
14
|
+
@urls_to_fetch.collect do |url|
|
15
|
+
if url =~ /^svn(\+ssh)?:\/\/.*/ || url =~ /\/svn\//
|
16
|
+
`svn ls #{url}`.split("\n").map {|entry| "/#{entry}"} rescue nil
|
17
|
+
else
|
18
|
+
open(url) do |stream|
|
19
|
+
links("", stream.read)
|
20
|
+
end rescue nil
|
21
|
+
end
|
22
|
+
end.flatten
|
23
|
+
end
|
24
|
+
|
25
|
+
def push_d(dir)
|
26
|
+
@cwd = File.join(@cwd, dir)
|
27
|
+
FileUtils.mkdir_p(@cwd)
|
28
|
+
end
|
29
|
+
|
30
|
+
def pop_d
|
31
|
+
@cwd = File.dirname(@cwd)
|
32
|
+
end
|
33
|
+
|
34
|
+
def links(base_url, contents)
|
35
|
+
links = []
|
36
|
+
contents.scan(/href\s*=\s*\"*[^\">]*/i) do |link|
|
37
|
+
link = link.sub(/href="/i, "")
|
38
|
+
next if link =~ /svnindex.xsl$/
|
39
|
+
next if link =~ /^(\w*:|)\/\// || link =~ /^\./
|
40
|
+
links << File.join(base_url, link)
|
41
|
+
end
|
42
|
+
links
|
43
|
+
end
|
44
|
+
|
45
|
+
def download(link)
|
46
|
+
puts "+ #{File.join(@cwd, File.basename(link))}" unless @quiet
|
47
|
+
open(link) do |stream|
|
48
|
+
File.open(File.join(@cwd, File.basename(link)), "wb") do |file|
|
49
|
+
file.write(stream.read)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def fetch(links = @urls_to_fetch)
|
55
|
+
links.each do |l|
|
56
|
+
(l =~ /\/$/ || links == @urls_to_fetch) ? fetch_dir(l) : download(l)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def fetch_dir(url)
|
61
|
+
@level += 1
|
62
|
+
push_d(File.basename(url)) if @level > 0
|
63
|
+
open(url) do |stream|
|
64
|
+
contents = stream.read
|
65
|
+
fetch(links(url, contents))
|
66
|
+
end
|
67
|
+
pop_d if @level > 0
|
68
|
+
@level -= 1
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Distil
|
2
|
+
|
3
|
+
def self.start_server(project, options)
|
4
|
+
require 'webrick'
|
5
|
+
require 'directory_watcher'
|
6
|
+
|
7
|
+
port= options['server_port'] || 8888;
|
8
|
+
path= options['url']
|
9
|
+
config= {
|
10
|
+
:Port => port
|
11
|
+
}
|
12
|
+
|
13
|
+
server= WEBrick::HTTPServer.new(config)
|
14
|
+
server.mount(path || '/', WEBrick::HTTPServlet::FileHandler, project.output_folder)
|
15
|
+
|
16
|
+
['INT', 'TERM'].each { |signal|
|
17
|
+
trap(signal){ server.shutdown }
|
18
|
+
}
|
19
|
+
|
20
|
+
puts "watching #{project.folder}"
|
21
|
+
dw = DirectoryWatcher.new(project.folder, {
|
22
|
+
:glob=>"**/*",
|
23
|
+
:pre_load => true,
|
24
|
+
:interval => 1
|
25
|
+
})
|
26
|
+
dw.add_observer { |*args|
|
27
|
+
args.each { |event|
|
28
|
+
puts event
|
29
|
+
if :modified==event.type
|
30
|
+
puts event.path
|
31
|
+
end
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
dw.start
|
36
|
+
gets
|
37
|
+
# b= Browser.new
|
38
|
+
# b.open("http://localhost:#{port}/#{path}")
|
39
|
+
# server.start
|
40
|
+
dw.stop
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -1,14 +1,67 @@
|
|
1
1
|
module Distil
|
2
2
|
|
3
|
+
CSS_IMPORT_REGEX = /@import\s+url\("?(.*\.css)"?\)/
|
4
|
+
CSS_IMAGE_URL_REGEX= /url\("?(.*\.(jpg|png|gif))"?\)/
|
5
|
+
|
3
6
|
class CssFile < SourceFile
|
4
|
-
|
7
|
+
include YuiMinifiableFile
|
8
|
+
|
5
9
|
extension "css"
|
6
10
|
content_type "css"
|
7
11
|
|
8
|
-
def
|
9
|
-
|
12
|
+
def content
|
13
|
+
return @content unless @content.nil?
|
14
|
+
@content= File.read(full_path)
|
15
|
+
# Replace all ' (single quotes) with " (double quotes)
|
16
|
+
@content.gsub!(/\'/,'"')
|
17
|
+
# Force a newline after a rule terminating ; (semi-colon)
|
18
|
+
@content.gsub!(/;(\n|\r)*/, ";\n")
|
19
|
+
@content
|
10
20
|
end
|
11
21
|
|
22
|
+
def rewrite_content_relative_to_path(path)
|
23
|
+
content.gsub(CSS_IMAGE_URL_REGEX) { |match|
|
24
|
+
image_file= File.join(dirname, $1)
|
25
|
+
|
26
|
+
if (!File.exists?(image_file))
|
27
|
+
match
|
28
|
+
else
|
29
|
+
asset= project.file_from_path(image_file)
|
30
|
+
"url(\"#{asset.relative_path}\")"
|
31
|
+
end
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def dependencies
|
36
|
+
@dependencies unless @dependencies.nil?
|
37
|
+
|
38
|
+
line_num=0
|
39
|
+
content.each_line { |line|
|
40
|
+
line_num+=1
|
41
|
+
|
42
|
+
line.scan(CSS_IMPORT_REGEX) { |match|
|
43
|
+
css_file= File.join(dirname, $1)
|
44
|
+
|
45
|
+
if (!File.exists?(css_file))
|
46
|
+
error "Imported CSS file not found: #{$1}", line_num
|
47
|
+
else
|
48
|
+
add_dependency(project.file_from_path(css_file))
|
49
|
+
end
|
50
|
+
}
|
51
|
+
|
52
|
+
line.scan(CSS_IMAGE_URL_REGEX) { |match|
|
53
|
+
image_file= File.join(dirname, $1)
|
54
|
+
|
55
|
+
if (!File.exists?(image_file))
|
56
|
+
warning "Resource not found: #{$1}", line_num
|
57
|
+
else
|
58
|
+
asset= project.file_from_path(image_file)
|
59
|
+
add_asset(asset)
|
60
|
+
end
|
61
|
+
}
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
12
65
|
end
|
13
66
|
|
14
67
|
end
|