sass 3.3.0.alpha.201 → 3.3.0.alpha.211

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/REVISION CHANGED
@@ -1 +1 @@
1
- 3a64325d2550f0bc57f6490854c0a7d0bc5c9db5
1
+ 33078d3298227e86d5cc9628b17270b823bf9003
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.3.0.alpha.201
1
+ 3.3.0.alpha.211
@@ -1 +1 @@
1
- 17 July 2013 20:29:34 GMT
1
+ 18 July 2013 18:31:42 GMT
@@ -191,6 +191,14 @@ module Sass
191
191
  options[:filesystem_importer].new(p.to_s)
192
192
  end
193
193
 
194
+ # Remove any deprecated importers if the location is imported explicitly
195
+ options[:load_paths].reject! do |importer|
196
+ importer.is_a?(Sass::Importers::DeprecatedPath) &&
197
+ options[:load_paths].find {|other_importer| other_importer.is_a?(Sass::Importers::Filesystem) &&
198
+ other_importer != importer &&
199
+ other_importer.root == importer.root}
200
+ end
201
+
194
202
  # Backwards compatibility
195
203
  options[:property_syntax] ||= options[:attribute_syntax]
196
204
  case options[:property_syntax]
@@ -197,7 +197,7 @@ MESSAGE
197
197
  def initialize(args)
198
198
  super
199
199
  @options[:for_engine] = {
200
- :load_paths => ['.'] + (ENV['SASSPATH'] || '').split(File::PATH_SEPARATOR)
200
+ :load_paths => default_sass_path
201
201
  }
202
202
  @default_syntax = :sass
203
203
  end
@@ -433,17 +433,14 @@ MSG
433
433
  ::Sass::Plugin.on_updated_stylesheet do |_, css, sourcemap|
434
434
  [css, sourcemap].each do |file|
435
435
  next unless file
436
- if File.exists? file
437
- puts_action :overwrite, :yellow, file
438
- else
439
- puts_action :create, :green, file
440
- end
436
+ puts_action :write, :green, file
441
437
  end
442
438
  end
443
439
 
444
440
  had_error = false
445
441
  ::Sass::Plugin.on_creating_directory {|dirname| puts_action :directory, :green, dirname}
446
442
  ::Sass::Plugin.on_deleting_css {|filename| puts_action :delete, :yellow, filename}
443
+ ::Sass::Plugin.on_deleting_sourcemap {|filename| puts_action :delete, :yellow, filename}
447
444
  ::Sass::Plugin.on_compilation_error do |error, _, _|
448
445
  if error.is_a?(SystemCallError) && !@options[:stop_on_error]
449
446
  had_error = true
@@ -505,6 +502,16 @@ MSG
505
502
  return false if colon_path?(path)
506
503
  return ::Sass::Util.glob(File.join(path, "*.s[ca]ss")).empty?
507
504
  end
505
+
506
+ def default_sass_path
507
+ if ENV['SASSPATH']
508
+ # The select here prevents errors when the environment's load paths specified do not exist.
509
+ ENV['SASSPATH'].split(File::PATH_SEPARATOR).select {|d| File.directory?(d)}
510
+ else
511
+ [::Sass::Importers::DeprecatedPath.new(".")]
512
+ end
513
+ end
514
+
508
515
  end
509
516
 
510
517
  class Scss < Sass
@@ -20,3 +20,4 @@ end
20
20
 
21
21
  require 'sass/importers/base'
22
22
  require 'sass/importers/filesystem'
23
+ require 'sass/importers/deprecated_path'
@@ -144,8 +144,27 @@ module Sass
144
144
  def to_s
145
145
  Sass::Util.abstract(self)
146
146
  end
