Class: Jsus::CLI

Inherits:
Object
  • Object
show all
Defined in:
bin/jsus

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = Jsus::CLI.cli_options) ⇒ CLI

Returns a new instance of CLI.



195
196
197
# File 'bin/jsus', line 195

def initialize(options = Jsus::CLI.cli_options)
  @options = options
end

Class Attribute Details

.cli_optionsObject

Returns the value of attribute cli_options.



26
27
28
# File 'bin/jsus', line 26

def cli_options
  @cli_options
end

Instance Attribute Details

#optionsObject

Returns the value of attribute options.



193
194
195
# File 'bin/jsus', line 193

def options
  @options
end

Class Method Details

.launch!Object



128
129
130
# File 'bin/jsus', line 128

def launch!
  new.launch
end

.parse_command_line!Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'bin/jsus', line 28

def parse_command_line!
  options = {}
  cli = OptionParser.new do |opts|
    opts.banner = "jsus #{Jsus.version}. Usage: jsus [options] <input_dir> <output_dir>"

    opts.on('-i', '--input-directory [DIR]', '[DEPRECATED] path to input directory ') do |dir|
      $stderr.puts "DEPRECATION NOTICE: please do not use -i command-line argument"
      options[:input_dir] = dir
    end

    opts.on('-o', '--output-directory [DIR]', '[DEPRECATED] path to output directory ') do |dir|
      $stderr.puts "DEPRECATION NOTICE: please do not use -o command-line argument"
      options[:output_dir] = dir
    end

    opts.on('-d', '--with-dependencies [DEPS]', 'path to directory containing dependency packages')  do |dir|
      options[:deps_dir] = dir
    end

    opts.on('-g', '--generate-includes [ROOT]', 'generates includes.js file that you may use for ad-hoc requiring of dependencies, defaults to output directory') do |dir|
      options[:generate_includes] = true
      options[:includes_root] = dir
    end

    opts.on('--generate-docs [*CLASSES]', Array, "generate docs for some of the sources. When given empty array, defaults to /**/*") do |docs|
      if !docs
        options[:documented_classes] = ["/**/*"]
      else
        options[:documented_classes] = docs
      end
    end

    opts.on('--no-syntax-highlight', 'if you turned on docs generation, it will use syntax highlighting by default. This option prevents it') do
      options[:no_syntax_highlight] = true
    end

    opts.on('--validate-with [*VALIDATORS]', Array, 'performs a check against some of the validators. Available validators: mooforge') do |validators|
      options[:validators] = (validators || []).map {|v| v.downcase }
    end

    opts.on('--postproc [*PROCESSORS]', Array, 'performs postprocessing. Available postprocs:\n* moocompat12 -- removes mootools 1.2compat tags and their contents\n* mooltIE8 -- removes mootools ltIE8 compat tags and their contents') do |postprocs|
      options[:postproc] = postprocs
    end

    opts.on('--compress', 'compresses resulting file with YUI compressor') do
      options[:compress] = true
    end

    opts.on('--no-cycle-search', 'disables search for circular dependencies which may take quite some time during compilation') do
      Jsus.look_for_cycles = false
    end

    opts.on_tail('-v', '--verbose', 'verbose mode, shows various debug messages') do
      Jsus.verbose = true
    end

    opts.on_tail('--very-verbose', 'very verbose mode, shows pool stats beside usual verbose stuff') do
      Jsus.verbose = true
      options[:display_pool_stats] = true
    end

    opts.on_tail('-b', '--benchmark', 'shows time spent on various stages') do
      options[:benchmark] = true
    end

    opts.on_tail('--without-scripts-info', 'do not generate scripts.json') do
      options[:without_scripts_info] = true
    end

    opts.on_tail('--without-tree-info', 'do not generate tree.json') do
      options[:without_tree_info] = true
    end

    opts.on_tail('--watch', 'watch file system events for *.js files in subdirectories and rerun jsus with the same parameters') do
      options[:watch] = true
    end

    opts.on_tail('-h', '--help', 'Show this message') do
      puts opts
      exit
    end
  end
  cli.parse!

  options[:input_dir]  ||= ARGV[0]
  options[:output_dir] ||= ARGV[1]

  if !(options[:input_dir] && options[:output_dir])
    puts cli
    exit
  end
  options[:input_dir]  = File.expand_path(options[:input_dir])
  options[:output_dir] = File.expand_path(options[:output_dir])
  self.cli_options = options
end

.run!Object



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'bin/jsus', line 132

def run!
  parse_command_line!
  launch!
  if watch?
    watch do |base, match|
      full_path = File.join(base, match)
      unless full_path.include?(cli_options[:output_dir])
        puts "#{match} has changed, relaunching jsus..."
        begin
          launch!
          puts "... done"
        rescue Exception => e
          puts "Exception happened: #{e}, #{e.inspect}"
          puts "\t#{e.backtrace.join("\n\t")}" if Jsus.verbose?
          puts "Compilation FAILED."
        end
        puts ""
      end
    end
  end
