sass4 4.0.0
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.
- checksums.yaml +7 -0
- data/.yardopts +13 -0
- data/AGENTS.md +534 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/CONTRIBUTING.md +148 -0
- data/MIT-LICENSE +20 -0
- data/README.md +242 -0
- data/VERSION +1 -0
- data/VERSION_NAME +1 -0
- data/bin/sass +13 -0
- data/bin/sass-convert +12 -0
- data/bin/scss +13 -0
- data/extra/sass-spec-ref.sh +40 -0
- data/extra/update_watch.rb +13 -0
- data/init.rb +18 -0
- data/lib/sass/cache_stores/base.rb +88 -0
- data/lib/sass/cache_stores/chain.rb +34 -0
- data/lib/sass/cache_stores/filesystem.rb +60 -0
- data/lib/sass/cache_stores/memory.rb +46 -0
- data/lib/sass/cache_stores/null.rb +25 -0
- data/lib/sass/cache_stores.rb +15 -0
- data/lib/sass/callbacks.rb +67 -0
- data/lib/sass/css.rb +407 -0
- data/lib/sass/deprecation.rb +55 -0
- data/lib/sass/engine.rb +1236 -0
- data/lib/sass/environment.rb +236 -0
- data/lib/sass/error.rb +198 -0
- data/lib/sass/exec/base.rb +188 -0
- data/lib/sass/exec/sass_convert.rb +283 -0
- data/lib/sass/exec/sass_scss.rb +436 -0
- data/lib/sass/exec.rb +9 -0
- data/lib/sass/features.rb +48 -0
- data/lib/sass/importers/base.rb +182 -0
- data/lib/sass/importers/deprecated_path.rb +51 -0
- data/lib/sass/importers/filesystem.rb +221 -0
- data/lib/sass/importers.rb +23 -0
- data/lib/sass/logger/base.rb +47 -0
- data/lib/sass/logger/delayed.rb +50 -0
- data/lib/sass/logger/log_level.rb +45 -0
- data/lib/sass/logger.rb +17 -0
- data/lib/sass/media.rb +210 -0
- data/lib/sass/plugin/compiler.rb +552 -0
- data/lib/sass/plugin/configuration.rb +134 -0
- data/lib/sass/plugin/generic.rb +15 -0
- data/lib/sass/plugin/merb.rb +48 -0
- data/lib/sass/plugin/rack.rb +60 -0
- data/lib/sass/plugin/rails.rb +47 -0
- data/lib/sass/plugin/staleness_checker.rb +199 -0
- data/lib/sass/plugin.rb +134 -0
- data/lib/sass/railtie.rb +10 -0
- data/lib/sass/repl.rb +57 -0
- data/lib/sass/root.rb +7 -0
- data/lib/sass/script/css_lexer.rb +33 -0
- data/lib/sass/script/css_parser.rb +36 -0
- data/lib/sass/script/functions.rb +3103 -0
- data/lib/sass/script/lexer.rb +518 -0
- data/lib/sass/script/parser.rb +1164 -0
- data/lib/sass/script/tree/funcall.rb +314 -0
- data/lib/sass/script/tree/interpolation.rb +220 -0
- data/lib/sass/script/tree/list_literal.rb +119 -0
- data/lib/sass/script/tree/literal.rb +49 -0
- data/lib/sass/script/tree/map_literal.rb +64 -0
- data/lib/sass/script/tree/node.rb +119 -0
- data/lib/sass/script/tree/operation.rb +149 -0
- data/lib/sass/script/tree/selector.rb +26 -0
- data/lib/sass/script/tree/string_interpolation.rb +125 -0
- data/lib/sass/script/tree/unary_operation.rb +69 -0
- data/lib/sass/script/tree/variable.rb +57 -0
- data/lib/sass/script/tree.rb +16 -0
- data/lib/sass/script/value/arg_list.rb +36 -0
- data/lib/sass/script/value/base.rb +258 -0
- data/lib/sass/script/value/bool.rb +35 -0
- data/lib/sass/script/value/callable.rb +25 -0
- data/lib/sass/script/value/color.rb +704 -0
- data/lib/sass/script/value/function.rb +19 -0
- data/lib/sass/script/value/helpers.rb +298 -0
- data/lib/sass/script/value/list.rb +135 -0
- data/lib/sass/script/value/map.rb +70 -0
- data/lib/sass/script/value/null.rb +44 -0
- data/lib/sass/script/value/number.rb +564 -0
- data/lib/sass/script/value/string.rb +138 -0
- data/lib/sass/script/value.rb +13 -0
- data/lib/sass/script.rb +66 -0
- data/lib/sass/scss/css_parser.rb +61 -0
- data/lib/sass/scss/parser.rb +1343 -0
- data/lib/sass/scss/rx.rb +134 -0
- data/lib/sass/scss/static_parser.rb +351 -0
- data/lib/sass/scss.rb +14 -0
- data/lib/sass/selector/abstract_sequence.rb +112 -0
- data/lib/sass/selector/comma_sequence.rb +195 -0
- data/lib/sass/selector/pseudo.rb +291 -0
- data/lib/sass/selector/sequence.rb +661 -0
- data/lib/sass/selector/simple.rb +124 -0
- data/lib/sass/selector/simple_sequence.rb +348 -0
- data/lib/sass/selector.rb +327 -0
- data/lib/sass/shared.rb +76 -0
- data/lib/sass/source/map.rb +209 -0
- data/lib/sass/source/position.rb +39 -0
- data/lib/sass/source/range.rb +41 -0
- data/lib/sass/stack.rb +140 -0
- data/lib/sass/supports.rb +225 -0
- data/lib/sass/tree/at_root_node.rb +83 -0
- data/lib/sass/tree/charset_node.rb +22 -0
- data/lib/sass/tree/comment_node.rb +82 -0
- data/lib/sass/tree/content_node.rb +9 -0
- data/lib/sass/tree/css_import_node.rb +68 -0
- data/lib/sass/tree/debug_node.rb +18 -0
- data/lib/sass/tree/directive_node.rb +59 -0
- data/lib/sass/tree/each_node.rb +24 -0
- data/lib/sass/tree/error_node.rb +18 -0
- data/lib/sass/tree/extend_node.rb +43 -0
- data/lib/sass/tree/for_node.rb +36 -0
- data/lib/sass/tree/function_node.rb +44 -0
- data/lib/sass/tree/if_node.rb +52 -0
- data/lib/sass/tree/import_node.rb +75 -0
- data/lib/sass/tree/keyframe_rule_node.rb +15 -0
- data/lib/sass/tree/media_node.rb +48 -0
- data/lib/sass/tree/mixin_def_node.rb +38 -0
- data/lib/sass/tree/mixin_node.rb +52 -0
- data/lib/sass/tree/node.rb +240 -0
- data/lib/sass/tree/prop_node.rb +162 -0
- data/lib/sass/tree/return_node.rb +19 -0
- data/lib/sass/tree/root_node.rb +44 -0
- data/lib/sass/tree/rule_node.rb +153 -0
- data/lib/sass/tree/supports_node.rb +38 -0
- data/lib/sass/tree/trace_node.rb +33 -0
- data/lib/sass/tree/variable_node.rb +36 -0
- data/lib/sass/tree/visitors/base.rb +72 -0
- data/lib/sass/tree/visitors/check_nesting.rb +173 -0
- data/lib/sass/tree/visitors/convert.rb +350 -0
- data/lib/sass/tree/visitors/cssize.rb +362 -0
- data/lib/sass/tree/visitors/deep_copy.rb +107 -0
- data/lib/sass/tree/visitors/extend.rb +64 -0
- data/lib/sass/tree/visitors/perform.rb +572 -0
- data/lib/sass/tree/visitors/set_options.rb +139 -0
- data/lib/sass/tree/visitors/to_css.rb +440 -0
- data/lib/sass/tree/warn_node.rb +18 -0
- data/lib/sass/tree/while_node.rb +18 -0
- data/lib/sass/util/multibyte_string_scanner.rb +151 -0
- data/lib/sass/util/normalized_map.rb +122 -0
- data/lib/sass/util/subset_map.rb +109 -0
- data/lib/sass/util/test.rb +9 -0
- data/lib/sass/util.rb +1137 -0
- data/lib/sass/version.rb +120 -0
- data/lib/sass.rb +102 -0
- data/rails/init.rb +1 -0
- metadata +283 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Sass
|
4
|
+
module CacheStores
|
5
|
+
# A backend for the Sass cache using the filesystem.
|
6
|
+
class Filesystem < Base
|
7
|
+
# The directory where the cached files will be stored.
|
8
|
+
#
|
9
|
+
# @return [String]
|
10
|
+
attr_accessor :cache_location
|
11
|
+
|
12
|
+
# @param cache_location [String] see \{#cache\_location}
|
13
|
+
def initialize(cache_location)
|
14
|
+
@cache_location = cache_location
|
15
|
+
end
|
16
|
+
|
17
|
+
# @see Base#\_retrieve
|
18
|
+
def _retrieve(key, version, sha)
|
19
|
+
return unless File.readable?(path_to(key))
|
20
|
+
begin
|
21
|
+
File.open(path_to(key), "rb") do |f|
|
22
|
+
if f.readline("\n").strip == version && f.readline("\n").strip == sha
|
23
|
+
return f.read
|
24
|
+
end
|
25
|
+
end
|
26
|
+
File.unlink path_to(key)
|
27
|
+
rescue Errno::ENOENT
|
28
|
+
# Already deleted. Race condition?
|
29
|
+
end
|
30
|
+
nil
|
31
|
+
rescue EOFError, TypeError, ArgumentError => e
|
32
|
+
Sass::Util.sass_warn "Warning. Error encountered while reading cache #{path_to(key)}: #{e}"
|
33
|
+
end
|
34
|
+
|
35
|
+
# @see Base#\_store
|
36
|
+
def _store(key, version, sha, contents)
|
37
|
+
compiled_filename = path_to(key)
|
38
|
+
FileUtils.mkdir_p(File.dirname(compiled_filename))
|
39
|
+
Sass::Util.atomic_create_and_write_file(compiled_filename) do |f|
|
40
|
+
f.puts(version)
|
41
|
+
f.puts(sha)
|
42
|
+
f.write(contents)
|
43
|
+
end
|
44
|
+
rescue Errno::EACCES
|
45
|
+
# pass
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Returns the path to a file for the given key.
|
51
|
+
#
|
52
|
+
# @param key [String]
|
53
|
+
# @return [String] The path to the cache file.
|
54
|
+
def path_to(key)
|
55
|
+
key = key.gsub(/[<>:\\|?*%]/) {|c| "%%%03d" % c.ord}
|
56
|
+
File.join(cache_location, key)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Sass
|
2
|
+
module CacheStores
|
3
|
+
# A backend for the Sass cache using in-process memory.
|
4
|
+
class Memory < Base
|
5
|
+
# Since the {Memory} store is stored in the Sass tree's options hash,
|
6
|
+
# when the options get serialized as part of serializing the tree,
|
7
|
+
# you get crazy exponential growth in the size of the cached objects
|
8
|
+
# unless you don't dump the cache.
|
9
|
+
#
|
10
|
+
# @private
|
11
|
+
def _dump(depth)
|
12
|
+
""
|
13
|
+
end
|
14
|
+
|
15
|
+
# If we deserialize this class, just make a new empty one.
|
16
|
+
#
|
17
|
+
# @private
|
18
|
+
def self._load(repr)
|
19
|
+
Memory.new
|
20
|
+
end
|
21
|
+
|
22
|
+
# Create a new, empty cache store.
|
23
|
+
def initialize
|
24
|
+
@contents = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
# @see Base#retrieve
|
28
|
+
def retrieve(key, sha)
|
29
|
+
return unless @contents.has_key?(key)
|
30
|
+
return unless @contents[key][:sha] == sha
|
31
|
+
obj = @contents[key][:obj]
|
32
|
+
obj.respond_to?(:deep_copy) ? obj.deep_copy : obj.dup
|
33
|
+
end
|
34
|
+
|
35
|
+
# @see Base#store
|
36
|
+
def store(key, sha, obj)
|
37
|
+
@contents[key] = {:sha => sha, :obj => obj}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Destructively clear the cache.
|
41
|
+
def reset!
|
42
|
+
@contents = {}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Sass
|
2
|
+
module CacheStores
|
3
|
+
# Doesn't store anything, but records what things it should have stored.
|
4
|
+
# This doesn't currently have any use except for testing and debugging.
|
5
|
+
#
|
6
|
+
# @private
|
7
|
+
class Null < Base
|
8
|
+
def initialize
|
9
|
+
@keys = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def _retrieve(key, version, sha)
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def _store(key, version, sha, contents)
|
17
|
+
@keys[key] = true
|
18
|
+
end
|
19
|
+
|
20
|
+
def was_set?(key)
|
21
|
+
@keys[key]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Sass
|
4
|
+
# Sass cache stores are in charge of storing cached information,
|
5
|
+
# especially parse trees for Sass documents.
|
6
|
+
#
|
7
|
+
# User-created importers must inherit from {CacheStores::Base}.
|
8
|
+
module CacheStores
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'sass/cache_stores/base'
|
13
|
+
require 'sass/cache_stores/filesystem'
|
14
|
+
require 'sass/cache_stores/memory'
|
15
|
+
require 'sass/cache_stores/chain'
|
@@ -0,0 +1,67 @@
|
|
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
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
module InstanceMethods
|
37
|
+
# Removes all callbacks registered against this object.
|
38
|
+
def clear_callbacks!
|
39
|
+
@_sass_callbacks = {}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Define a callback with the given name.
|
44
|
+
# This will define an `on_#{name}` method
|
45
|
+
# that registers a block,
|
46
|
+
# and a `run_#{name}` method that runs that block
|
47
|
+
# (optionall with some arguments).
|
48
|
+
#
|
49
|
+
# @param name [Symbol] The name of the callback
|
50
|
+
# @return [void]
|
51
|
+
def define_callback(name)
|
52
|
+
class_eval <<RUBY, __FILE__, __LINE__ + 1
|
53
|
+
def on_#{name}(&block)
|
54
|
+
@_sass_callbacks = {} unless defined? @_sass_callbacks
|
55
|
+
(@_sass_callbacks[#{name.inspect}] ||= []) << block
|
56
|
+
end
|
57
|
+
|
58
|
+
def run_#{name}(*args)
|
59
|
+
return unless defined? @_sass_callbacks
|
60
|
+
return unless @_sass_callbacks[#{name.inspect}]
|
61
|
+
@_sass_callbacks[#{name.inspect}].each {|c| c[*args]}
|
62
|
+
end
|
63
|
+
private :run_#{name}
|
64
|
+
RUBY
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/sass/css.rb
ADDED
@@ -0,0 +1,407 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../sass'
|
2
|
+
require 'sass/tree/node'
|
3
|
+
require 'sass/scss/css_parser'
|
4
|
+
|
5
|
+
module Sass
|
6
|
+
# This class converts CSS documents into Sass or SCSS templates.
|
7
|
+
# It works by parsing the CSS document into a {Sass::Tree} structure,
|
8
|
+
# and then applying various transformations to the structure
|
9
|
+
# to produce more concise and idiomatic Sass/SCSS.
|
10
|
+
#
|
11
|
+
# Example usage:
|
12
|
+
#
|
13
|
+
# Sass::CSS.new("p { color: blue }").render(:sass) #=> "p\n color: blue"
|
14
|
+
# Sass::CSS.new("p { color: blue }").render(:scss) #=> "p {\n color: blue; }"
|
15
|
+
class CSS
|
16
|
+
# @param template [String] The CSS stylesheet.
|
17
|
+
# This stylesheet can be encoded using any encoding
|
18
|
+
# that can be converted to Unicode.
|
19
|
+
# If the stylesheet contains an `@charset` declaration,
|
20
|
+
# that overrides the Ruby encoding
|
21
|
+
# (see {file:SASS_REFERENCE.md#Encodings the encoding documentation})
|
22
|
+
# @option options :old [Boolean] (false)
|
23
|
+
# Whether or not to output old property syntax
|
24
|
+
# (`:color blue` as opposed to `color: blue`).
|
25
|
+
# This is only meaningful when generating Sass code,
|
26
|
+
# rather than SCSS.
|
27
|
+
# @option options :indent [String] (" ")
|
28
|
+
# The string to use for indenting each line. Defaults to two spaces.
|
29
|
+
def initialize(template, options = {})
|
30
|
+
if template.is_a? IO
|
31
|
+
template = template.read
|
32
|
+
end
|
33
|
+
|
34
|
+
@options = options.merge(:_convert => true)
|
35
|
+
# Backwards compatibility
|
36
|
+
@options[:old] = true if @options[:alternate] == false
|
37
|
+
@template = template
|
38
|
+
@checked_encoding = false
|
39
|
+
end
|
40
|
+
|
41
|
+
# Converts the CSS template into Sass or SCSS code.
|
42
|
+
#
|
43
|
+
# @param fmt [Symbol] `:sass` or `:scss`, designating the format to return.
|
44
|
+
# @return [String] The resulting Sass or SCSS code
|
45
|
+
# @raise [Sass::SyntaxError] if there's an error parsing the CSS template
|
46
|
+
def render(fmt = :sass)
|
47
|
+
check_encoding!
|
48
|
+
build_tree.send("to_#{fmt}", @options).strip + "\n"
|
49
|
+
rescue Sass::SyntaxError => err
|
50
|
+
err.modify_backtrace(:filename => @options[:filename] || '(css)')
|
51
|
+
raise err
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the original encoding of the document.
|
55
|
+
#
|
56
|
+
# @return [Encoding, nil]
|
57
|
+
# @raise [Encoding::UndefinedConversionError] if the source encoding
|
58
|
+
# cannot be converted to UTF-8
|
59
|
+
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
|
60
|
+
def source_encoding
|
61
|
+
check_encoding!
|
62
|
+
@original_encoding
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def check_encoding!
|
68
|
+
return if @checked_encoding
|
69
|
+
@checked_encoding = true
|
70
|
+
@template, @original_encoding = Sass::Util.check_sass_encoding(@template)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Parses the CSS template and applies various transformations
|
74
|
+
#
|
75
|
+
# @return [Tree::Node] The root node of the parsed tree
|
76
|
+
def build_tree
|
77
|
+
root = Sass::SCSS::CssParser.new(@template, @options[:filename], nil).parse
|
78
|
+
parse_selectors(root)
|
79
|
+
expand_commas(root)
|
80
|
+
nest_seqs(root)
|
81
|
+
parent_ref_rules(root)
|
82
|
+
flatten_rules(root)
|
83
|
+
bubble_subject(root)
|
84
|
+
fold_commas(root)
|
85
|
+
dump_selectors(root)
|
86
|
+
root
|
87
|
+
end
|
88
|
+
|
89
|
+
# Parse all the selectors in the document and assign them to
|
90
|
+
# {Sass::Tree::RuleNode#parsed_rules}.
|
91
|
+
#
|
92
|
+
# @param root [Tree::Node] The parent node
|
93
|
+
def parse_selectors(root)
|
94
|
+
root.children.each do |child|
|
95
|
+
next parse_selectors(child) if child.is_a?(Tree::DirectiveNode)
|
96
|
+
next unless child.is_a?(Tree::RuleNode)
|
97
|
+
parser = Sass::SCSS::CssParser.new(child.rule.first, child.filename, nil, child.line)
|
98
|
+
child.parsed_rules = parser.parse_selector
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Transform
|
103
|
+
#
|
104
|
+
# foo, bar, baz
|
105
|
+
# color: blue
|
106
|
+
#
|
107
|
+
# into
|
108
|
+
#
|
109
|
+
# foo
|
110
|
+
# color: blue
|
111
|
+
# bar
|
112
|
+
# color: blue
|
113
|
+
# baz
|
114
|
+
# color: blue
|
115
|
+
#
|
116
|
+
# @param root [Tree::Node] The parent node
|
117
|
+
def expand_commas(root)
|
118
|
+
root.children.map! do |child|
|
119
|
+
# child.parsed_rules.members.size > 1 iff the rule contains a comma
|
120
|
+
unless child.is_a?(Tree::RuleNode) && child.parsed_rules.members.size > 1
|
121
|
+
expand_commas(child) if child.is_a?(Tree::DirectiveNode)
|
122
|
+
next child
|
123
|
+
end
|
124
|
+
child.parsed_rules.members.map do |seq|
|
125
|
+
node = Tree::RuleNode.new([])
|
126
|
+
node.parsed_rules = make_cseq(seq)
|
127
|
+
node.children = child.children
|
128
|
+
node
|
129
|
+
end
|
130
|
+
end
|
131
|
+
root.children.flatten!
|
132
|
+
end
|
133
|
+
|
134
|
+
# Make rules use nesting so that
|
135
|
+
#
|
136
|
+
# foo
|
137
|
+
# color: green
|
138
|
+
# foo bar
|
139
|
+
# color: red
|
140
|
+
# foo baz
|
141
|
+
# color: blue
|
142
|
+
#
|
143
|
+
# becomes
|
144
|
+
#
|
145
|
+
# foo
|
146
|
+
# color: green
|
147
|
+
# bar
|
148
|
+
# color: red
|
149
|
+
# baz
|
150
|
+
# color: blue
|
151
|
+
#
|
152
|
+
# @param root [Tree::Node] The parent node
|
153
|
+
def nest_seqs(root)
|
154
|
+
current_rule = nil
|
155
|
+
root.children.map! do |child|
|
156
|
+
unless child.is_a?(Tree::RuleNode)
|
157
|
+
nest_seqs(child) if child.is_a?(Tree::DirectiveNode)
|
158
|
+
next child
|
159
|
+
end
|
160
|
+
|
161
|
+
seq = first_seq(child)
|
162
|
+
seq.members.reject! {|sseq| sseq == "\n"}
|
163
|
+
first, rest = seq.members.first, seq.members[1..-1]
|
164
|
+
|
165
|
+
if current_rule.nil? || first_sseq(current_rule) != first
|
166
|
+
current_rule = Tree::RuleNode.new([])
|
167
|
+
current_rule.parsed_rules = make_seq(first)
|
168
|
+
end
|
169
|
+
|
170
|
+
if rest.empty?
|
171
|
+
current_rule.children += child.children
|
172
|
+
else
|
173
|
+
child.parsed_rules = make_seq(*rest)
|
174
|
+
current_rule << child
|
175
|
+
end
|
176
|
+
|
177
|
+
current_rule
|
178
|
+
end
|
179
|
+
root.children.compact!
|
180
|
+
root.children.uniq!
|
181
|
+
|
182
|
+
root.children.each {|v| nest_seqs(v)}
|
183
|
+
end
|
184
|
+
|
185
|
+
# Make rules use parent refs so that
|
186
|
+
#
|
187
|
+
# foo
|
188
|
+
# color: green
|
189
|
+
# foo.bar
|
190
|
+
# color: blue
|
191
|
+
#
|
192
|
+
# becomes
|
193
|
+
#
|
194
|
+
# foo
|
195
|
+
# color: green
|
196
|
+
# &.bar
|
197
|
+
# color: blue
|
198
|
+
#
|
199
|
+
# @param root [Tree::Node] The parent node
|
200
|
+
def parent_ref_rules(root)
|
201
|
+
current_rule = nil
|
202
|
+
root.children.map! do |child|
|
203
|
+
unless child.is_a?(Tree::RuleNode)
|
204
|
+
parent_ref_rules(child) if child.is_a?(Tree::DirectiveNode)
|
205
|
+
next child
|
206
|
+
end
|
207
|
+
|
208
|
+
sseq = first_sseq(child)
|
209
|
+
next child unless sseq.is_a?(Sass::Selector::SimpleSequence)
|
210
|
+
|
211
|
+
firsts, rest = [sseq.members.first], sseq.members[1..-1]
|
212
|
+
firsts.push rest.shift if firsts.first.is_a?(Sass::Selector::Parent)
|
213
|
+
|
214
|
+
last_simple_subject = rest.empty? && sseq.subject?
|
215
|
+
if current_rule.nil? || first_sseq(current_rule).members != firsts ||
|
216
|
+
!!first_sseq(current_rule).subject? != !!last_simple_subject
|
217
|
+
current_rule = Tree::RuleNode.new([])
|
218
|
+
current_rule.parsed_rules = make_sseq(last_simple_subject, *firsts)
|
219
|
+
end
|
220
|
+
|
221
|
+
if rest.empty?
|
222
|
+
current_rule.children += child.children
|
223
|
+
else
|
224
|
+
rest.unshift Sass::Selector::Parent.new
|
225
|
+
child.parsed_rules = make_sseq(sseq.subject?, *rest)
|
226
|
+
current_rule << child
|
227
|
+
end
|
228
|
+
|
229
|
+
current_rule
|
230
|
+
end
|
231
|
+
root.children.compact!
|
232
|
+
root.children.uniq!
|
233
|
+
|
234
|
+
root.children.each {|v| parent_ref_rules(v)}
|
235
|
+
end
|
236
|
+
|
237
|
+
# Flatten rules so that
|
238
|
+
#
|
239
|
+
# foo
|
240
|
+
# bar
|
241
|
+
# color: red
|
242
|
+
#
|
243
|
+
# becomes
|
244
|
+
#
|
245
|
+
# foo bar
|
246
|
+
# color: red
|
247
|
+
#
|
248
|
+
# and
|
249
|
+
#
|
250
|
+
# foo
|
251
|
+
# &.bar
|
252
|
+
# color: blue
|
253
|
+
#
|
254
|
+
# becomes
|
255
|
+
#
|
256
|
+
# foo.bar
|
257
|
+
# color: blue
|
258
|
+
#
|
259
|
+
# @param root [Tree::Node] The parent node
|
260
|
+
def flatten_rules(root)
|
261
|
+
root.children.each do |child|
|
262
|
+
case child
|
263
|
+
when Tree::RuleNode
|
264
|
+
flatten_rule(child)
|
265
|
+
when Tree::DirectiveNode
|
266
|
+
flatten_rules(child)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# Flattens a single rule.
|
272
|
+
#
|
273
|
+
# @param rule [Tree::RuleNode] The candidate for flattening
|
274
|
+
# @see #flatten_rules
|
275
|
+
def flatten_rule(rule)
|
276
|
+
while rule.children.size == 1 && rule.children.first.is_a?(Tree::RuleNode)
|
277
|
+
child = rule.children.first
|
278
|
+
|
279
|
+
if first_simple_sel(child).is_a?(Sass::Selector::Parent)
|
280
|
+
rule.parsed_rules = child.parsed_rules.resolve_parent_refs(rule.parsed_rules)
|
281
|
+
else
|
282
|
+
rule.parsed_rules = make_seq(*(first_seq(rule).members + first_seq(child).members))
|
283
|
+
end
|
284
|
+
|
285
|
+
rule.children = child.children
|
286
|
+
end
|
287
|
+
|
288
|
+
flatten_rules(rule)
|
289
|
+
end
|
290
|
+
|
291
|
+
def bubble_subject(root)
|
292
|
+
root.children.each do |child|
|
293
|
+
bubble_subject(child) if child.is_a?(Tree::RuleNode) || child.is_a?(Tree::DirectiveNode)
|
294
|
+
next unless child.is_a?(Tree::RuleNode) && !child.children.empty?
|
295
|
+
next unless child.children.all? do |c|
|
296
|
+
next unless c.is_a?(Tree::RuleNode)
|
297
|
+
first_simple_sel(c).is_a?(Sass::Selector::Parent) && first_sseq(c).subject?
|
298
|
+
end
|
299
|
+
first_sseq(child).subject = true
|
300
|
+
child.children.each {|c| first_sseq(c).subject = false}
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
# Transform
|
305
|
+
#
|
306
|
+
# foo
|
307
|
+
# bar
|
308
|
+
# color: blue
|
309
|
+
# baz
|
310
|
+
# color: blue
|
311
|
+
#
|
312
|
+
# into
|
313
|
+
#
|
314
|
+
# foo
|
315
|
+
# bar, baz
|
316
|
+
# color: blue
|
317
|
+
#
|
318
|
+
# @param root [Tree::Node] The parent node
|
319
|
+
def fold_commas(root)
|
320
|
+
prev_rule = nil
|
321
|
+
root.children.map! do |child|
|
322
|
+
unless child.is_a?(Tree::RuleNode)
|
323
|
+
fold_commas(child) if child.is_a?(Tree::DirectiveNode)
|
324
|
+
next child
|
325
|
+
end
|
326
|
+
|
327
|
+
if prev_rule && prev_rule.children.map {|c| c.to_sass} == child.children.map {|c| c.to_sass}
|
328
|
+
prev_rule.parsed_rules.members << first_seq(child)
|
329
|
+
next nil
|
330
|
+
end
|
331
|
+
|
332
|
+
fold_commas(child)
|
333
|
+
prev_rule = child
|
334
|
+
child
|
335
|
+
end
|
336
|
+
root.children.compact!
|
337
|
+
end
|
338
|
+
|
339
|
+
# Dump all the parsed {Sass::Tree::RuleNode} selectors to strings.
|
340
|
+
#
|
341
|
+
# @param root [Tree::Node] The parent node
|
342
|
+
def dump_selectors(root)
|
343
|
+
root.children.each do |child|
|
344
|
+
next dump_selectors(child) if child.is_a?(Tree::DirectiveNode)
|
345
|
+
next unless child.is_a?(Tree::RuleNode)
|
346
|
+
child.rule = [child.parsed_rules.to_s]
|
347
|
+
dump_selectors(child)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
# Create a {Sass::Selector::CommaSequence}.
|
352
|
+
#
|
353
|
+
# @param seqs [Array<Sass::Selector::Sequence>]
|
354
|
+
# @return [Sass::Selector::CommaSequence]
|
355
|
+
def make_cseq(*seqs)
|
356
|
+
Sass::Selector::CommaSequence.new(seqs)
|
357
|
+
end
|
358
|
+
|
359
|
+
# Create a {Sass::Selector::CommaSequence} containing only a single
|
360
|
+
# {Sass::Selector::Sequence}.
|
361
|
+
#
|
362
|
+
# @param sseqs [Array<Sass::Selector::Sequence, String>]
|
363
|
+
# @return [Sass::Selector::CommaSequence]
|
364
|
+
def make_seq(*sseqs)
|
365
|
+
make_cseq(Sass::Selector::Sequence.new(sseqs))
|
366
|
+
end
|
367
|
+
|
368
|
+
# Create a {Sass::Selector::CommaSequence} containing only a single
|
369
|
+
# {Sass::Selector::Sequence} which in turn contains only a single
|
370
|
+
# {Sass::Selector::SimpleSequence}.
|
371
|
+
#
|
372
|
+
# @param subject [Boolean] Whether this is a subject selector
|
373
|
+
# @param sseqs [Array<Sass::Selector::Sequence, String>]
|
374
|
+
# @return [Sass::Selector::CommaSequence]
|
375
|
+
def make_sseq(subject, *sseqs)
|
376
|
+
make_seq(Sass::Selector::SimpleSequence.new(sseqs, subject))
|
377
|
+
end
|
378
|
+
|
379
|
+
# Return the first {Sass::Selector::Sequence} in a {Sass::Tree::RuleNode}.
|
380
|
+
#
|
381
|
+
# @param rule [Sass::Tree::RuleNode]
|
382
|
+
# @return [Sass::Selector::Sequence]
|
383
|
+
def first_seq(rule)
|
384
|
+
rule.parsed_rules.members.first
|
385
|
+
end
|
386
|
+
|
387
|
+
# Return the first {Sass::Selector::SimpleSequence} in a
|
388
|
+
# {Sass::Tree::RuleNode}.
|
389
|
+
#
|
390
|
+
# @param rule [Sass::Tree::RuleNode]
|
391
|
+
# @return [Sass::Selector::SimpleSequence, String]
|
392
|
+
def first_sseq(rule)
|
393
|
+
first_seq(rule).members.first
|
394
|
+
end
|
395
|
+
|
396
|
+
# Return the first {Sass::Selector::Simple} in a {Sass::Tree::RuleNode},
|
397
|
+
# unless the rule begins with a combinator.
|
398
|
+
#
|
399
|
+
# @param rule [Sass::Tree::RuleNode]
|
400
|
+
# @return [Sass::Selector::Simple?]
|
401
|
+
def first_simple_sel(rule)
|
402
|
+
sseq = first_sseq(rule)
|
403
|
+
return unless sseq.is_a?(Sass::Selector::SimpleSequence)
|
404
|
+
sseq.members.first
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Sass
|
2
|
+
# A deprecation warning that should only be printed once for a given line in a
|
3
|
+
# given file.
|
4
|
+
#
|
5
|
+
# A global Deprecation instance should be created for each type of deprecation
|
6
|
+
# warning, and `warn` should be called each time a warning is needed.
|
7
|
+
class Deprecation
|
8
|
+
@@allow_double_warnings = false
|
9
|
+
|
10
|
+
# Runs a block in which double deprecation warnings for the same location
|
11
|
+
# are allowed.
|
12
|
+
def self.allow_double_warnings
|
13
|
+
old_allow_double_warnings = @@allow_double_warnings
|
14
|
+
@@allow_double_warnings = true
|
15
|
+
yield
|
16
|
+
ensure
|
17
|
+
@@allow_double_warnings = old_allow_double_warnings
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
# A set of filename, line pairs for which warnings have been emitted.
|
22
|
+
@seen = Set.new
|
23
|
+
end
|
24
|
+
|
25
|
+
# Prints `message` as a deprecation warning associated with `filename`,
|
26
|
+
# `line`, and optionally `column`.
|
27
|
+
#
|
28
|
+
# This ensures that only one message will be printed for each line of a
|
29
|
+
# given file.
|
30
|
+
#
|
31
|
+
# @overload warn(filename, line, message)
|
32
|
+
# @param filename [String, nil]
|
33
|
+
# @param line [Number]
|
34
|
+
# @param message [String]
|
35
|
+
# @overload warn(filename, line, column, message)
|
36
|
+
# @param filename [String, nil]
|
37
|
+
# @param line [Number]
|
38
|
+
# @param column [Number]
|
39
|
+
# @param message [String]
|
40
|
+
def warn(filename, line, column_or_message, message = nil)
|
41
|
+
return if !@@allow_double_warnings && @seen.add?([filename, line]).nil?
|
42
|
+
if message
|
43
|
+
column = column_or_message
|
44
|
+
else
|
45
|
+
message = column_or_message
|
46
|
+
end
|
47
|
+
|
48
|
+
location = "line #{line}"
|
49
|
+
location << ", column #{column}" if column
|
50
|
+
location << " of #{filename}" if filename
|
51
|
+
|
52
|
+
Sass::Util.sass_warn("DEPRECATION WARNING on #{location}:\n#{message}")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|