147
+
148
+ # If the importer is based on files on the local filesystem
149
+ # this method should return folders which should be watched
150
+ # for changes.
151
+ #
152
+ # @return [Array<String>] List of absolute paths of directories to watch
153
+ def directories_to_watch
154
+ []
155
+ end
156
+
157
+ # If this importer is based on files on the local filesystem This method
158
+ # should return true if the file, when changed, should trigger a
159
+ # recompile.
160
+ #
161
+ # It is acceptable for non-sass files to be watched and trigger a recompile.
162
+ #
163
+ # @param filename [String] The absolute filename for a file that has changed.
164
+ # @return [Boolean] When the file changed should cause a recompile.
165
+ def watched_file?(filename)
166
+ false
167
+ end
147
168
  end
148
169
  end
149
170
  end
150
-
151
-
@@ -0,0 +1,45 @@
1
+ module Sass
2
+ module Importers
3
+ # This importer emits a deprecation warning the first time it is used to
4
+ # import a file. It is used to deprecate the current working
5
+ # directory from the list of automatic sass load paths.
6
+ class DeprecatedPath < Filesystem
7
+
8
+ # @param root [String] The absolute, expanded path to the folder that is deprecated.
9
+ def initialize(root)
10
+ @specified_root = root
11
+ @warning_given = false
12
+ super
13
+ end
14
+
15
+ # @see Sass::Importers::Base#find
16
+ def find(*args)
17
+ found = super
18
+ if found && !@warning_given
19
+ @warning_given = true
20
+ Sass::Util.sass_warn deprecation_warning
21
+ end
22
+ found
23
+ end
24
+
25
+ # @see Sass::Importers::Base#to_s
26
+ def to_s
27
+ "#{@root} (DEPRECATED)"
28
+ end
29
+
30
+ protected
31
+
32
+ # @return [String] The deprecation warning that will be printed the first
33
+ # time an import occurs.
34
+ def deprecation_warning
35
+ path = (@specified_root == ".") ? "the current working directory" : @specified_root
36
+ <<WARNING
37
+ DEPRECATION WARNING: Importing from #{path} will not be
38
+ automatic in future versions of Sass. To avoid future errors, you can add it
39
+ to your environment explicitly by setting `SASSPATH=#{@specified_root}`, by using the -I command
40
+ line option, or by changing your Sass configuration options.
41
+ WARNING
42
+ end
43
+ end
44
+ end
45
+ end
@@ -55,6 +55,17 @@ module Sass
55
55
  root.eql?(other.root)
56
56
  end
57
57
 
58
+ # @see Base#directories_to_watch
59
+ def directories_to_watch
60
+ [root]
61
+ end
62
+
63
+ # @see Base#watched_file?
64
+ def watched_file?(filename)
65
+ filename =~ /\.s[ac]ss$/ &&
66
+ filename.start_with?(root + File::SEPARATOR)
67
+ end
68
+
58
69
  protected
59
70
 
60
71
  # If a full uri is passed, this removes the root from it
@@ -1,4 +1,5 @@
1
1
  require 'fileutils'
2
+ require 'pathname'
2
3
 
3
4
  require 'sass'
4
5
  # XXX CE: is this still necessary now that we have the compiler class?
@@ -148,6 +149,14 @@ module Sass::Plugin
148
149
  # The location of the CSS file that was deleted.
149
150
  define_callback :deleting_css
150
151
 
152
+ # Register a callback to be run when Sass deletes a sourcemap file.
153
+ # This happens when the corresponding Sass/SCSS file has been deleted.
154
+ #
155
+ # @yield [filename]
156
+ # @yieldparam filename [String]
157
+ # The location of the sourcemap file that was deleted.
158
+ define_callback :deleting_sourcemap
159
+
151
160
  # Updates out-of-date stylesheets.
152
161
  #
153
162
  # Checks each Sass/SCSS file in {file:SASS_REFERENCE.md#template_location-option `:template_location`}
@@ -213,53 +222,57 @@ module Sass::Plugin
213
222
  def watch(individual_files = [])
214
223
  update_stylesheets(individual_files)
215
224
 
216
- require 'listen'
217
-
218
- template_paths = template_locations # cache the locations
219
- individual_files_hash = individual_files.inject({}) do |h, files|
220
- parent = File.dirname(files.first)
221
- (h[parent] ||= []) << files unless template_paths.include?(parent)
222
- h
225
+ directories = watched_paths
226
+ individual_files.each do |(source, _, _)|
227
+ directories << File.dirname(File.expand_path(source))
223
228
  end
