Class: Sass::Plugin::Compiler

Inherits:
Object
  • Object
show all
Extended by:
Callbacks
Includes:
Configuration
Defined in:
lib/sass/plugin/compiler.rb

Overview

The Compiler class handles compilation of multiple files and/or directories, including checking which CSS files are out-of-date and need to be updated and calling Sass to perform the compilation on those files.

Sass::Plugin uses this class to update stylesheets for a single application. Unlike Sass::Plugin, though, the Compiler class has no global state, and so multiple instances may be created and used independently.

If you need to compile a Sass string into CSS, please see the Engine class.

Unlike Sass::Plugin, this class doesn't keep track of whether or how many times a stylesheet should be updated. Therefore, the following Sass::Plugin options are ignored by the Compiler:

  • :never_update
  • :always_check

Instance Method Summary collapse

Methods included from Callbacks

define_callback, extended

Methods included from Configuration

#add_template_location, #default_options, #options, #remove_template_location, #reset!, #template_location_array

Constructor Details

#initialize(opts = {}) ⇒ Compiler

Creates a new compiler.

Parameters:



35
36
37
38
# File 'lib/sass/plugin/compiler.rb', line 35

def initialize(opts = {})
  @watched_files = Set.new
  options.merge!(opts)
end

Instance Method Details

#clean(individual_files = [])

Remove all output files that would be created by calling update_stylesheets, if they exist.

This method runs the deleting_css and deleting_sourcemap callbacks for the files that are deleted.

Parameters:

  • individual_files (Array<(String, String[, String])>) (defaults to: [])

    A list of files to check for updates in addition to those specified by the :template_location option. The first string in each pair is the location of the Sass/SCSS file, the second is the location of the CSS file that it should be compiled to. The third string, if provided, is the location of the Sourcemap file.



362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/sass/plugin/compiler.rb', line 362

def clean(individual_files = [])
  file_list(individual_files).each do |(_, css_file, sourcemap_file)|
    if File.exist?(css_file)
      run_deleting_css css_file
      File.delete(css_file)
    end
    if sourcemap_file && File.exist?(sourcemap_file)
      run_deleting_sourcemap sourcemap_file
      File.delete(sourcemap_file)
    end
  end
  nil
end

#engine_options(additional_options = {}) ⇒ {Symbol => Object}

Non-destructively modifies Sass::Plugin::Configuration#options so that default values are properly set, and returns the result.

Parameters:

Returns:

  • ({Symbol => Object})

    The modified options hash



337
338
339
340
341
342
343
# File 'lib/sass/plugin/compiler.rb', line 337

def engine_options(additional_options = {})
  opts = options.merge(additional_options)
  opts[:load_paths] = load_paths(opts)
  options[:sourcemap] = :auto if options[:sourcemap] == true
  options[:sourcemap] = :none if options[:sourcemap] == false
  opts
end

#file_list(individual_files = []) ⇒ Array<(String, String, String)>

Construct a list of files that might need to be compiled from the provided individual_files and the template_locations.

Note: this method does not cache the results as they can change across invocations when sass files are added or removed.

Parameters:

  • individual_files (Array<(String, String[, String])>) (defaults to: [])

    A list of files to check for updates in addition to those specified by the :template_location option. The first string in each pair is the location of the Sass/SCSS file, the second is the location of the CSS file that it should be compiled to. The third string, if provided, is the location of the Sourcemap file.

Returns:

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

    A list of [sass_file, css_file, sourcemap_file] tuples similar to what was passed in, but expanded to include the current state of the directories being updated.



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/sass/plugin/compiler.rb', line 240

def file_list(individual_files = [])
  files = individual_files.map do |tuple|
    if engine_options[:sourcemap] == :none
      tuple[0..1]
    elsif tuple.size < 3
      [tuple[0], tuple[1], Sass::Util.sourcemap_name(tuple[1])]
    else
      tuple.dup
    end
  end

  template_location_array.each do |template_location, css_location|
    Sass::Util.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
      # Get the relative path to the file
      name = Sass::Util.relative_path_from(file, template_location).to_s
      css = css_filename(name, css_location)
      sourcemap = Sass::Util.sourcemap_name(css) unless engine_options[:sourcemap] == :none
      files << [file, css, sourcemap]
    end
  end
  files
