jsus 0.3.4 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -21,4 +21,27 @@ Feature: compression
21
21
  And file "tmp/compression.min.js" should contain
22
22
  """
23
23
  var Color
24
- """
24
+ """
25
+
26
+ # It's more a sanity check than a real test.
27
+ Scenario: using the --compression-method option
28
+ When I run "jsus Compression tmp --compress --compression-method uglifier"
29
+ Then the following files should exist:
30
+ | tmp/compression.js |
31
+ | tmp/compression.min.js |
32
+ And file "tmp/compression.min.js" should not contain
33
+ """
34
+ /*
35
+ """
36
+ And file "tmp/compression.min.js" should not contain
37
+ """
38
+ */
39
+ """
40
+ And file "tmp/compression.min.js" should contain
41
+ """
42
+ Input
43
+ """
44
+ And file "tmp/compression.min.js" should contain
45
+ """
46
+ Color
47
+ """
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{jsus}
8
- s.version = "0.3.4"
8
+ s.version = "0.3.5"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Mark Abramov"]
12
- s.date = %q{2011-08-30}
12
+ s.date = %q{2011-09-15}
13
13
  s.default_executable = %q{jsus}
14
14
  s.description = %q{Javascript packager and dependency resolver}
15
15
  s.email = %q{markizko@gmail.com}
@@ -94,7 +94,10 @@ Gem::Specification.new do |s|
94
94
  "features/step_definitions/cli_steps.rb",
95
95
  "features/support/env.rb",
96
96
  "jsus.gemspec",
97
+ "lib/extensions/rgl.rb",
97
98
  "lib/jsus.rb",
99
+ "lib/jsus/cli.rb",
100
+ "lib/jsus/compiler.rb",
98
101
  "lib/jsus/container.rb",
99
102
  "lib/jsus/middleware.rb",
100
103
  "lib/jsus/package.rb",
@@ -113,9 +116,11 @@ Gem::Specification.new do |s|
113
116
  "lib/jsus/util/validator.rb",
114
117
  "lib/jsus/util/validator/base.rb",
115
118
  "lib/jsus/util/validator/mooforge.rb",
119
+ "lib/jsus/util/watcher.rb",
116
120
  "markup/index_template.haml",
117
121
  "markup/stylesheet.css",
118
122
  "markup/template.haml",
123
+ "spec/benchmarks/topsort.rb",
119
124
  "spec/data/Basic/README",
120
125
  "spec/data/Basic/app/javascripts/Orwik/Source/Library/Color.js",
121
126
  "spec/data/Basic/app/javascripts/Orwik/Source/Widget/Input/Input.Color.js",
@@ -194,6 +199,7 @@ Gem::Specification.new do |s|
194
199
  "spec/data/test_source_one.js",
195
200
  "spec/data/unicode_source.js",
196
201
  "spec/data/unicode_source_with_bom.js",
202
+ "spec/extensions/rgl_spec.rb",
197
203
  "spec/jsus/container_spec.rb",
198
204
  "spec/jsus/middleware_spec.rb",
199
205
  "spec/jsus/package_spec.rb",
@@ -201,6 +207,7 @@ Gem::Specification.new do |s|
201
207
  "spec/jsus/pool_spec.rb",
202
208
  "spec/jsus/source_file_spec.rb",
203
209
  "spec/jsus/tag_spec.rb",
210
+ "spec/jsus/util/compressor_spec.rb",
204
211
  "spec/jsus/util/documenter_spec.rb",
205
212
  "spec/jsus/util/file_cache_spec.rb",
206
213
  "spec/jsus/util/inflection_spec.rb",
@@ -208,10 +215,12 @@ Gem::Specification.new do |s|
208
215
  "spec/jsus/util/tree_spec.rb",
209
216
  "spec/jsus/util/validator/base_spec.rb",
210
217
  "spec/jsus/util/validator/mooforge_spec.rb",
218
+ "spec/jsus/util/watcher_spec.rb",
219
+ "spec/jsus/util_spec.rb",
211
220
  "spec/shared/class_stubs.rb",
212
221
  "spec/spec_helper.rb"
213
222
  ]
214
- s.homepage = %q{http://github.com/markiz/jsus}
223
+ s.homepage = %q{http://github.com/jsus/jsus}
215
224
  s.licenses = ["Public Domain"]
216
225
  s.require_paths = ["lib"]
217
226
  s.rubygems_version = %q{1.6.2}
@@ -226,13 +235,17 @@ Gem::Specification.new do |s|
226
235
  s.add_runtime_dependency(%q<rgl>, [">= 0"])
227
236
  s.add_development_dependency(%q<rake>, [">= 0"])
228
237
  s.add_development_dependency(%q<rspec>, [">= 0"])
229
- s.add_development_dependency(%q<cucumber>, [">= 0"])
238
+ s.add_development_dependency(%q<cucumber>, ["= 1.0.3"])
230
239
  s.add_development_dependency(%q<jeweler>, [">= 0"])
231
240
  s.add_development_dependency(%q<murdoc>, ["~> 0.1.11"])
232
241
  s.add_development_dependency(%q<ruby-debug19>, [">= 0"])
242
+ s.add_development_dependency(%q<linecache>, ["= 0.45"])
233
243
  s.add_development_dependency(%q<ruby-debug>, [">= 0"])
234
244
  s.add_development_dependency(%q<fssm>, [">= 0"])
235
- s.add_development_dependency(%q<yui-compressor>, [">= 0"])
245
+ s.add_development_dependency(%q<yuicompressor>, [">= 0"])
246
+ s.add_development_dependency(%q<uglifier>, [">= 0"])
247
+ s.add_development_dependency(%q<front-compiler>, [">= 0"])
248
+ s.add_development_dependency(%q<closure-compiler>, [">= 0"])
236
249
  s.add_development_dependency(%q<sinatra>, [">= 0"])
237
250
  s.add_development_dependency(%q<rack-test>, [">= 0"])
238
251
  s.add_development_dependency(%q<yard>, [">= 0"])
@@ -242,13 +255,17 @@ Gem::Specification.new do |s|
242
255
  s.add_dependency(%q<rgl>, [">= 0"])
243
256
  s.add_dependency(%q<rake>, [">= 0"])
244
257
  s.add_dependency(%q<rspec>, [">= 0"])
245
- s.add_dependency(%q<cucumber>, [">= 0"])
258
+ s.add_dependency(%q<cucumber>, ["= 1.0.3"])
246
259
  s.add_dependency(%q<jeweler>, [">= 0"])
247
260
  s.add_dependency(%q<murdoc>, ["~> 0.1.11"])
248
261
  s.add_dependency(%q<ruby-debug19>, [">= 0"])
262
+ s.add_dependency(%q<linecache>, ["= 0.45"])
249
263
  s.add_dependency(%q<ruby-debug>, [">= 0"])
250
264
  s.add_dependency(%q<fssm>, [">= 0"])
251
- s.add_dependency(%q<yui-compressor>, [">= 0"])
265
+ s.add_dependency(%q<yuicompressor>, [">= 0"])
266
+ s.add_dependency(%q<uglifier>, [">= 0"])
267
+ s.add_dependency(%q<front-compiler>, [">= 0"])
268
+ s.add_dependency(%q<closure-compiler>, [">= 0"])
252
269
  s.add_dependency(%q<sinatra>, [">= 0"])
253
270
  s.add_dependency(%q<rack-test>, [">= 0"])
254
271
  s.add_dependency(%q<yard>, [">= 0"])
@@ -259,13 +276,17 @@ Gem::Specification.new do |s|
259
276
  s.add_dependency(%q<rgl>, [">= 0"])
260
277
  s.add_dependency(%q<rake>, [">= 0"])
261
278
  s.add_dependency(%q<rspec>, [">= 0"])
262
- s.add_dependency(%q<cucumber>, [">= 0"])
279
+ s.add_dependency(%q<cucumber>, ["= 1.0.3"])
263
280
  s.add_dependency(%q<jeweler>, [">= 0"])
264
281
  s.add_dependency(%q<murdoc>, ["~> 0.1.11"])
265
282
  s.add_dependency(%q<ruby-debug19>, [">= 0"])
283
+ s.add_dependency(%q<linecache>, ["= 0.45"])
266
284
  s.add_dependency(%q<ruby-debug>, [">= 0"])
267
285
  s.add_dependency(%q<fssm>, [">= 0"])
268
- s.add_dependency(%q<yui-compressor>, [">= 0"])
286
+ s.add_dependency(%q<yuicompressor>, [">= 0"])
287
+ s.add_dependency(%q<uglifier>, [">= 0"])
288
+ s.add_dependency(%q<front-compiler>, [">= 0"])
289
+ s.add_dependency(%q<closure-compiler>, [">= 0"])
269
290
  s.add_dependency(%q<sinatra>, [">= 0"])
270
291
  s.add_dependency(%q<rack-test>, [">= 0"])
271
292
  s.add_dependency(%q<yard>, [">= 0"])
@@ -0,0 +1,37 @@
1
+ module RGL
2
+ class TopsortedGraphHasCycles < Exception; end
3
+ class DirectedAdjacencyGraph
4
+ # Returns array of topologically sorted vertices. Also checks if there are
5
+ # cycles.
6
+ #
7
+ # @note Default implementation of topsort iterator is a bit faster, but it doesn't
8
+ # check for that condition.
9
+ # @return [Array] sorted vertices list
10
+ # @raise [TopsortedGraphHasCycles] if graph has cycles
11
+ # @api public
12
+ def topsorted_vertices
13
+ result = []
14
+ available_vertices = []
15
+ reversed_graph = reverse
16
+ out_degrees = {}
17
+
18
+ vertices.each do |v|
19
+ out_degrees[v] = reversed_graph.out_degree(v)
20
+ available_vertices.push(v) if out_degrees[v] == 0
21
+ end
22
+
23
+ while available_vertices.size > 0
24
+ vertice = available_vertices.pop
25
+ result.push(vertice)
26
+ each_adjacent(vertice) do |dependent|
27
+ reversed_graph.remove_edge(dependent, vertice)
28
+ out_degrees[dependent] -= 1
29
+ available_vertices.push(dependent) if out_degrees[dependent] == 0
30
+ end
31
+ end
32
+
33
+ raise TopsortedGraphHasCycles unless result.size == vertices.size
34
+ result
35
+ end # topsorted_vertices
36
+ end # class DirectedAdjacencyGraph
37
+ end # module RGL
@@ -6,9 +6,9 @@ require 'active_support/ordered_hash'
6
6
  require 'active_support/core_ext/module/delegation'
7
7
  require 'rgl/adjacency'
8
8
  require 'rgl/topsort'
9
+ require 'extensions/rgl'
9
10
 
10
11
  require 'fileutils'
11
- require 'pathname'
12
12
 
13
13
  #
14
14
  # Jsus -- your better javascript packager.
@@ -22,7 +22,8 @@ module Jsus
22
22
  autoload :Pool, 'jsus/pool'
23
23
  autoload :Util, 'jsus/util'
24
24
  autoload :Middleware, 'jsus/middleware'
25
- autoload :Compressor, 'jsus/compressor'
25
+ autoload :CLI, 'jsus/cli'
26
+ autoload :Compiler, 'jsus/compiler'
26
27
 
27
28
  # In verbose mode jsus shows a lot of warnings like missing dependencies.
28
29
  # Default: false
@@ -71,7 +72,7 @@ module Jsus
71
72
  #
72
73
  # @return [Jsus::Util::Logger]
73
74
  def self.logger
74
- Thread.current[:jsus_logger] ||= Jsus::Util::Logger.new(STDOUT).tap do |logger|
75
+ @@logger ||= Jsus::Util::Logger.new($stdout).tap do |logger|
75
76
  logger.level = Logger::ERROR
76
77
  logger.formatter = lambda {|severity, time, progname, msg|
77
78
  "[#{time.strftime("%Y-%m-%d %H:%M:%S")}] [JSUS:#{severity}] #{msg}\n"
@@ -86,6 +87,6 @@ module Jsus
86
87
  # @note In case you use non-jsus logger, you might want to extend it with
87
88
  # Jsus::Util::Logger::Buffering module.
88
89
  def self.logger=(value)
89
- Thread.current[:jsus_logger] = value
90
+ @@logger = value
90
91
  end # self.logger=
91
92
  end
@@ -0,0 +1,206 @@
1
+ # TODO: document Cli class
2
+ module Jsus
3
+ class CLI
4
+ class << self
5
+ attr_accessor :cli_options
6
+
7
+ def run!(options)
8
+ self.cli_options = options
9
+ new.launch
10
+
11
+ if options[:watch]
12
+ input_dirs = [ options[:input_dir], options[:deps_dir] ].compact
13
+ Jsus.logger.info "Jsus enters watch mode, it will watch your files for changes and relaunch itself"
14
+ Jsus::Util::Watcher.watch(input_dirs) do |filename|
15
+ Jsus.logger.info "#{filename} changed, recompiling..."
16
+ new.launch
17
+ Jsus.logger.info "... done"
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ attr_accessor :options
24
+
25
+ def initialize(options = Jsus::CLI.cli_options)
26
+ @options = options
27
+ end
28
+
29
+ def setup_output_directory
30
+ output_dir = Pathname.new(options[:output_dir])
31
+ output_dir.mkpath
32
+ output_dir
33
+ end
34
+
35
+ def launch
36
+ checkpoint(:start)
37
+ @output_dir = setup_output_directory
38
+ @pool = preload_pool
39
+ @package = load_package
40
+ display_pool_stats(@pool) if options[:display_pool_stats]
41
+ @package_content = compile_package(@package)
42
+ post_process!(@package_content, options[:postproc]) if options[:postproc]
43
+
44
+ package_filename = @output_dir + @package.filename
45
+
46
+ if options[:compress]
47
+ File.open(package_filename.to_s.chomp(".js") + ".min.js", 'w') do |f|
48
+ f.write compress_package(@package_content)
49
+ end
50
+ end
51
+
52
+ package_filename.open('w') {|f| f << @package_content }
53
+
54
+ generate_supplemental_files
55
+ validate_sources
56
+ generate_includes if options[:generate_includes]
57
+ generate_docs if options[:documented_classes] && !options[:documented_classes].empty?
58
+ output_benchmarks
59
+ rescue Exception => e
60
+ output_benchmarks
61
+ end
62
+
63
+ def preload_pool
64
+ if options[:deps_dir]
65
+ Jsus::Pool.new(options[:deps_dir])
66
+ else
67
+ Jsus::Pool.new
68
+ end.tap { checkpoint(:pool) }
69
+ end
70
+
71
+ def load_package
72
+ package = Jsus::Package.new(Pathname.new(options[:input_dir]), :pool => @pool)
73
+ package.include_dependencies!
74
+ checkpoint(:dependencies)
75
+ package
76
+ end
77
+
78
+ def display_pool_stats(pool)
79
+ checkpoint(:pool_stats)
80
+ message = <<-EOF
81
+ Pool stats:
82
+ Main package:
83
+ #{display_package @package}
84
+
85
+ Supplementary packages:
86
+ #{pool.packages.map {|package| display_package package}.join }
87
+
88
+ EOF
89
+ Jsus.logger.info message
90
+ end
91
+
92
+ def display_package(package)
93
+ result = "Package: #{package.name}\n"
94
+ package.source_files.to_a.sort_by {|sf| sf.filename}.each do |sf|
95
+ result << " [#{sf.relative_filename}]\n"
96
+ result << " Provides: [#{sf.provides_names.join(", ")}]\n"
97
+ result << " Requires: [#{sf.requires_names.join(", ")}]\n"
98
+ end
99
+ result << "\n"
100
+ end
101
+
102
+ def compile_package(package)
103
+ package.compile(nil).tap { checkpoint(:compilation) }
104
+ end
105
+
106
+ # Modificate content string
107
+ def post_process!(content, postproc)
108
+ Compiler.post_process!(content, postproc)
109
+ checkpoint(:postproc)
110
+ end
111
+
112
+ def compress_package(content)
113
+ compression_method = options.fetch(:compression_method, :yui)
114
+ compressed_content = Jsus::Util::Compressor.compress(content, :method => compression_method)
115
+ if compressed_content != ""
116
+ @compression_ratio = compressed_content.size.to_f / content.size.to_f
117
+ else
118
+ @compression_ratio = 1.00
119
+ Jsus.logger.error "YUI compressor could not parse input. \n" <<
120
+ "Compressor command used: #{compressor.command.join(' ')}"
121
+ end
122
+ checkpoint(:compress)
123
+
124
+ compressed_content
125
+ end
126
+
127
+ def generate_supplemental_files
128
+ @package.generate_scripts_info(@output_dir) unless options[:without_scripts_info]
129
+ @package.generate_tree(@output_dir) unless options[:without_tree_info]
130
+ checkpoint(:supplemental_files)
131
+ end
132
+
133
+ def generate_includes
134
+ includes_root = Pathname.new(options[:includes_root] || @output_dir)
135
+ Compiler.generate_includes(@package, includes_root, @output_dir + "includes.js")
136
+ checkpoint(:includes)
137
+ end
138
+
139
+ def generate_docs
140
+ documenter = Jsus::Util::Documenter.new(:highlight_source => !options[:no_syntax_highlight])
141
+ @package.source_files.each {|source| documenter << source }
142
+ @pool.sources.each {|source| documenter << source }
143
+ documenter.only(options[:documented_classes]).generate(@output_dir + "/docs")
144
+ checkpoint(:documentation)
145
+ end
146
+
147
+ def validate_sources
148
+ validators_map = {"mooforge" => Jsus::Util::Validator::Mooforge}
149
+ (options[:validators] || []).each do |validator_name|
150
+ if validator = validators_map[validator_name]
151
+ errors = validator.new(@pool.sources.to_a & @package.source_files.to_a).validation_errors
152
+ unless errors.empty?
153
+ Jsus.logger.info "Validator #{validator_name} found errors: " <<
154
+ errors.map {|e| Jsus.logger.info " * #{e}"}.join("\n")
155
+ end
156
+ else
157
+ Jsus.logger.info "No such validator: #{validator_name}"
158
+ end
159
+ end
160
+ checkpoint(:validators)
161
+ end
162
+
163
+ def output_benchmarks
164
+ if options[:benchmark]
165
+ message = "Benchmarking results:\n"
166
+ message << "Total execution time: #{formatted_time_for(:all)}\n"
167
+ message << "\n"
168
+ message << "Of them:\n"
169
+ message << "Pool preloading time: #{formatted_time_for(:pool)}\n" if checkpoint?(:pool)
170
+ message << "Docs generation time: #{formatted_time_for(:documentation)}\n" if checkpoint?(:documentation)
171
+ message << "Total compilation time: #{formatted_time_for(:compilation)}\n" if checkpoint?(:compilation)
172
+ message << "Post-processing time: #{formatted_time_for(:postproc)}\n" if checkpoint?(:postproc)
173
+ message << "Compression time: #{formatted_time_for(:compress)}\n" if checkpoint?(:compress)
174
+ message << "\n"
175
+ message << "Compression ratio: #{sprintf("%.2f%%", @compression_ratio * 100)}\n" if Jsus.verbose? && @compression_ratio
176
+ Jsus.logger.info message
177
+ end
178
+ end
179
+
180
+ def checkpoint(checkpoint_name)
181
+ @checkpoints ||= {}
182
+ @time_for ||= {}
183
+ @checkpoints[checkpoint_name] = Time.now
184
+ if @last_checkpoint
185
+ @time_for[checkpoint_name] = @checkpoints[checkpoint_name] - @last_checkpoint
186
+ end
187
+ @last_checkpoint = Time.now
188
+ end
189
+
190
+ def checkpoint?(checkpoint_name)
191
+ @checkpoints[checkpoint_name]
192
+ end
193
+
194
+ def time_for(checkpoint_name)
195
+ if checkpoint_name == :all
196
+ @last_checkpoint - @checkpoints[:start]
197
+ else
198
+ @time_for[checkpoint_name]
199
+ end
200
+ end
201
+
202
+ def formatted_time_for(checkpoint_name)
203
+ "#{format("%.3f", time_for(checkpoint_name))}s"
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,28 @@
1
+ module Jsus::Compiler
2
+ # Handles main features of jsus
3
+ extend self
4
+
5
+ def post_process!(content, postproc)
6
+ postproc.each do |processor|
7
+ case processor.strip
8
+ when /^moocompat12$/i
9
+ content.gsub!(/\/\/<1.2compat>.*?\/\/<\/1.2compat>/m, '')
10
+ content.gsub!(/\/\*<1.2compat>\*\/.*?\/\*<\/1.2compat>\*\//m, '')
11
+ when /^mooltie8$/i
12
+ content.gsub!(/\/\/<ltIE8>.*?\/\/<\/ltIE8>/m, '')
13
+ content.gsub!(/\/\*<ltIE8>\*\/.*?\/\*<\/ltIE8>\*\//m, '')
14
+ else
15
+ Jsus.logger.error "Unknown post-processor: #{processor}"
16
+ end
17
+ end
18
+ end
19
+
20
+ def generate_includes(package, includes_root, output_file)
21
+ File.open(output_file, "w") do |f|
22
+ c = Jsus::Container.new(*(package.source_files.to_a + package.linked_external_dependencies.to_a))
23
+ paths = c.required_files(includes_root)
24
+ f.puts Jsus::Util::CodeGenerator.generate_includes(paths)
25
+ end
26
+ end
27
+
28
+ end