224
- directories = template_paths + individual_files_hash.keys +
225
- [{:relative_paths => true}]
229
+ directories = remove_redundant_directories(directories)
226
230
 
227
231
  # TODO: Keep better track of what depends on what
228
232
  # so we don't have to run a global update every time anything changes.
229
- listener = Listen::MultiListener.new(*directories) do |modified, added, removed|
230
- modified.each do |f|
231
- parent = File.dirname(f)
232
- if files = individual_files_hash[parent]
233
- next unless files.first == f
234
- else
235
- next unless f =~ /\.s[ac]ss$/
236
- end
237
- run_template_modified(f)
233
+ listener = create_listener(*(directories + [{:relative_paths => false}])) do |modified, added, removed|
234
+ recompile_required = false
235
+
236
+ modified.uniq.each do |f|
237
+ next unless watched_file?(f)
238
+ recompile_required = true
239
+ run_template_modified(relative_to_pwd(f))
238
240
  end
239
241
 
240
- added.each do |f|
241
- parent = File.dirname(f)
242
- if files = individual_files_hash[parent]
243
- next unless files.first == f
244
- else
245
- next unless f =~ /\.s[ac]ss$/
246
- end
247
- run_template_created(f)
242
+ added.uniq.each do |f|
243
+ next unless watched_file?(f)
244
+ recompile_required = true
245
+ run_template_created(relative_to_pwd(f))
248
246
  end
249
247
 
250
- removed.each do |f|
251
- parent = File.dirname(f)
252
- if files = individual_files_hash[parent]
253
- next unless files.first == f
248
+ removed.uniq.each do |f|
249
+ if files = individual_files.find {|(source,_,_)| File.expand_path(source) == f}
250
+ recompile_required = true
251
+ # This was a file we were watching explicitly and compiling to a particular location.
252
+ # Delete the corresponding file.
254
253
  try_delete_css files[1]
255
254
  else
256
- next unless f =~ /\.s[ac]ss$/
257
- try_delete_css f.gsub(/\.s[ac]ss$/, '.css')
255
+ next unless watched_file?(f)
256
+ recompile_required = true
257
+ # Look for the sass directory that contained the sass file
258
+ # And try to remove the css file that corresponds to it
259
+ template_location_array.each do |(sass_dir, css_dir)|
260
+ sass_dir = File.expand_path(sass_dir)
261
+ if child_of_directory?(sass_dir, f)
262
+ remainder = f[(sass_dir.size + 1)..-1]
263
+ try_delete_css(css_filename(remainder, css_dir))
264
+ break
265
+ end
266
+ end
258
267
  end
259
- run_template_deleted(f)
268
+ run_template_deleted(relative_to_pwd(f))
260
269
  end
261
270
 
262
- update_stylesheets(individual_files)
271
+ if recompile_required
272
+ # In case a file we're watching is removed and then recreated we prune out the non-existant files here.
273
+ watched_files_remaining = individual_files.select {|(source, _, _)| File.exists?(source)}
274
+ update_stylesheets(watched_files_remaining)
275
+ end
263
276
  end
264
277
 
265
278
  # The native windows listener is much slower than the polling
@@ -267,7 +280,7 @@ module Sass::Plugin
267
280
  listener.force_polling(true) if @options[:poll] || Sass::Util.windows?
268
281
 
269
282
  begin
270
- listener.start
283
+ listener.start!
271
284
  rescue Exception => e
272
285
  raise e unless e.is_a?(Interrupt)
273
286
  end
@@ -291,6 +304,23 @@ module Sass::Plugin
291
304
 
292
305
  private
293
306
 
