Module: Guard::CoffeeScript::Runner

Defined in:
lib/guard/coffeescript/runner.rb

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.last_run_failedObject

Returns the value of attribute last_run_failed



7
8
9
# File 'lib/guard/coffeescript/runner.rb', line 7

def last_run_failed
  @last_run_failed
end

Class Method Details

.compile(filename, options) ⇒ Array<String, String> (private)

Compile the CoffeeScript and generate the source map.

Options Hash (options):

  • :source_map (Boolean)

    generate the source map files



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/guard/coffeescript/runner.rb', line 116

def compile(filename, options)
  file = File.read(filename)
  file_options = options_for_file(filename, options)

  if options[:source_map]
    file_options.merge! options_for_source_map(filename, options)
    result = ::CoffeeScript.compile(file, file_options)
    fail 'CoffeeScript.compile returned nil' if result.nil?
    js, map = result['js'], result['v3SourceMap']
  else
    js  = ::CoffeeScript.compile(file, file_options)
  end

  [js, map]
end

.compile_files(files, patterns, options) ⇒ Array<Array<String>, Array<String>] the result for the compilation run (private)

Compiles all CoffeeScript files and writes the JavaScript files.



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
# File 'lib/guard/coffeescript/runner.rb', line 81

def compile_files(files, patterns, options)
  errors        = []
  changed_files = []
  directories   = detect_nested_directories(files, patterns, options)

  directories.each do |directory, scripts|
    scripts.each do |file|
      begin
        js, map = compile(file, options)
        changed_files << write_javascript_file(js, map, file, directory, options)

      rescue RuntimeError, ExecJS::ProgramError => e
        error_message = file + ': ' + e.message.to_s

        if options[:error_to_js]
          js_error_message = "throw \"#{ error_message }\";"
          changed_files << write_javascript_file(js_error_message, nil, file, directory, options)
        end

        errors << error_message
        Formatter.error(error_message)
      end
    end
  end

  [changed_files.flatten.compact, errors]
end

.detect_nested_directories(files, patterns, options) ⇒ Object (private)

Detects the output directory for each CoffeeScript file. Builds the product of all patterns and assigns to each directory the files to which it belongs to.

Options Hash (options):

  • :output (String)

    the output directory

  • :shallow (Boolean)

    do not create nested directories



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/guard/coffeescript/runner.rb', line 223

def detect_nested_directories(files, patterns, options)
  return { options[:output] => files } if options[:shallow]

  directories = {}

  patterns.product(files).each do |pattern, file|
    next unless (matches = file.match(pattern))

    target = matches[1] ? File.join(options[:output], File.dirname(matches[1])).gsub(/\/\.$/, '') : options[:output] || File.dirname(file)
    if directories[target]
      directories[target] << file
    else
      directories[target] = [file]
    end
  end

  directories
end

.javascript_file_name(file, directory) ⇒ Object (private)

Calculates the output filename from the coffescript filename and the output directory



209
210
211
# File 'lib/guard/coffeescript/runner.rb', line 209

def javascript_file_name(file, directory)
  File.join(directory, File.basename(file.gsub(/((?:js\.)?(?:coffee|coffee\.md|litcoffee))$/, 'js')))
end

.notify_result(changed_files, errors, options = {}) ⇒ Object (private)

Writes console and system notifications about the result of the compilation.

Options Hash (options):

  • :hide_success (Boolean)

    hide success message notification

  • :noop (Boolean)

    do not generate an output file



250
251
252
253
254
255
256
257
258
259
260
# File 'lib/guard/coffeescript/runner.rb', line 250

def notify_result(changed_files, errors, options = {})
  if !errors.empty?
    self.last_run_failed = true
    Formatter.notify(errors.join("\n"), title: 'CoffeeScript results', image: :failed, priority: 2)
  elsif !options[:hide_success] || last_run_failed
    self.last_run_failed = false
    message = "Successfully #{ options[:noop] ? 'verified' : 'generated' } #{ changed_files.join(', ') }"
    Formatter.success(message)
    Formatter.notify(message, title: 'CoffeeScript results')
  end
end

.notify_start(files, options) ⇒ Object (private)

Generates a start compilation notification.

Options Hash (options):

  • :noop (Boolean)

    do not generate an output file



70
71
72
73
# File 'lib/guard/coffeescript/runner.rb', line 70

