Class: Compass::Compiler

Inherits:
Object
  • Object
show all
Includes:
Actions
Defined in:
lib/compass/compiler.rb

Instance Attribute Summary collapse

Attributes included from Actions

#logger

Instance Method Summary collapse

Methods included from Actions

#basename, #copy, #directory, #log_action, #process_erb, #relativize, #remove, #separate, #strip_trailing_separator, #write_file

Constructor Details

#initialize(working_path, from, to, options) ⇒ Compiler

Returns a new instance of Compiler.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/compass/compiler.rb', line 10

def initialize(working_path, from, to, options)
  Compass::Deprecation.deprecated!(:compass_compiler_constructor,
    "Compass::Compiler is deprecated. Use Compass::SassCompiler instead.")
  self.working_path = working_path.to_s
  self.from, self.to = File.expand_path(from), to
  self.logger = options.delete(:logger)
  sass_opts = options.delete(:sass) || {}
  self.options = options
  self.sass_options = options.dup
  self.sass_options.delete(:quiet)
  self.sass_options.update(sass_opts)
  self.sass_options[:cache_location] ||= determine_cache_location
  self.sass_options[:filesystem_importer] ||= Sass::Importers::Filesystem
  self.sass_options[:importer] = self.importer = self.sass_options[:filesystem_importer].new(from)
  self.sass_options[:compass] ||= {}
  self.sass_options[:compass][:logger] = self.logger
  self.sass_options[:compass][:environment] = Compass.configuration.environment
  self.sass_options[:compass][:compiler_in_use] = true
  reset_staleness_checker!
end

Instance Attribute Details

#fromObject

Returns the value of attribute from.



8
9
10
# File 'lib/compass/compiler.rb', line 8

def from
  @from
end

#importerObject

Returns the value of attribute importer.



8
9
10
# File 'lib/compass/compiler.rb', line 8

def importer
  @importer
end

#optionsObject

Returns the value of attribute options.



8
9
10
# File 'lib/compass/compiler.rb', line 8

def options
  @options
end

#sass_optionsObject

Returns the value of attribute sass_options.



8
9
10
# File 'lib/compass/compiler.rb', line 8

def sass_options
  @sass_options
end

#staleness_checkerObject

Returns the value of attribute staleness_checker.



8
9
10
# File 'lib/compass/compiler.rb', line 8

def staleness_checker
  @staleness_checker
end

#toObject

Returns the value of attribute to.



8
9
10
# File 'lib/compass/compiler.rb', line 8

def to
  @to
end

#working_pathObject

Returns the value of attribute working_path.



8
9
10
# File 'lib/compass/compiler.rb', line 8

def working_path
  @working_path
end

Instance Method Details

#clean!Object



108
109
110
111
112
113
114
# File 'lib/compass/compiler.rb', line 108

def clean!
  remove options[:cache_location]
  css_files.zip(sourcemap_files).each do |css_file, sourcemap_file|
    remove css_file
    remove sourcemap_file
  end
end

#compile(sass_filename, css_filename, sourcemap_filename = nil) ⇒ Object

Compile one Sass file



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 'lib/compass/compiler.rb', line 166

def compile(sass_filename, css_filename, sourcemap_filename = nil)
  css_content, sourcemap = logger.red do
    timed(lambda {|r| r[0]}) do
      engine = engine(sass_filename, css_filename, sourcemap_filename)
      if sourcemap_filename && options[:sourcemap]
        engine.render_with_sourcemap(relative_path(css_filename, sourcemap_filename))
      else
        [engine.render, nil]
      end
    end
  end
  duration = options[:time] ? "(#{(css_content.__duration * 1000).round / 1000.0}s)" : ""
  write_file(css_filename, css_content, options.merge(:force => true, :extra => duration), sass_options[:unix_newlines])
  Compass.configuration.run_stylesheet_saved(css_filename)
  if sourcemap && sourcemap_filename
    sourcemap_content = sourcemap.to_json(:css_path => css_filename,
                                          :sourcemap_path => sourcemap_filename)
    write_file(sourcemap_filename, sourcemap_content, options.merge(:force => true), sass_options[:unix_newlines])
    Compass.configuration.run_sourcemap_saved(sourcemap_filename)
  elsif sourcemap_filename && File.exist?(sourcemap_filename)
    remove sourcemap_filename
    Compass.configuration.run_sourcemap_removed(sourcemap_filename)
  end
end

#compile_if_required(sass_filename, css_filename, sourcemap_filename = nil) ⇒ Object