307
+ def create_listener(*args, &block)
308
+ require 'listen'
309
+ Listen::Listener.new(*args, &block)
310
+ end
311
+
312
+ def remove_redundant_directories(directories)
313
+ dedupped = []
314
+ directories.each do |new_directory|
315
+ # no need to add a directory that is already watched.
316
+ next if dedupped.any? {|existing_directory| child_of_directory?(existing_directory, new_directory)}
317
+ # get rid of any sub directories of this new directory
318
+ dedupped.reject! {|existing_directory| child_of_directory?(new_directory, existing_directory)}
319
+ dedupped << new_directory
320
+ end
321
+ dedupped
322
+ end
323
+
294
324
  def update_stylesheet(filename, css, sourcemap)
295
325
  dir = File.dirname(css)
296
326
  unless File.exists?(dir)
@@ -329,9 +359,27 @@ module Sass::Plugin
329
359
  end
330
360
 
331
361
  def try_delete_css(css)
332
- return unless File.exists?(css)
333
- run_deleting_css css
334
- File.delete css
362
+ if File.exists?(css)
363
+ run_deleting_css css
364
+ File.delete css
365
+ end
366
+ map = Sass::Util.sourcemap_name(css)
367
+ if File.exists?(map)
368
+ run_deleting_sourcemap map
369
+ File.delete map
370
+ end
371
+ end
372
+
373
+ def watched_file?(file)
374
+ normalized_load_paths.find {|lp| lp.watched_file?(file)}
375
+ end
376
+
377
+ def watched_paths
378
+ @watched_paths ||= normalized_load_paths.map {|lp| lp.directories_to_watch}.compact.flatten
379
+ end
380
+
381
+ def normalized_load_paths
382
+ @normalized_load_paths ||= Sass::Engine.normalize_options(:load_paths=> load_paths)[:load_paths]
335
383
  end
336
384
 
337
385
  def load_paths(opts = options)
@@ -347,7 +395,18 @@ module Sass::Plugin
347
395
  end
348
396
 
349
397
  def css_filename(name, path)
350
- "#{path}/#{name}".gsub(/\.s[ac]ss$/, '.css')
398
+ "#{path}#{File::SEPARATOR unless path.end_with?(File::SEPARATOR)}#{name}".gsub(/\.s[ac]ss$/, '.css')
399
+ end
400
+
401
+ def relative_to_pwd(f)
402
+ Pathname.new(f).relative_path_from(Pathname.new(Dir.pwd)).to_s
403
+ rescue ArgumentError # when a relative path cannot be computed
404
+ f
405
+ end
406
+
407
+ def child_of_directory?(parent, child)
408
+ parent_dir = parent.end_with?(File::SEPARATOR) ? parent : (parent + File::SEPARATOR)
409
+ child.start_with?(parent_dir) || parent == child
351
410
  end
352
411
  end
353
412
  end