end

.watchObject



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'bin/jsus', line 154

def watch
  require 'fssm'
  puts "Jsus enters watch mode, it will watch your files for changes and relaunch itself"
  puts ""
  start_directory = Dir.pwd
  watched_dirs = [cli_options[:input_dir], cli_options[:deps_dir]].compact.map {|path| File.expand_path(path)}

  watched_dirs.reject! do |dir|
    # This is a work around for rb-fsevent quirk
    # Apparently, when your dependency dir is a child directory for your input dir,
    # You get problems.
    result = false
    pathname_traversal = Pathname.new(dir).descend do |parent|
      parent = parent.to_s
      result ||= watched_dirs.include?(parent) && parent != dir
    end
    result
  end

  puts "Watching directories: " + watched_dirs.inspect if Jsus.verbose?
  FSSM.monitor do
    watched_dirs.each do |dir|
      path dir do
        glob ["**/*.js", "**/package.yml", "**/package.json"]
        update {|base, relative| yield base, relative }
        delete {|base, relative| yield base, relative }
        create {|base, relative| yield base, relative }
      end
    end
  end

rescue LoadError => e
  puts "You need to install fssm gem for --watch option."
  puts "You may also want to install rb-fsevent for OS X"
  raise e
end

.watch?Boolean

Returns:

  • (Boolean)


124
125
126
# File 'bin/jsus', line 124

def watch?
  cli_options[:watch]
end

Instance Method Details

#checkpoint(checkpoint_name) ⇒ Object



352
353
354
355
356
357
358
359
360
# File 'bin/jsus', line 352

def checkpoint(checkpoint_name)
  @checkpoints ||= {}
  @time_for    ||= {}
  @checkpoints[checkpoint_name] = Time.now
  if @last_checkpoint
    @time_for[checkpoint_name] = @checkpoints[checkpoint_name] - @last_checkpoint
  end
  @last_checkpoint = Time.now
end

#checkpoint?(checkpoint_name) ⇒ Boolean

Returns:

  • (Boolean)


362
363
364
# File 'bin/jsus', line 362

def checkpoint?(checkpoint_name)
  @checkpoints[checkpoint_name]
end

#compile_packageObject



261
262
263
264
# File 'bin/jsus', line 261

def compile_package
  @package_content = @package.compile(nil)
  checkpoint(:compilation)
end

#compress_packageObject



282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'bin/jsus', line 282

def compress_package
  compressed_content = Jsus::Util::Compressor.new(@package_content).result
  if compressed_content != ""
    @compression_ratio = compressed_content.size.to_f / @package_content.size.to_f
    compressed_file_name = @package.filename.sub(/.js$/, ".min.js")
    File.open(File.join(@output_dir, compressed_file_name), "w") {|f| f.write(compressed_content) }
  else
    @compression_ratio = 1.00
    puts "ERROR: YUI compressor could not parse input. "
    puts "Compressor command used: #{compressor.command.join(' ')}"
  end
  checkpoint(:compress)
end

#display_package(package) ⇒ Object



251
252
253
254
255
256
257
258
259
# File 'bin/jsus', line 251

def display_package(package)
  puts "Package: #{package.name}"
  package.source_files.to_a.sort_by {|sf| sf.filename}.each do |sf|
    puts "    [#{sf.relative_filename}]"
    puts "        Provides: [#{sf.provides_names.join(", ")}]"
    puts "        Requires: [#{sf.requires_names.join(", ")}]"
  end
  puts ""
end

#display_pool_statsObject



237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'bin/jsus', line 237

def display_pool_stats
  checkpoint(:pool_stats)
  puts ""
  puts "Pool stats:"
  puts ""
  puts "Main package:"
  display_package @package
  puts "Supplementary packages:"
  @pool.packages.each do |package|
    display_package package
  end
  puts ""
end

#formatted_time_for(checkpoint_name) ⇒ Object



374
375
376
# File 'bin/jsus', line 374

def formatted_time_for(checkpoint_name)
  "#{format("%.3f", time_for(checkpoint_name))}s"
end

#generate_docsObject



312
313
314
315
316
317
318
# File 'bin/jsus', line 312

def generate_docs
  documenter = Jsus::Util::Documenter.new(:highlight_source => !options[:no_syntax_highlight])
  @package.source_files.each {|source| documenter << source }
  @pool.sources.each {|source| documenter << source }
  documenter.only(options[:documented_classes]).generate(@output_dir + "/docs")
  checkpoint(:documentation)
end

#generate_includesObject