144
145
146
147
148
149
150
151
# File 'lib/compass/compiler.rb', line 144

def compile_if_required(sass_filename, css_filename, sourcemap_filename = nil)
  if should_compile?(sass_filename, css_filename, sourcemap_filename)
    compile sass_filename, css_filename, sourcemap_filename
  else
    logger.record :unchanged, basename(sass_filename) unless options[:quiet]
    remove(sourcemap_filename) if sourcemap_filename && !options[:sourcemap]
  end
end

#corresponding_css_file(sass_file) ⇒ Object



66
67
68
# File 'lib/compass/compiler.rb', line 66

def corresponding_css_file(sass_file)
  "#{to}/#{stylesheet_name(sass_file)}.css"
end

#corresponding_sourcemap_file(sass_file) ⇒ Object



70
71
72
# File 'lib/compass/compiler.rb', line 70

def corresponding_sourcemap_file(sass_file)
  "#{to}/#{stylesheet_name(sass_file)}.css.map"
end

#css_filesObject



58
59
60
# File 'lib/compass/compiler.rb', line 58

def css_files
  @css_files ||= sass_files.map{|sass_file| corresponding_css_file(sass_file)}
end

#determine_cache_locationObject



37
38
39
# File 'lib/compass/compiler.rb', line 37

def determine_cache_location
  Compass.configuration.cache_path || Sass::Plugin.options[:cache_location] || File.join(working_path, ".sass-cache")
end

#engine(sass_filename, css_filename, sourcemap_filename = nil) ⇒ Object

A sass engine for compiling a single file.



204
205
206
207
208
209
210
211
# File 'lib/compass/compiler.rb', line 204

def engine(sass_filename, css_filename, sourcemap_filename = nil)
  syntax = (sass_filename =~ /\.(s[ac]ss)$/) && $1.to_sym || :sass
  opts = sass_options.merge(:filename => sass_filename,
                            :css_filename => css_filename,
                            :syntax => syntax,
                            :sourcemap_filename => sourcemap_filename)
  Sass::Engine.new(open(sass_filename).read, opts)
end

#error_contents(e, sass_filename) ⇒ Object

Haml refactored this logic in 2.3, this is backwards compatibility for either one



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/compass/compiler.rb', line 227

def error_contents(e, sass_filename)
  if show_full_exception?
    e.sass_template = sass_filename
    Sass::SyntaxError.exception_to_css(e)
  else
    header = Sass::SyntaxError.send(:header_string, e, 1)
    <<END
/*
#{header.gsub("*/", "*\\/")}

Backtrace:\n#{e.backtrace.join("\n").gsub("*/", "*\\/")}
*/
END
  end
end

#handle_exception(sass_filename, css_filename, e) ⇒ Object

Place the syntax error into the target css file, formatted to display in the browser (in development mode) if there’s an error.



216
217
218
219
220
221
222
223
224
# File 'lib/compass/compiler.rb', line 216

def handle_exception(sass_filename, css_filename, e)
  exception_file = basename(e.sass_filename || sass_filename)
  file = basename(sass_filename)
  exception_file = nil if exception_file == file
  formatted_error = "(Line #{e.sass_line}#{ " of #{exception_file}" if exception_file}: #{e.message})"
  logger.record :error, file, formatted_error
  Compass.configuration.run_stylesheet_error(sass_filename, formatted_error)
  write_file css_filename, error_contents(e, sass_filename), options.merge(:force => true), sass_options[:unix_newlines]
end

#needs_update?(css_filename, sass_filename) ⇒ Boolean

Returns:

  • (Boolean)


86
87
88
# File 'lib/compass/compiler.rb', line 86

def needs_update?(css_filename, sass_filename)
  staleness_checker.stylesheet_needs_update?(css_filename, File.expand_path(sass_filename), importer)
end

#new_config?Boolean

Determines if the configuration file is newer than any css file

Returns:

  • (Boolean)


91
92
93
94
95
96
97
98
99
# File 'lib/compass/compiler.rb', line 91

def new_config?
  config_file = Compass.detect_configuration_file
  return false unless config_file
  config_mtime = File.mtime(config_file)
  css_files.each do |css_filename|
    return config_file if File.exists?(css_filename) && config_mtime > File.mtime(css_filename)
  end
  nil
end

#out_of_date?Boolean

Returns the sass file that needs to be compiled, if any.

Returns:

  • (Boolean)


79
80
81
82
83
84
# File 'lib/compass/compiler.rb', line 79