@@ -1,3 +1,3 @@
1
- source :gemcutter
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gem 'rake'
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env ruby
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../test_helper'
4
+ require 'sass/plugin'
5
+ require 'sass/plugin/compiler'
6
+
7
+ class CompilerTest < Test::Unit::TestCase
8
+ class FakeListener
9
+ attr_accessor :options
10
+ attr_accessor :directories
11
+ attr_reader :start_called
12
+
13
+ def initialize(*args, &on_filesystem_event)
14
+ self.options = args.last.is_a?(Hash) ? args.pop : {}
15
+ self.directories = args
16
+ @on_filesystem_event = on_filesystem_event
17
+ @start_called = false
18
+ reset_events!
19
+ end
20
+
21
+ def fire_events!(*args)
22
+ @on_filesystem_event.call(@modified, @added, @removed)
23
+ reset_events!
24
+ end
25
+
26
+ def changed(filename)
27
+ @modified << File.expand_path(filename)
28
+ end
29
+
30
+ def added(filename)
31
+ @added << File.expand_path(filename)
32
+ end
33
+
34
+ def removed(filename)
35
+ @removed << File.expand_path(filename)
36
+ end
37
+
38
+ def on_start!(&run_during_start)
39
+ @run_during_start = run_during_start
40
+ end
41
+
42
+ def start!
43
+ @run_during_start.call(self) if @run_during_start
44
+ end
45
+
46
+ def reset_events!
47
+ @modified = []
48
+ @added = []
49
+ @removed = []
50
+ end
51
+ end
52
+
53
+ module MockWatcher
54
+ attr_accessor :run_during_start
55
+ attr_accessor :update_stylesheets_times
56
+ attr_accessor :update_stylesheets_called_with
57
+ attr_accessor :deleted_css_files
58
+
59
+ def fake_listener
60
+ @fake_listener
61
+ end
62
+
63
+ def update_stylesheets(individual_files)
64
+ @update_stylesheets_times ||= 0
65
+ @update_stylesheets_times += 1
66
+ (@update_stylesheets_called_with ||= []) << individual_files
67
+ end
68
+
69
+ def try_delete_css(css_filename)
70
+ (@deleted_css_files ||= []) << css_filename
71
+ end
72
+
73
+ private
74
+ def create_listener(*args, &on_filesystem_event)
75
+ @fake_listener = FakeListener.new(*args, &on_filesystem_event)
76
+ @fake_listener.on_start!(&run_during_start)
77
+ @fake_listener
78
+ end
79
+ end
80
+
81
+ def test_initialize
82
+ watcher
83
+ end
84
+
85
+ def test_watch_starts_the_listener
86
+ start_called = false
87
+ c = watcher do |listener|
88
+ start_called = true
89
+ end
90
+ c.watch
91
+ assert start_called, "start! was not called"
92
+ end
93
+
94
+ def test_sass_callbacks_fire_from_listener_events
95
+ c = watcher do |listener|
96
+ listener.changed "changed.scss"
97
+ listener.added "added.scss"
98
+ listener.removed "removed.scss"
99
+ listener.fire_events!
100
+ end
101
+
102
+ modified_fired = false
103
+ c.on_template_modified do |sass_file|
104
+ modified_fired = true
105
+ assert_equal "changed.scss", sass_file
106
+ end
107
+
108
+ added_fired = false
109
+ c.on_template_created do |sass_file|
110
+ added_fired = true
111
+ assert_equal "added.scss", sass_file
112
+ end
113
+
114
+ removed_fired = false
115
+ c.on_template_deleted do |sass_file|
116
+ removed_fired = true
117
+ assert_equal "removed.scss", sass_file
118
+ end
119
+
120
+ c.watch
121
+
122
+ assert_equal 2, c.update_stylesheets_times
123
+ assert modified_fired
124
+ assert added_fired
125
+ assert removed_fired
126
+ end
127
+
128
+ def test_removing_a_sass_file_removes_corresponding_css_file
129
+ c = watcher do |listener|
130
+ listener.removed "remove_me.scss"
131
+ listener.fire_events!
132
+ end
133
+
134
+ c.watch
135
+
136
+ assert_equal "./remove_me.css", c.deleted_css_files.first
137
+ end
138
+
139
+ def test_an_importer_can_watch_an_image
140
+ image_importer = Sass::Importers::Filesystem.new(".")
141
+ class << image_importer
142
+ def watched_file?(filename)
143
+ filename =~ /\.png$/
144
+ end
145
+ end
146
+ c = watcher(:load_paths => [image_importer]) do |listener|
147
+ listener.changed "image.png"
148
+ listener.fire_events!
149
+ end
150
+
151
+ modified_fired = false
152
+ c.on_template_modified do |f|
153
+ modified_fired = true
154
+ assert_equal "image.png", f
155
+ end
156
+
157
+ c.watch
158
+
159
+ assert_equal 2, c.update_stylesheets_times
160
+ assert modified_fired
161
+ end
162
+
163
+ def test_watching_specific_files_and_one_is_deleted
164
+ directories = nil
165
+ c = watcher do |listener|
166
+ directories = listener.directories
167
+ listener.removed "/asdf/foobar/sass/foo.scss"
168
+ listener.fire_events!
169
+ end
170
+ c.watch([["/asdf/foobar/sass/foo.scss", "/asdf/foobar/css/foo.css", nil]])
171
+ assert directories.include?("/asdf/foobar/sass"), directories.inspect
172
+ assert_equal "/asdf/foobar/css/foo.css", c.deleted_css_files.first, "the corresponding css file was not deleted"
173
+ assert_equal [], c.update_stylesheets_called_with[1], "the sass file should not have been compiled"
174
+ end
175
+
176
+ def test_watched_directories_are_dedupped
177
+ directories = nil
178
+ c = watcher(:load_paths => [".", "./foo", "."]) do |listener|
179
+ directories = listener.directories
180
+ end
181
+ c.watch
182
+ assert_equal [File.expand_path(".")], directories
183
+ end
184
+
185
+ def test_a_changed_css_in_a_watched_directory_does_not_force_a_compile
186
+ c = watcher do |listener|
187
+ listener.changed "foo.css"
188
+ listener.fire_events!
189
+ end
190
+
191
+ c.on_template_modified do |f|
192
+ assert false, "Should not have been called"
193
+ end
194
+
195
+ c.watch
196
+
197
+ assert_equal 1, c.update_stylesheets_times
198
+ end
199
+
200
+ private
201
+
202
+ def default_options
203
+ {:template_location => [[".","."]]}
204
+ end
205
+
206
+ def watcher(options = {}, &run_during_start)
207
+ options = default_options.merge(options)
208
+ watcher = Sass::Plugin::Compiler.new(options)
209
+ watcher.extend(MockWatcher)
210
+ watcher.run_during_start = run_during_start
211
+ watcher
212
+ end
213
+ end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sass
3
3
  version: !ruby/object:Gem::Version