302
303
304
305
306
307
308
309
310
# File 'bin/jsus', line 302

def generate_includes
  includes_root = options[:includes_root] || @output_dir
  File.open(File.join(@output_dir, "includes.js"), "w") do |f|
    c = Jsus::Container.new(*(@package.source_files.to_a + @package.linked_external_dependencies.to_a))
    paths = c.required_files(includes_root)
    f.puts Jsus::Util::CodeGenerator.generate_includes(paths)
  end
  checkpoint(:includes)
end

#generate_supplemental_filesObject



296
297
298
299
300
# File 'bin/jsus', line 296

def generate_supplemental_files
  @package.generate_scripts_info(@output_dir) unless options[:without_scripts_info]
  @package.generate_tree(@output_dir) unless options[:without_tree_info]
  checkpoint(:supplemental_files)
end

#launchObject



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'bin/jsus', line 199

def launch
  checkpoint(:start)
  setup_output_directory
  preload_pool
  load_package
  display_pool_stats if options[:display_pool_stats]
  compile_package
  post_process if options[:postproc]
  compress_package if options[:compress]
  package_filename = File.join(@output_dir, @package.filename)
  File.open(package_filename, 'w') {|f| f << @package_content  }
  generate_supplemental_files
  validate_sources
  generate_includes if options[:generate_includes]
  generate_docs if options[:documented_classes] && !options[:documented_classes].empty?
  output_benchmarks
end

#load_packageObject



231
232
233
234
235
# File 'bin/jsus', line 231

def load_package
  @package = Jsus::Package.new(options[:input_dir], :pool => @pool)
  @package.include_dependencies!
  checkpoint(:dependencies)
end

#output_benchmarksObject



336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
# File 'bin/jsus', line 336

def output_benchmarks
  if options[:benchmark]
    puts "Benchmarking results:"
    puts "Total execution time:   #{formatted_time_for(:all)}"
    puts ""
    puts "Of them:"
    puts "Pool preloading time:   #{formatted_time_for(:pool)}"
    puts "Docs generation time:   #{formatted_time_for(:documentation)}" if options[:documented_classes] && !options[:documented_classes].empty?
    puts "Total compilation time: #{formatted_time_for(:compilation)}"
    puts "Post-processing time:   #{formatted_time_for(:postproc)}" if options[:postproc]
    puts "Compression time:       #{formatted_time_for(:compress)}" if options[:compress]
    puts ""
    puts "Compression ratio: #{sprintf("%.2f%%", @compression_ratio * 100)}" if Jsus.verbose? && @compression_ratio
  end
end

#post_processObject



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'bin/jsus', line 266

def post_process
  options[:postproc].each do |processor|
    case processor.strip
    when /^moocompat12$/i
      @package_content.gsub!(/\/\/<1.2compat>.*?\/\/<\/1.2compat>/m, '')
      @package_content.gsub!(/\/\*<1.2compat>\*\/.*?\/\*<\/1.2compat>\*\//m, '')
    when /^mooltie8$/i
      @package_content.gsub!(/\/\/<ltIE8>.*?\/\/<\/ltIE8>/m, '')
      @package_content.gsub!(/\/\*<ltIE8>\*\/.*?\/\*<\/ltIE8>\*\//m, '')
    else
      $stderr.puts "Unknown post-processor: #{processor}"
    end
  end
  checkpoint(:postproc)
end

#preload_poolObject



222
223
224
225
226
227
228
229
# File 'bin/jsus', line 222

def preload_pool
  @pool = if options[:deps_dir]
    Jsus::Pool.new(options[:deps_dir])
  else
    Jsus::Pool.new
  end
  checkpoint(:pool)
end

#setup_output_directoryObject



217
218
219
220
# File 'bin/jsus', line 217

def setup_output_directory
  @output_dir = options[:output_dir]
  FileUtils.mkdir_p(@output_dir)
end

#time_for(checkpoint_name) ⇒ Object



366
367
368
369
370
371
372
# File 'bin/jsus', line 366

def time_for(checkpoint_name)
  if checkpoint_name == :all
    @last_checkpoint - @checkpoints[:start]
  else
    @time_for[checkpoint_name]
  end
end

#validate_sourcesObject



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'bin/jsus', line 320

def validate_sources
  validators_map = {"mooforge" => Jsus::Util::Validator::Mooforge}
  (options[:validators] || []).each do |validator_name|
    if validator = validators_map[validator_name]
      errors = validator.new(@pool.sources.to_a & @package.source_files.to_a).validation_errors
      unless errors.empty?
        puts "Validator #{validator_name} found errors: "
        errors.each {|e| puts "  * #{e}"}
      end
    else
      puts "No such validator: #{validator_name}"
    end
  end
  checkpoint(:validators)
end