sass 3.1.0.alpha.2
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/.yardopts +11 -0
- data/CONTRIBUTING +3 -0
- data/EDGE_GEM_VERSION +1 -0
- data/MIT-LICENSE +20 -0
- data/README.md +201 -0
- data/REVISION +1 -0
- data/Rakefile +353 -0
- data/VERSION +1 -0
- data/VERSION_NAME +1 -0
- data/bin/css2sass +13 -0
- data/bin/sass +8 -0
- data/bin/sass-convert +7 -0
- data/extra/update_watch.rb +13 -0
- data/init.rb +18 -0
- data/lib/sass.rb +71 -0
- data/lib/sass/cache_store.rb +208 -0
- data/lib/sass/callbacks.rb +66 -0
- data/lib/sass/css.rb +294 -0
- data/lib/sass/engine.rb +792 -0
- data/lib/sass/environment.rb +143 -0
- data/lib/sass/error.rb +201 -0
- data/lib/sass/exec.rb +619 -0
- data/lib/sass/importers.rb +22 -0
- data/lib/sass/importers/base.rb +138 -0
- data/lib/sass/importers/filesystem.rb +121 -0
- data/lib/sass/less.rb +363 -0
- data/lib/sass/plugin.rb +126 -0
- data/lib/sass/plugin/compiler.rb +346 -0
- data/lib/sass/plugin/configuration.rb +123 -0
- data/lib/sass/plugin/generic.rb +15 -0
- data/lib/sass/plugin/merb.rb +48 -0
- data/lib/sass/plugin/rack.rb +47 -0
- data/lib/sass/plugin/rails.rb +41 -0
- data/lib/sass/plugin/staleness_checker.rb +145 -0
- data/lib/sass/railtie.rb +8 -0
- data/lib/sass/repl.rb +58 -0
- data/lib/sass/root.rb +7 -0
- data/lib/sass/script.rb +63 -0
- data/lib/sass/script/bool.rb +18 -0
- data/lib/sass/script/color.rb +490 -0
- data/lib/sass/script/css_lexer.rb +29 -0
- data/lib/sass/script/css_parser.rb +31 -0
- data/lib/sass/script/funcall.rb +78 -0
- data/lib/sass/script/functions.rb +852 -0
- data/lib/sass/script/interpolation.rb +70 -0
- data/lib/sass/script/lexer.rb +337 -0
- data/lib/sass/script/literal.rb +236 -0
- data/lib/sass/script/node.rb +101 -0
- data/lib/sass/script/number.rb +420 -0
- data/lib/sass/script/operation.rb +92 -0
- data/lib/sass/script/parser.rb +392 -0
- data/lib/sass/script/string.rb +67 -0
- data/lib/sass/script/string_interpolation.rb +93 -0
- data/lib/sass/script/unary_operation.rb +57 -0
- data/lib/sass/script/variable.rb +48 -0
- data/lib/sass/scss.rb +17 -0
- data/lib/sass/scss/css_parser.rb +51 -0
- data/lib/sass/scss/parser.rb +838 -0
- data/lib/sass/scss/rx.rb +126 -0
- data/lib/sass/scss/sass_parser.rb +11 -0
- data/lib/sass/scss/script_lexer.rb +15 -0
- data/lib/sass/scss/script_parser.rb +25 -0
- data/lib/sass/scss/static_parser.rb +40 -0
- data/lib/sass/selector.rb +361 -0
- data/lib/sass/selector/abstract_sequence.rb +62 -0
- data/lib/sass/selector/comma_sequence.rb +82 -0
- data/lib/sass/selector/sequence.rb +236 -0
- data/lib/sass/selector/simple.rb +113 -0
- data/lib/sass/selector/simple_sequence.rb +135 -0
- data/lib/sass/shared.rb +78 -0
- data/lib/sass/tree/comment_node.rb +128 -0
- data/lib/sass/tree/debug_node.rb +36 -0
- data/lib/sass/tree/directive_node.rb +75 -0
- data/lib/sass/tree/extend_node.rb +65 -0
- data/lib/sass/tree/for_node.rb +67 -0
- data/lib/sass/tree/if_node.rb +81 -0
- data/lib/sass/tree/import_node.rb +124 -0
- data/lib/sass/tree/mixin_def_node.rb +60 -0
- data/lib/sass/tree/mixin_node.rb +123 -0
- data/lib/sass/tree/node.rb +490 -0
- data/lib/sass/tree/prop_node.rb +220 -0
- data/lib/sass/tree/root_node.rb +125 -0
- data/lib/sass/tree/rule_node.rb +273 -0
- data/lib/sass/tree/variable_node.rb +39 -0
- data/lib/sass/tree/warn_node.rb +42 -0
- data/lib/sass/tree/while_node.rb +48 -0
- data/lib/sass/util.rb +687 -0
- data/lib/sass/util/subset_map.rb +101 -0
- data/lib/sass/version.rb +109 -0
- data/rails/init.rb +1 -0
- data/test/sass/cache_test.rb +74 -0
- data/test/sass/callbacks_test.rb +61 -0
- data/test/sass/conversion_test.rb +1210 -0
- data/test/sass/css2sass_test.rb +364 -0
- data/test/sass/data/hsl-rgb.txt +319 -0
- data/test/sass/engine_test.rb +2273 -0
- data/test/sass/extend_test.rb +1348 -0
- data/test/sass/functions_test.rb +565 -0
- data/test/sass/importer_test.rb +104 -0
- data/test/sass/less_conversion_test.rb +632 -0
- data/test/sass/mock_importer.rb +49 -0
- data/test/sass/more_results/more1.css +9 -0
- data/test/sass/more_results/more1_with_line_comments.css +26 -0
- data/test/sass/more_results/more_import.css +29 -0
- data/test/sass/more_templates/_more_partial.sass +2 -0
- data/test/sass/more_templates/more1.sass +23 -0
- data/test/sass/more_templates/more_import.sass +11 -0
- data/test/sass/plugin_test.rb +430 -0
- data/test/sass/results/alt.css +4 -0
- data/test/sass/results/basic.css +9 -0
- data/test/sass/results/compact.css +5 -0
- data/test/sass/results/complex.css +86 -0
- data/test/sass/results/compressed.css +1 -0
- data/test/sass/results/expanded.css +19 -0
- data/test/sass/results/import.css +31 -0
- data/test/sass/results/line_numbers.css +49 -0
- data/test/sass/results/mixins.css +95 -0
- data/test/sass/results/multiline.css +24 -0
- data/test/sass/results/nested.css +22 -0
- data/test/sass/results/options.css +1 -0
- data/test/sass/results/parent_ref.css +13 -0
- data/test/sass/results/script.css +16 -0
- data/test/sass/results/scss_import.css +31 -0
- data/test/sass/results/scss_importee.css +2 -0
- data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
- data/test/sass/results/subdir/subdir.css +3 -0
- data/test/sass/results/units.css +11 -0
- data/test/sass/results/warn.css +0 -0
- data/test/sass/results/warn_imported.css +0 -0
- data/test/sass/script_conversion_test.rb +254 -0
- data/test/sass/script_test.rb +459 -0
- data/test/sass/scss/css_test.rb +897 -0
- data/test/sass/scss/rx_test.rb +156 -0
- data/test/sass/scss/scss_test.rb +1088 -0
- data/test/sass/scss/test_helper.rb +37 -0
- data/test/sass/templates/_partial.sass +2 -0
- data/test/sass/templates/alt.sass +16 -0
- data/test/sass/templates/basic.sass +23 -0
- data/test/sass/templates/bork1.sass +2 -0
- data/test/sass/templates/bork2.sass +2 -0
- data/test/sass/templates/bork3.sass +2 -0
- data/test/sass/templates/bork4.sass +2 -0
- data/test/sass/templates/compact.sass +17 -0
- data/test/sass/templates/complex.sass +305 -0
- data/test/sass/templates/compressed.sass +15 -0
- data/test/sass/templates/expanded.sass +17 -0
- data/test/sass/templates/import.sass +12 -0
- data/test/sass/templates/importee.less +2 -0
- data/test/sass/templates/importee.sass +19 -0
- data/test/sass/templates/line_numbers.sass +13 -0
- data/test/sass/templates/mixin_bork.sass +5 -0
- data/test/sass/templates/mixins.sass +76 -0
- data/test/sass/templates/multiline.sass +20 -0
- data/test/sass/templates/nested.sass +25 -0
- data/test/sass/templates/nested_bork1.sass +2 -0
- data/test/sass/templates/nested_bork2.sass +2 -0
- data/test/sass/templates/nested_bork3.sass +2 -0
- data/test/sass/templates/nested_bork4.sass +2 -0
- data/test/sass/templates/nested_mixin_bork.sass +6 -0
- data/test/sass/templates/options.sass +2 -0
- data/test/sass/templates/parent_ref.sass +25 -0
- data/test/sass/templates/script.sass +101 -0
- data/test/sass/templates/scss_import.scss +11 -0
- data/test/sass/templates/scss_importee.scss +1 -0
- data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
- data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
- data/test/sass/templates/subdir/subdir.sass +6 -0
- data/test/sass/templates/units.sass +11 -0
- data/test/sass/templates/warn.sass +3 -0
- data/test/sass/templates/warn_imported.sass +4 -0
- data/test/sass/test_helper.rb +8 -0
- data/test/sass/util/subset_map_test.rb +91 -0
- data/test/sass/util_test.rb +275 -0
- data/test/test_helper.rb +64 -0
- data/vendor/fssm/LICENSE +20 -0
- data/vendor/fssm/README.markdown +55 -0
- data/vendor/fssm/Rakefile +59 -0
- data/vendor/fssm/VERSION.yml +5 -0
- data/vendor/fssm/example.rb +9 -0
- data/vendor/fssm/fssm.gemspec +77 -0
- data/vendor/fssm/lib/fssm.rb +33 -0
- data/vendor/fssm/lib/fssm/backends/fsevents.rb +36 -0
- data/vendor/fssm/lib/fssm/backends/inotify.rb +26 -0
- data/vendor/fssm/lib/fssm/backends/polling.rb +25 -0
- data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +131 -0
- data/vendor/fssm/lib/fssm/monitor.rb +26 -0
- data/vendor/fssm/lib/fssm/path.rb +91 -0
- data/vendor/fssm/lib/fssm/pathname.rb +502 -0
- data/vendor/fssm/lib/fssm/state/directory.rb +57 -0
- data/vendor/fssm/lib/fssm/state/file.rb +24 -0
- data/vendor/fssm/lib/fssm/support.rb +63 -0
- data/vendor/fssm/lib/fssm/tree.rb +176 -0
- data/vendor/fssm/profile/prof-cache.rb +40 -0
- data/vendor/fssm/profile/prof-fssm-pathname.html +1231 -0
- data/vendor/fssm/profile/prof-pathname.rb +68 -0
- data/vendor/fssm/profile/prof-plain-pathname.html +988 -0
- data/vendor/fssm/profile/prof.html +2379 -0
- data/vendor/fssm/spec/path_spec.rb +75 -0
- data/vendor/fssm/spec/root/duck/quack.txt +0 -0
- data/vendor/fssm/spec/root/file.css +0 -0
- data/vendor/fssm/spec/root/file.rb +0 -0
- data/vendor/fssm/spec/root/file.yml +0 -0
- data/vendor/fssm/spec/root/moo/cow.txt +0 -0
- data/vendor/fssm/spec/spec_helper.rb +14 -0
- metadata +297 -0
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.1.0.alpha.2
|
data/VERSION_NAME
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Bleeding Edge
|
data/bin/css2sass
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../lib/sass'
|
4
|
+
require 'sass/exec'
|
5
|
+
|
6
|
+
warn <<END
|
7
|
+
DEPRECATION WARNING:
|
8
|
+
The css2sass tool is deprecated and will be removed in Sass 3.2.
|
9
|
+
Use the sass-convert tool instead.
|
10
|
+
END
|
11
|
+
|
12
|
+
opts = Sass::Exec::SassConvert.new(%w[--from css --to sass] + ARGV)
|
13
|
+
opts.parse!
|
data/bin/sass
ADDED
data/bin/sass-convert
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sinatra'
|
3
|
+
require 'json'
|
4
|
+
set :port, 3124
|
5
|
+
set :environment, :production
|
6
|
+
enable :lock
|
7
|
+
Dir.chdir(File.dirname(__FILE__) + "/..")
|
8
|
+
|
9
|
+
post "/" do
|
10
|
+
puts "Recieved payload!"
|
11
|
+
puts "Rev: #{`git name-rev HEAD`.strip}"
|
12
|
+
system %{rake handle_update --trace REF=#{JSON.parse(params["payload"])["ref"].inspect}}
|
13
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
begin
|
2
|
+
require File.join(File.dirname(__FILE__), 'lib', 'sass') # From here
|
3
|
+
rescue LoadError
|
4
|
+
begin
|
5
|
+
require 'sass' # From gem
|
6
|
+
rescue LoadError => e
|
7
|
+
# gems:install may be run to install Haml with the skeleton plugin
|
8
|
+
# but not the gem itself installed.
|
9
|
+
# Don't die if this is the case.
|
10
|
+
raise e unless defined?(Rake) &&
|
11
|
+
(Rake.application.top_level_tasks.include?('gems') ||
|
12
|
+
Rake.application.top_level_tasks.include?('gems:install'))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Load Sass.
|
17
|
+
# Sass may be undefined if we're running gems:install.
|
18
|
+
require 'sass/plugin' if defined?(Sass)
|
data/lib/sass.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
$LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
|
3
|
+
|
4
|
+
# This is necessary to set so that the Haml code that tries to load Sass
|
5
|
+
# knows that Sass is indeed loading,
|
6
|
+
# even if there's some crazy autoload stuff going on.
|
7
|
+
SASS_BEGUN_TO_LOAD = true unless defined?(SASS_BEGUN_TO_LOAD)
|
8
|
+
|
9
|
+
require 'sass/version'
|
10
|
+
|
11
|
+
# The module that contains everything Sass-related:
|
12
|
+
#
|
13
|
+
# * {Sass::Engine} is the class used to render Sass/SCSS within Ruby code.
|
14
|
+
# * {Sass::Plugin} is interfaces with web frameworks (Rails and Merb in particular).
|
15
|
+
# * {Sass::SyntaxError} is raised when Sass encounters an error.
|
16
|
+
# * {Sass::CSS} handles conversion of CSS to Sass.
|
17
|
+
#
|
18
|
+
# Also see the {file:SASS_REFERENCE.md full Sass reference}.
|
19
|
+
module Sass
|
20
|
+
# Compile a Sass or SCSS string to CSS.
|
21
|
+
# Defaults to SCSS.
|
22
|
+
#
|
23
|
+
# @param contents [String] The contents of the Sass file.
|
24
|
+
# @param options [{Symbol => Object}] An options hash;
|
25
|
+
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
|
26
|
+
# @raise [Sass::SyntaxError] if there's an error in the document
|
27
|
+
# @raise [Encoding::UndefinedConversionError] if the source encoding
|
28
|
+
# cannot be converted to UTF-8
|
29
|
+
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
|
30
|
+
def self.compile(contents, options = {})
|
31
|
+
options[:syntax] ||= :scss
|
32
|
+
Engine.new(contents, options).to_css
|
33
|
+
end
|
34
|
+
|
35
|
+
# Compile a file on disk to CSS.
|
36
|
+
#
|
37
|
+
# @param filename [String] The path to the Sass, SCSS, or CSS file on disk.
|
38
|
+
# @param options [{Symbol => Object}] An options hash;
|
39
|
+
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
|
40
|
+
# @raise [Sass::SyntaxError] if there's an error in the document
|
41
|
+
# @raise [Encoding::UndefinedConversionError] if the source encoding
|
42
|
+
# cannot be converted to UTF-8
|
43
|
+
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
|
44
|
+
#
|
45
|
+
# @overload compile_file(filename, options = {})
|
46
|
+
# @return [String] The compiled CSS.
|
47
|
+
#
|
48
|
+
# @overload compile_file(filename, css_filename, options = {})
|
49
|
+
# @param css_filename [String] The location to which to write the compiled CSS.
|
50
|
+
def self.compile_file(filename, *args)
|
51
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
52
|
+
css_filename ||= args.shift
|
53
|
+
options[:css_filename] = css_filename
|
54
|
+
result = Sass::Engine.for_file(filename, options).render
|
55
|
+
if css_filename
|
56
|
+
open(css_filename,"w") {|css_file| css_file.write(result) }
|
57
|
+
nil
|
58
|
+
else
|
59
|
+
result
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
require 'sass/util'
|
65
|
+
|
66
|
+
dir = Sass::Util.scope("vendor/fssm/lib")
|
67
|
+
$LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
|
68
|
+
|
69
|
+
require 'sass/engine'
|
70
|
+
require 'sass/plugin' if defined?(Merb::Plugins)
|
71
|
+
require 'sass/railtie'
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Sass
|
4
|
+
# An abstract base class for backends for the Sass cache.
|
5
|
+
# Any key-value store can act as such a backend;
|
6
|
+
# it just needs to implement the
|
7
|
+
# \{#_store} and \{#_retrieve} methods.
|
8
|
+
#
|
9
|
+
# To use a cache store with Sass,
|
10
|
+
# use the {file:SASS_REFERENCE.md#cache_store-option `:cache_store` option}.
|
11
|
+
class CacheStore
|
12
|
+
# Store cached contents for later retrieval
|
13
|
+
# Must be implemented by all CacheStore subclasses
|
14
|
+
#
|
15
|
+
# Note: cache contents contain binary data.
|
16
|
+
#
|
17
|
+
# @param key [String] The key to store the contents under
|
18
|
+
# @param version [String] The current sass version.
|
19
|
+
# Cached contents must not be retrieved across different versions of sass.
|
20
|
+
# @param sha [String] The sha of the sass source.
|
21
|
+
# Cached contents must not be retrieved if the sha has changed.
|
22
|
+
# @param contents [String] The contents to store.
|
23
|
+
def _store(key, version, sha, contents)
|
24
|
+
raise "#{self.class} must implement #_store."
|
25
|
+
end
|
26
|
+
|
27
|
+
# Retrieved cached contents.
|
28
|
+
# Must be implemented by all subclasses.
|
29
|
+
#
|
30
|
+
# Note: if the key exists but the sha or version have changed,
|
31
|
+
# then the key may be deleted by the cache store, if it wants to do so.
|
32
|
+
#
|
33
|
+
# @param key [String] The key to retrieve
|
34
|
+
# @param version [String] The current sass version.
|
35
|
+
# Cached contents must not be retrieved across different versions of sass.
|
36
|
+
# @param sha [String] The sha of the sass source.
|
37
|
+
# Cached contents must not be retrieved if the sha has changed.
|
38
|
+
# @return [String] The contents that were previously stored.
|
39
|
+
# @return [NilClass] when the cache key is not found or the version or sha have changed.
|
40
|
+
def _retrieve(key, version, sha)
|
41
|
+
raise "#{self.class} must implement #_retrieve."
|
42
|
+
end
|
43
|
+
|
44
|
+
# Store a {Sass::Tree::RootNode}.
|
45
|
+
#
|
46
|
+
# @param key [String] The key to store it under.
|
47
|
+
# @param sha [String] The checksum for the contents that are being stored.
|
48
|
+
# @param obj [Object] The object to cache.
|
49
|
+
def store(key, sha, root)
|
50
|
+
_store(key, Sass::VERSION, sha, Sass::Util.dump(root))
|
51
|
+
end
|
52
|
+
|
53
|
+
# Retrieve a {Sass::Tree::RootNode}.
|
54
|
+
#
|
55
|
+
# @param key [String] The key the root element was stored under.
|
56
|
+
# @param sha [String] The checksum of the root element's content.
|
57
|
+
# @return [Object] The cached object.
|
58
|
+
def retrieve(key, sha)
|
59
|
+
contents = _retrieve(key, Sass::VERSION, sha)
|
60
|
+
Sass::Util.load(contents) if contents
|
61
|
+
rescue EOFError, TypeError, ArgumentError => e
|
62
|
+
raise
|
63
|
+
Sass::Util.sass_warn "Warning. Error encountered while reading cache #{path_to(key)}: #{e}"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Return the key for the sass file.
|
67
|
+
#
|
68
|
+
# The `(sass_dirname, sass_basename)` pair
|
69
|
+
# should uniquely identify the Sass document,
|
70
|
+
# but otherwise there are no restrictions on their content.
|
71
|
+
#
|
72
|
+
# @param sass_dirname [String]
|
73
|
+
# The fully-expanded location of the Sass file.
|
74
|
+
# This corresponds to the directory name on a filesystem.
|
75
|
+
# @param sass_basename [String] The name of the Sass file that is being referenced.
|
76
|
+
# This corresponds to the basename on a filesystem.
|
77
|
+
def key(sass_dirname, sass_basename)
|
78
|
+
dir = Digest::SHA1.hexdigest(sass_dirname)
|
79
|
+
filename = "#{sass_basename}c"
|
80
|
+
"#{dir}/#{filename}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# A backend for the Sass cache using the filesystem.
|
85
|
+
class FileCacheStore < CacheStore
|
86
|
+
# The directory where the cached files will be stored.
|
87
|
+
#
|
88
|
+
# @return [String]
|
89
|
+
attr_accessor :cache_location
|
90
|
+
|
91
|
+
# Create a new FileCacheStore.
|
92
|
+
#
|
93
|
+
# @param cache_location [String] see \{#cache\_location}
|
94
|
+
def initialize(cache_location)
|
95
|
+
@cache_location = cache_location
|
96
|
+
end
|
97
|
+
|
98
|
+
# @see {CacheStore#\_retrieve\_}
|
99
|
+
def _retrieve(key, version, sha)
|
100
|
+
return unless File.readable?(path_to(key))
|
101
|
+
contents = nil
|
102
|
+
File.open(path_to(key), "rb") do |f|
|
103
|
+
if f.readline("\n").strip == version && f.readline("\n").strip == sha
|
104
|
+
return f.read
|
105
|
+
end
|
106
|
+
end
|
107
|
+
File.unlink path_to(key)
|
108
|
+
nil
|
109
|
+
rescue EOFError, TypeError, ArgumentError => e
|
110
|
+
Sass::Util.sass_warn "Warning. Error encountered while reading cache #{path_to(key)}: #{e}"
|
111
|
+
end
|
112
|
+
|
113
|
+
# @see {CacheStore#\_store\_}
|
114
|
+
def _store(key, version, sha, contents)
|
115
|
+
return unless File.writable?(File.dirname(@cache_location))
|
116
|
+
return if File.exists?(@cache_location) && !File.writable?(@cache_location)
|
117
|
+
compiled_filename = path_to(key)
|
118
|
+
return if File.exists?(File.dirname(compiled_filename)) && !File.writable?(File.dirname(compiled_filename))
|
119
|
+
return if File.exists?(compiled_filename) && !File.writable?(compiled_filename)
|
120
|
+
FileUtils.mkdir_p(File.dirname(compiled_filename))
|
121
|
+
File.open(compiled_filename, "wb") do |f|
|
122
|
+
f.puts(version)
|
123
|
+
f.puts(sha)
|
124
|
+
f.write(contents)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
# Returns the path to a file for the given key.
|
131
|
+
#
|
132
|
+
# @param key [String]
|
133
|
+
# @return [String] The path to the cache file.
|
134
|
+
def path_to(key)
|
135
|
+
File.join(cache_location, key)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# A backend for the Sass cache using in-process memory.
|
140
|
+
class InMemoryCacheStore < CacheStore
|
141
|
+
# Since the {InMemoryCacheStore} is stored in the Sass tree's options hash,
|
142
|
+
# when the options get serialized as part of serializing the tree,
|
143
|
+
# you get crazy exponential growth in the size of the cached objects
|
144
|
+
# unless you don't dump the cache.
|
145
|
+
#
|
146
|
+
# @private
|
147
|
+
def _dump(depth)
|
148
|
+
""
|
149
|
+
end
|
150
|
+
|
151
|
+
# If we deserialize this class, just make a new empty one.
|
152
|
+
#
|
153
|
+
# @private
|
154
|
+
def self._load(repr)
|
155
|
+
InMemoryCacheStore.new
|
156
|
+
end
|
157
|
+
|
158
|
+
# Create a new, empty cache store.
|
159
|
+
def initialize
|
160
|
+
@contents = {}
|
161
|
+
end
|
162
|
+
|
163
|
+
# @see CacheStore#_retrieve
|
164
|
+
def _retrieve(key, version, sha)
|
165
|
+
if @contents.has_key?(key)
|
166
|
+
return unless @contents[key][:version] == version
|
167
|
+
return unless @contents[key][:sha] == sha
|
168
|
+
return @contents[key][:contents]
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# @see CacheStore#_store
|
173
|
+
def _store(key, version, sha, contents)
|
174
|
+
@contents[key] = {
|
175
|
+
:version => version,
|
176
|
+
:sha => sha,
|
177
|
+
:contents => contents
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
181
|
+
# Destructively clear the cache.
|
182
|
+
def reset!
|
183
|
+
@contents = {}
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Doesn't store anything, but records what things it should have stored.
|
188
|
+
# This doesn't currently have any use except for testing and debugging.
|
189
|
+
#
|
190
|
+
# @private
|
191
|
+
class NullCacheStore < CacheStore
|
192
|
+
def initialize
|
193
|
+
@keys = {}
|
194
|
+
end
|
195
|
+
|
196
|
+
def _retrieve(key, version, sha)
|
197
|
+
nil
|
198
|
+
end
|
199
|
+
|
200
|
+
def _store(key, version, sha, contents)
|
201
|
+
@keys[key] = true
|
202
|
+
end
|
203
|
+
|
204
|
+
def was_set?(key)
|
205
|
+
@keys[key]
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Sass
|
2
|
+
# A lightweight infrastructure for defining and running callbacks.
|
3
|
+
# Callbacks are defined using \{#define\_callback\} at the class level,
|
4
|
+
# and called using `run_#{name}` at the instance level.
|
5
|
+
#
|
6
|
+
# Clients can add callbacks by calling the generated `on_#{name}` method,
|
7
|
+
# and passing in a block that's run when the callback is activated.
|
8
|
+
#
|
9
|
+
# @example Define a callback
|
10
|
+
# class Munger
|
11
|
+
# extend Sass::Callbacks
|
12
|
+
# define_callback :string_munged
|
13
|
+
#
|
14
|
+
# def munge(str)
|
15
|
+
# res = str.gsub(/[a-z]/, '\1\1')
|
16
|
+
# run_string_munged str, res
|
17
|
+
# res
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @example Use a callback
|
22
|
+
# m = Munger.new
|
23
|
+
# m.on_string_munged {|str, res| puts "#{str} was munged into #{res}!"}
|
24
|
+
# m.munge "bar" #=> bar was munged into bbaarr!
|
25
|
+
module Callbacks
|
26
|
+
# Automatically includes {InstanceMethods}
|
27
|
+
# when something extends this module.
|
28
|
+
#
|
29
|
+
# @param base [Module]
|
30
|
+
def self.extended(base)
|
31
|
+
base.send(:include, InstanceMethods)
|
32
|
+
end
|
33
|
+
protected
|
34
|
+
|
35
|
+
module InstanceMethods
|
36
|
+
# Removes all callbacks registered against this object.
|
37
|
+
def clear_callbacks!
|
38
|
+
@_sass_callbacks = {}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Define a callback with the given name.
|
43
|
+
# This will define an `on_#{name}` method
|
44
|
+
# that registers a block,
|
45
|
+
# and a `run_#{name}` method that runs that block
|
46
|
+
# (optionall with some arguments).
|
47
|
+
#
|
48
|
+
# @param name [Symbol] The name of the callback
|
49
|
+
# @return [void]
|
50
|
+
def define_callback(name)
|
51
|
+
class_eval <<RUBY
|
52
|
+
def on_#{name}(&block)
|
53
|
+
@_sass_callbacks ||= {}
|
54
|
+
(@_sass_callbacks[#{name.inspect}] ||= []) << block
|
55
|
+
end
|
56
|
+
|
57
|
+
def run_#{name}(*args)
|
58
|
+
return unless @_sass_callbacks
|
59
|
+
return unless @_sass_callbacks[#{name.inspect}]
|
60
|
+
@_sass_callbacks[#{name.inspect}].each {|c| c[*args]}
|
61
|
+
end
|
62
|
+
private :run_#{name}
|
63
|
+
RUBY
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/sass/css.rb
ADDED
@@ -0,0 +1,294 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../sass'
|
2
|
+
require 'sass/tree/node'
|
3
|
+
require 'sass/scss/css_parser'
|
4
|
+
require 'strscan'
|
5
|
+
|
6
|
+
module Sass
|
7
|
+
# This class converts CSS documents into Sass or SCSS templates.
|
8
|
+
# It works by parsing the CSS document into a {Sass::Tree} structure,
|
9
|
+
# and then applying various transformations to the structure
|
10
|
+
# to produce more concise and idiomatic Sass/SCSS.
|
11
|
+
#
|
12
|
+
# Example usage:
|
13
|
+
#
|
14
|
+
# Sass::CSS.new("p { color: blue }").render(:sass) #=> "p\n color: blue"
|
15
|
+
# Sass::CSS.new("p { color: blue }").render(:scss) #=> "p {\n color: blue; }"
|
16
|
+
class CSS
|
17
|
+
# @param template [String] The CSS stylesheet.
|
18
|
+
# This stylesheet can be encoded using any encoding
|
19
|
+
# that can be converted to Unicode.
|
20
|
+
# If the stylesheet contains an `@charset` declaration,
|
21
|
+
# that overrides the Ruby encoding
|
22
|
+
# (see {file:SASS_REFERENCE.md#encodings the encoding documentation})
|
23
|
+
# @option options :old [Boolean] (false)
|
24
|
+
# Whether or not to output old property syntax
|
25
|
+
# (`:color blue` as opposed to `color: blue`).
|
26
|
+
# This is only meaningful when generating Sass code,
|
27
|
+
# rather than SCSS.
|
28
|
+
def initialize(template, options = {})
|
29
|
+
if template.is_a? IO
|
30
|
+
template = template.read
|
31
|
+
end
|
32
|
+
|
33
|
+
@options = options.dup
|
34
|
+
# Backwards compatibility
|
35
|
+
@options[:old] = true if @options[:alternate] == false
|
36
|
+
@template = template
|
37
|
+
end
|
38
|
+
|
39
|
+
# Converts the CSS template into Sass or SCSS code.
|
40
|
+
#
|
41
|
+
# @param fmt [Symbol] `:sass` or `:scss`, designating the format to return.
|
42
|
+
# @return [String] The resulting Sass or SCSS code
|
43
|
+
# @raise [Sass::SyntaxError] if there's an error parsing the CSS template
|
44
|
+
def render(fmt = :sass)
|
45
|
+
check_encoding!
|
46
|
+
build_tree.send("to_#{fmt}", @options).strip + "\n"
|
47
|
+
rescue Sass::SyntaxError => err
|
48
|
+
err.modify_backtrace(:filename => @options[:filename] || '(css)')
|
49
|
+
raise err
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the original encoding of the document,
|
53
|
+
# or `nil` under Ruby 1.8.
|
54
|
+
#
|
55
|
+
# @return [Encoding, nil]
|
56
|
+
# @raise [Encoding::UndefinedConversionError] if the source encoding
|
57
|
+
# cannot be converted to UTF-8
|
58
|
+
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
|
59
|
+
def source_encoding
|
60
|
+
check_encoding!
|
61
|
+
@original_encoding
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def check_encoding!
|
67
|
+
return if @checked_encoding
|
68
|
+
@checked_encoding = true
|
69
|
+
@template, @original_encoding = Sass::Util.check_sass_encoding(@template) do |msg, line|
|
70
|
+
raise Sass::SyntaxError.new(msg, :line => line)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Parses the CSS template and applies various transformations
|
75
|
+
#
|
76
|
+
# @return [Tree::Node] The root node of the parsed tree
|
77
|
+
def build_tree
|
78
|
+
root = Sass::SCSS::CssParser.new(@template).parse
|
79
|
+
expand_commas root
|
80
|
+
parent_ref_rules root
|
81
|
+
remove_parent_refs root
|
82
|
+
flatten_rules root
|
83
|
+
fold_commas root
|
84
|
+
root
|
85
|
+
end
|
86
|
+
|
87
|
+
# Transform
|
88
|
+
#
|
89
|
+
# foo, bar, baz
|
90
|
+
# color: blue
|
91
|
+
#
|
92
|
+
# into
|
93
|
+
#
|
94
|
+
# foo
|
95
|
+
# color: blue
|
96
|
+
# bar
|
97
|
+
# color: blue
|
98
|
+
# baz
|
99
|
+
# color: blue
|
100
|
+
#
|
101
|
+
# @param root [Tree::Node] The parent node
|
102
|
+
def expand_commas(root)
|
103
|
+
root.children.map! do |child|
|
104
|
+
unless child.is_a?(Tree::RuleNode) && child.rule.first.include?(',')
|
105
|
+
expand_commas(child) if child.is_a?(Tree::DirectiveNode)
|
106
|
+
next child
|
107
|
+
end
|
108
|
+
child.rule.first.split(',').map do |rule|
|
109
|
+
node = Tree::RuleNode.new([rule.strip])
|
110
|
+
node.children = child.children
|
111
|
+
node
|
112
|
+
end
|
113
|
+
end
|
114
|
+
root.children.flatten!
|
115
|
+
end
|
116
|
+
|
117
|
+
# Make rules use parent refs so that
|
118
|
+
#
|
119
|
+
# foo
|
120
|
+
# color: green
|
121
|
+
# foo.bar
|
122
|
+
# color: blue
|
123
|
+
#
|
124
|
+
# becomes
|
125
|
+
#
|
126
|
+
# foo
|
127
|
+
# color: green
|
128
|
+
# &.bar
|
129
|
+
# color: blue
|
130
|
+
#
|
131
|
+
# This has the side effect of nesting rules,
|
132
|
+
# so that
|
133
|
+
#
|
134
|
+
# foo
|
135
|
+
# color: green
|
136
|
+
# foo bar
|
137
|
+
# color: red
|
138
|
+
# foo baz
|
139
|
+
# color: blue
|
140
|
+
#
|
141
|
+
# becomes
|
142
|
+
#
|
143
|
+
# foo
|
144
|
+
# color: green
|
145
|
+
# & bar
|
146
|
+
# color: red
|
147
|
+
# & baz
|
148
|
+
# color: blue
|
149
|
+
#
|
150
|
+
# @param root [Tree::Node] The parent node
|
151
|
+
def parent_ref_rules(root)
|
152
|
+
current_rule = nil
|
153
|
+
root.children.map! do |child|
|
154
|
+
unless child.is_a?(Tree::RuleNode)
|
155
|
+
parent_ref_rules(child) if child.is_a?(Tree::DirectiveNode)
|
156
|
+
next child
|
157
|
+
end
|
158
|
+
|
159
|
+
first, rest = child.rule.first.scan(/\A(&?(?: .|[^ ])[^.#: \[]*)([.#: \[].*)?\Z/m).first
|
160
|
+
|
161
|
+
if current_rule.nil? || current_rule.rule.first != first
|
162
|
+
current_rule = Tree::RuleNode.new([first])
|
163
|
+
end
|
164
|
+
|
165
|
+
if rest
|
166
|
+
child.rule = ["&" + rest]
|
167
|
+
current_rule << child
|
168
|
+
else
|
169
|
+
current_rule.children += child.children
|
170
|
+
end
|
171
|
+
|
172
|
+
current_rule
|
173
|
+
end
|
174
|
+
root.children.compact!
|
175
|
+
root.children.uniq!
|
176
|
+
|
177
|
+
root.children.each { |v| parent_ref_rules(v) }
|
178
|
+
end
|
179
|
+
|
180
|
+
# Remove useless parent refs so that
|
181
|
+
#
|
182
|
+
# foo
|
183
|
+
# & bar
|
184
|
+
# color: blue
|
185
|
+
#
|
186
|
+
# becomes
|
187
|
+
#
|
188
|
+
# foo
|
189
|
+
# bar
|
190
|
+
# color: blue
|
191
|
+
#
|
192
|
+
# @param root [Tree::Node] The parent node
|
193
|
+
def remove_parent_refs(root)
|
194
|
+
root.children.each do |child|
|
195
|
+
case child
|
196
|
+
when Tree::RuleNode
|
197
|
+
child.rule.first.gsub! /^& +/, ''
|
198
|
+
remove_parent_refs child
|
199
|
+
when Tree::DirectiveNode
|
200
|
+
remove_parent_refs child
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Flatten rules so that
|
206
|
+
#
|
207
|
+
# foo
|
208
|
+
# bar
|
209
|
+
# color: red
|
210
|
+
#
|
211
|
+
# becomes
|
212
|
+
#
|
213
|
+
# foo bar
|
214
|
+
# color: red
|
215
|
+
#
|
216
|
+
# and
|
217
|
+
#
|
218
|
+
# foo
|
219
|
+
# &.bar
|
220
|
+
# color: blue
|
221
|
+
#
|
222
|
+
# becomes
|
223
|
+
#
|
224
|
+
# foo.bar
|
225
|
+
# color: blue
|
226
|
+
#
|
227
|
+
# @param root [Tree::Node] The parent node
|
228
|
+
def flatten_rules(root)
|
229
|
+
root.children.each do |child|
|
230
|
+
case child
|
231
|
+
when Tree::RuleNode
|
232
|
+
flatten_rule(child)
|
233
|
+
when Tree::DirectiveNode
|
234
|
+
flatten_rules(child)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# Flattens a single rule
|
240
|
+
#
|
241
|
+
# @param rule [Tree::RuleNode] The candidate for flattening
|
242
|
+
# @see #flatten_rules
|
243
|
+
def flatten_rule(rule)
|
244
|
+
while rule.children.size == 1 && rule.children.first.is_a?(Tree::RuleNode)
|
245
|
+
child = rule.children.first
|
246
|
+
|
247
|
+
if child.rule.first[0] == ?&
|
248
|
+
rule.rule = [child.rule.first.gsub(/^&/, rule.rule.first)]
|
249
|
+
else
|
250
|
+
rule.rule = ["#{rule.rule.first} #{child.rule.first}"]
|
251
|
+
end
|
252
|
+
|
253
|
+
rule.children = child.children
|
254
|
+
end
|
255
|
+
|
256
|
+
flatten_rules(rule)
|
257
|
+
end
|
258
|
+
|
259
|
+
# Transform
|
260
|
+
#
|
261
|
+
# foo
|
262
|
+
# bar
|
263
|
+
# color: blue
|
264
|
+
# baz
|
265
|
+
# color: blue
|
266
|
+
#
|
267
|
+
# into
|
268
|
+
#
|
269
|
+
# foo
|
270
|
+
# bar, baz
|
271
|
+
# color: blue
|
272
|
+
#
|
273
|
+
# @param rule [Tree::RuleNode] The candidate for flattening
|
274
|
+
def fold_commas(root)
|
275
|
+
prev_rule = nil
|
276
|
+
root.children.map! do |child|
|
277
|
+
unless child.is_a?(Tree::RuleNode)
|
278
|
+
fold_commas(child) if child.is_a?(Tree::DirectiveNode)
|
279
|
+
next child
|
280
|
+
end
|
281
|
+
|
282
|
+
if prev_rule && prev_rule.children == child.children
|
283
|
+
prev_rule.rule.first << ", #{child.rule.first}"
|
284
|
+
next nil
|
285
|
+
end
|
286
|
+
|
287
|
+
fold_commas(child)
|
288
|
+
prev_rule = child
|
289
|
+
child
|
290
|
+
end
|
291
|
+
root.children.compact!
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|