end

#stylesheet_needs_update?(css_file, template_file) ⇒ Boolean

Compass expects this to exist

Returns:

  • (Boolean)


346
347
348
# File 'lib/sass/plugin/compiler.rb', line 346

def stylesheet_needs_update?(css_file, template_file)
  StalenessChecker.stylesheet_needs_update?(css_file, template_file)
end

#update_stylesheets(individual_files = [])

Updates out-of-date stylesheets.

Checks each Sass/SCSS file in :template_location to see if it's been modified more recently than the corresponding CSS file in :css_location. If it has, it updates the CSS file.

Parameters:

  • individual_files (Array<(String, String[, String])>) (defaults to: [])

    A list of files to check for updates in addition to those specified by the :template_location option. The first string in each pair is the location of the Sass/SCSS file, the second is the location of the CSS file that it should be compiled to. The third string, if provided, is the location of the Sourcemap file.



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/sass/plugin/compiler.rb', line 201

def update_stylesheets(individual_files = [])
  Sass::Plugin.checked_for_updates = true
  staleness_checker = StalenessChecker.new(engine_options)

  files = file_list(individual_files)
  run_updating_stylesheets(files)

  updated_stylesheets = []
  files.each do |file, css, sourcemap|
    # TODO: Does staleness_checker need to check the sourcemap file as well?
    if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
      # XXX For consistency, this should return the sourcemap too, but it would
      # XXX be an API change.
      updated_stylesheets << [file, css]
      update_stylesheet(file, css, sourcemap)
    else
      run_not_updating_stylesheet(file, css, sourcemap)
    end
  end
  run_updated_stylesheets(updated_stylesheets)
end

#watch(individual_files = [], options = {})

Watches the template directory (or directories) and updates the CSS files whenever the related Sass/SCSS files change. watch never returns.

Whenever a change is detected to a Sass/SCSS file in :template_location, the corresponding CSS file in :css_location will be recompiled. The CSS files of any Sass/SCSS files that import the changed file will also be recompiled.

Before the watching starts in earnest, watch calls #update_stylesheets.

Note that watch uses the Listen library to monitor the filesystem for changes. Listen isn't loaded until watch is run. The version of Listen distributed with Sass is loaded by default, but if another version has already been loaded that will be used instead.

Parameters:

  • individual_files (Array<(String, String[, String])>) (defaults to: [])

    A list of files to check for updates in addition to those specified by the :template_location option. The first string in each pair is the location of the Sass/SCSS file, the second is the location of the CSS file that it should be compiled to. The third string, if provided, is the location of the Sourcemap file.

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

    The options that control how watching works.

Options Hash (options):

  • :skip_initial_update (Boolean)

    Don't do an initial update when starting the watcher when true



291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/sass/plugin/compiler.rb', line 291

def watch(individual_files = [], options = {})
  @inferred_directories = []
  options, individual_files = individual_files, [] if individual_files.is_a?(Hash)
  update_stylesheets(individual_files) unless options[:skip_initial_update]

  directories = watched_paths
  individual_files.each do |(source, _, _)|
    source = File.expand_path(source)
    @watched_files << Sass::Util.realpath(source).to_s
    @inferred_directories << File.dirname(source)
  end

  directories += @inferred_directories
  directories = remove_redundant_directories(directories)

  # TODO: Keep better track of what depends on what
  # so we don't have to run a global update every time anything changes.
  # XXX The :additional_watch_paths option exists for Compass to use until
  # a deprecated feature is removed. It may be removed without warning.
  directories += Array(options[:additional_watch_paths])

  options = {
    :relative_paths => false,
    # The native windows listener is much slower than the polling option, according to
    # https://github.com/nex3/sass/commit/a3031856b22bc834a5417dedecb038b7be9b9e3e
    :force_polling => @options[:poll] || Sass::Util.windows?
  }

  listener = create_listener(*directories, options) do |modified, added, removed|
    on_file_changed(individual_files, modified, added, removed)
    yield(modified, added, removed) if block_given?
  end

  begin
    listener.start
    sleep
  rescue Interrupt
    # Squelch Interrupt for clean exit from Listen::Listener
  end
end