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.

Parameters:

  • filename (String)

    the CoffeeScript file n

  • options (Hash)

    the options for the execution

Options Hash (options):

  • :source_map (Boolean)

    generate the source map files

  • :pre_process (Proc)

    Proc or lambda to run with the file content before compilation

Returns:

  • (Array<String, String>)

    the JavaScript source and the source map



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

def compile(filename, options)
  file = File.read(filename)
  file = options[:pre_process].call(file) if options[:pre_process].is_a? Proc
  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.

Parameters:

  • files (Array<String>)

    the files to compile

  • options (Hash)

    the options for the execution

Returns:

  • (Array<Array<String>, Array<String>] the result for the compilation run)

    Array, Array] the result for the compilation run



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

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, ::CoffeeScript::EngineError, ::CoffeeScript::CompilationError => 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.

Parameters:

  • files (Array<String>)

    the CoffeeScript files

  • patterns (Array<Regexp>)

    the patterns in the block

  • options (Hash)

    the options for the execution

Options Hash (options):

  • :output (String)

    the output directory

  • :shallow (Boolean)

    do not create nested directories



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

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

Parameters:

  • file (string)

    the CoffeeScript file name

  • directory (String)

    the output directory



212
213
214
# File 'lib/guard/coffeescript/runner.rb', line 212

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.

Parameters:

  • changed_files (Array<String>)

    the changed JavaScript files

  • errors (Array<String>)

    the error messages

  • options (Hash) (defaults to: {})

    the options for the execution

Options Hash (options):

  • :hide_success (Boolean)

    hide success message notification

  • :noop (Boolean)

    do not generate an output file



253
254
255
256
257
258
259
260
261
262
263
# File 'lib/guard/coffeescript/runner.rb', line 253

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.

Parameters:

  • files (Array<String>)

    the generated files

  • options (Hash)

    the options for the execution

Options Hash (options):

  • :noop (Boolean)

    do not generate an output file



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

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.

Parameters:

  • file (String)

    the CoffeeScript file

  • options (Hash)

    the options for the execution of all files

Options Hash (options):

  • :bare (Boolean)

    do not wrap the output in a top level function

Returns:

  • (Hash)

    options for a particular file's execution



142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/guard/coffeescript/runner.rb', line 142

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.

Parameters:

  • filename (String)

    the CoffeeScript filename

  • options (Hash)

    the options for the execution



161
162
163
164
165
166
167
168
169
170
171
# File 'lib/guard/coffeescript/runner.rb', line 161

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.

Parameters:

  • files (Array<String>)

    the spec files or directories

  • patterns (Array<Regexp>)

    the patterns in the block

  • options (Hash) (defaults to: {})

    the options for the removal

Options Hash (options):

  • :output (String)

    the output directory

  • :shallow (Boolean)

    do not create nested directories



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

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.

Parameters:

  • files (Array<String>)

    the spec files or directories

  • patterns (Array<Regexp>)

    the patterns in the block

  • options (Hash) (defaults to: {})

    the options for the execution

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

  • :pre_process (Proc)

    Proc or lambda to run with the file content before compilation

Returns:

  • (Array<Array<String>, Boolean>)

    the result for the compilation run



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

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.

Parameters:

  • js (String)

    the JavaScript content

  • map (String)

    the source map content

  • file (String)

    the CoffeeScript file name

  • directory (String)

    the output directory

  • options (Hash)

    the options for the execution

Options Hash (options):

  • :noop (Boolean)

    do not generate an output file

Returns:

  • (String)

    the JavaScript file name



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

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