def out_of_date?
  sass_files.zip(css_files).each do |sass_filename, css_filename|
    return sass_filename if needs_update?(css_filename, sass_filename)
  end
  false
end

#relative_path(from_path, to_path) ⇒ Object



191
192
193
# File 'lib/compass/compiler.rb', line 191

def relative_path(from_path, to_path)
  Pathname.new(to_path).relative_path_from(Pathname.new(from_path).dirname).to_s
end

#relative_stylesheet_name(sass_file) ⇒ Object



46
47
48
# File 'lib/compass/compiler.rb', line 46

def relative_stylesheet_name(sass_file)
  sass_file[(from.length + 1)..-1]
end

#reset!Object



101
102
103
104
105
106
# File 'lib/compass/compiler.rb', line 101

def reset!
  reset_staleness_checker!
  @sass_files = nil
  @css_files = nil
  @sourcemap_files = nil
end

#reset_staleness_checker!Object



31
32
33
34
35
# File 'lib/compass/compiler.rb', line 31

def reset_staleness_checker!
  self.staleness_checker = nil
  #Sass::Plugin::StalenessChecker.dependencies_cache = {}
  self.staleness_checker = Sass::Plugin::StalenessChecker.new(sass_options)
end

#runObject



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/compass/compiler.rb', line 116

def run
  failure_count = 0
  if new_config?
    # Wipe out the cache and force compilation if the configuration has changed.
    remove options[:cache_location] if options[:cache_location]
    options[:force] = true
  end

  # Make sure the target directories exist
  target_directories.each {|dir| directory dir}

  # Compile each sass file.
  result = timed do
    sass_files.zip(css_files, sourcemap_files).each do |sass_filename, css_filename, sourcemap_filename|
      begin
        compile_if_required sass_filename, css_filename, sourcemap_filename
      rescue Sass::SyntaxError => e
        failure_count += 1
        handle_exception(sass_filename, css_filename, e)
      end
    end
  end
  if options[:time]
    puts "Compilation took #{(result.__duration * 1000).round / 1000.0}s"
  end
  return failure_count
end

#sass_files(options = {}) ⇒ Object



41
42
43
44
# File 'lib/compass/compiler.rb', line 41

def sass_files(options = {})
  exclude_partials = options.fetch(:exclude_partials, true)
  @sass_files = self.options[:sass_files] || Dir.glob(separate("#{from}/**/#{'[^_]' if exclude_partials}*.s[ac]ss"))
end

#should_compile?(sass_filename, css_filename, sourcemap_filename = nil) ⇒ Boolean

Returns:

  • (Boolean)


195
196
197
198
199
200
201
# File 'lib/compass/compiler.rb', line 195

def should_compile?(sass_filename, css_filename, sourcemap_filename = nil)
  return true if css_filename && !File.exist?(css_filename)
  return true if sourcemap_filename && options[:sourcemap] && !File.exist?(sourcemap_filename)
  options[:force] ||
    needs_update?(css_filename, sass_filename) ||
    (options[:sourcemap] && needs_update?(sourcemap_filename, sass_filename))
end

#show_full_exception?Boolean

We don’t want to show the full exception in production environments.

Returns:

  • (Boolean)


244
245
246
# File 'lib/compass/compiler.rb', line 244

def show_full_exception?
  Compass.configuration.environment == :development
end

#sourcemap_filesObject



62
63
64
# File 'lib/compass/compiler.rb', line 62

def sourcemap_files
  @sourcemap_files ||= sass_files.map{|sass_file| corresponding_sourcemap_file(sass_file)}
end

#stylesheet_name(sass_file) ⇒ Object



50
51
52
53
54
55
56
# File 'lib/compass/compiler.rb', line 50

def stylesheet_name(sass_file)
  if sass_file.index(from) == 0
    sass_file[(from.length + 1)..-6].sub(/\.css$/,'')
  else
    raise Compass::Error, "You must compile individual stylesheets from the project directory."
  end
end

#target_directoriesObject



74
75
76
# File 'lib/compass/compiler.rb', line 74

def target_directories
  css_files.map{|css_file| File.dirname(css_file)}.uniq.sort.sort_by{|d| d.length }
end

#timed(timed_thing = lambda {|res| res}) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
# File 'lib/compass/compiler.rb', line 153

def timed(timed_thing = lambda {|res| res})
  start_time = Time.now
  res = yield
  end_time = Time.now
  has_duration = timed_thing.call(res)
  has_duration.instance_variable_set("@__duration", end_time - start_time)
  def has_duration.__duration
    @__duration
  end
  res
end