def notify_start(files, options)
  message = options[:message] || (options[:noop] ? 'Verify ' : 'Compile ') + files.join(', ')
  Formatter.info(message, reset: true)
end

.options_for_file(file, options) ⇒ Hash (private)

Gets the CoffeeScript compilation options.

Options Hash (options):

  • :bare (Boolean)

    do not wrap the output in a top level function



139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/guard/coffeescript/runner.rb', line 139

def options_for_file(file, options)
  file_options = options.clone

  # if :bare was provided an array of filenames, check for file's inclusion
  if file_options[:bare].respond_to? :include?
    filename            = file[/([^\/]*)\.(?:coffee|coffee\.md|litcoffee)$/]
    file_options[:bare] = file_options[:bare].include?(filename)
  end

  file_options[:literate] = true if file[/\.(?:coffee\.md|litcoffee)$/]

  file_options
end

.options_for_source_map(filename, options) ⇒ Object (private)

Gets the CoffeeScript source map options.



158
159
160
161
162
163
164
165
166
167
168
# File 'lib/guard/coffeescript/runner.rb', line 158

def options_for_source_map(filename, options)
  # if :input was provided, make all filenames relative to that
  filename = Pathname.new(filename).relative_path_from(Pathname.new(options[:input])).to_s if options[:input]

  {
    sourceMap: true,
    generatedFile: filename.gsub(/((?:js\.)?(?:coffee|coffee\.md|litcoffee))$/, 'js'),
    sourceFiles: [filename],
    sourceRoot: options[:source_root] || options[:input] || ''
  }
end

.remove(files, patterns, options = {}) ⇒ Object

The remove function deals with CoffeeScript file removal by locating the output javascript file and removing it.

Options Hash (options):

  • :output (String)

    the output directory

  • :shallow (Boolean)

    do not create nested directories



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/guard/coffeescript/runner.rb', line 41

def remove(files, patterns, options = {})
  removed_files = []
  directories   = detect_nested_directories(files, patterns, options)

  directories.each do |directory, scripts|
    scripts.each do |file|
      javascript = javascript_file_name(file, directory)
      if File.exist?(javascript)
        FileUtils.remove_file(javascript)
        removed_files << javascript
      end
    end
  end

  return unless removed_files.length > 0

  message = "Removed #{ removed_files.join(', ') }"
  Formatter.success(message)
  Formatter.notify(message, title: 'CoffeeScript results')
end

.run(files, patterns, options = {}) ⇒ Array<Array<String>, Boolean>

The CoffeeScript runner handles the CoffeeScript compilation, creates nested directories and the output file, writes the result to the console and triggers optional system notifications.

Options Hash (options):

  • :input (String)

    the input directory

  • :output (String)

    the output directory

  • :bare (Boolean)

    do not wrap the output in a top level function

  • :shallow (Boolean)

    do not create nested directories

  • :hide_success (Boolean)

    hide success message notification

  • :noop (Boolean)

    do not generate an output file

  • :source_map (Boolean)

    generate the source map files



25
26
27
28
29
30
# File 'lib/guard/coffeescript/runner.rb', line 25

def run(files, patterns, options = {})
  notify_start(files, options)
  changed_files, errors = compile_files(files, patterns, options)
  notify_result(changed_files, errors, options)
  [changed_files, errors.empty?]
end

.write_javascript_file(js, map, file, directory, options) ⇒ String (private)

Analyzes the CoffeeScript compilation output and creates the nested directories and writes the output file.

Options Hash (options):

  • :noop (Boolean)

    do not generate an output file



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/guard/coffeescript/runner.rb', line 181

def write_javascript_file(js, map, file, directory, options)
  directory = Dir.pwd if !directory || directory.empty?
  filename = javascript_file_name(file, directory)

  return filename if options[:noop]

  if options[:source_map]
    map_name = filename + '.map'
    js += "\n//# sourceMappingURL=#{File.basename(map_name)}\n"
  end

  FileUtils.mkdir_p(File.expand_path(directory)) unless File.directory?(directory)
  File.open(File.expand_path(filename), 'w') { |f| f.write(js) }

  if options[:source_map]
    File.open(File.expand_path(map_name), 'w') { |f| f.write(map) }
    [filename, map_name]
  else
    filename
  end
end