4
- hash: 592302751
4
+ hash: 592302763
5
5
  prerelease: 6
6
6
  segments:
7
7
  - 3
8
8
  - 3
9
9
  - 0
10
10
  - alpha
11
- - 201
12
- version: 3.3.0.alpha.201
11
+ - 211
12
+ version: 3.3.0.alpha.211
13
13
  platform: ruby
14
14
  authors:
15
15
  - Nathan Weizenbaum
@@ -19,7 +19,7 @@ autorequire:
19
19
  bindir: bin
20
20
  cert_chain: []
21
21
 
22
- date: 2013-07-17 00:00:00 -04:00
22
+ date: 2013-07-18 00:00:00 -04:00
23
23
  default_executable:
24
24
  dependencies:
25
25
  - !ruby/object:Gem::Dependency
@@ -30,11 +30,12 @@ dependencies:
30
30
  requirements:
31
31
  - - ~>
32
32
  - !ruby/object:Gem::Version
33
- hash: 5
33
+ hash: 19
34
34
  segments:
35
+ - 1
36
+ - 1
35
37
  - 0
36
- - 7
37
- version: "0.7"
38
+ version: 1.1.0
38
39
  type: :runtime
39
40
  version_requirements: *id001
40
41
  - !ruby/object:Gem::Dependency
@@ -98,6 +99,7 @@ files:
98
99
  - lib/sass/importers.rb
99
100
  - lib/sass/importers/base.rb
100
101
  - lib/sass/importers/filesystem.rb
102
+ - lib/sass/importers/deprecated_path.rb
101
103
  - lib/sass/logger.rb
102
104
  - lib/sass/logger/base.rb
103
105
  - lib/sass/logger/log_level.rb
@@ -314,6 +316,7 @@ files:
314
316
  - test/sass/util/multibyte_string_scanner_test.rb
315
317
  - test/sass/util/subset_map_test.rb
316
318
  - test/sass/source_map_test.rb
319
+ - test/sass/compiler_test.rb
317
320
  - test/test_helper.rb
318
321
  - extra/update_watch.rb
319
322
  - Rakefile
@@ -384,3 +387,4 @@ test_files:
384
387
  - test/sass/util/multibyte_string_scanner_test.rb
385
388
  - test/sass/util/subset_map_test.rb
386
389
  - test/sass/source_map_test.rb
390
+ - test/sass/compiler_test.rb