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.
Files changed (147) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +13 -0
  3. data/AGENTS.md +534 -0
  4. data/CODE_OF_CONDUCT.md +10 -0
  5. data/CONTRIBUTING.md +148 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +242 -0
  8. data/VERSION +1 -0
  9. data/VERSION_NAME +1 -0
  10. data/bin/sass +13 -0
  11. data/bin/sass-convert +12 -0
  12. data/bin/scss +13 -0
  13. data/extra/sass-spec-ref.sh +40 -0
  14. data/extra/update_watch.rb +13 -0
  15. data/init.rb +18 -0
  16. data/lib/sass/cache_stores/base.rb +88 -0
  17. data/lib/sass/cache_stores/chain.rb +34 -0
  18. data/lib/sass/cache_stores/filesystem.rb +60 -0
  19. data/lib/sass/cache_stores/memory.rb +46 -0
  20. data/lib/sass/cache_stores/null.rb +25 -0
  21. data/lib/sass/cache_stores.rb +15 -0
  22. data/lib/sass/callbacks.rb +67 -0
  23. data/lib/sass/css.rb +407 -0
  24. data/lib/sass/deprecation.rb +55 -0
  25. data/lib/sass/engine.rb +1236 -0
  26. data/lib/sass/environment.rb +236 -0
  27. data/lib/sass/error.rb +198 -0
  28. data/lib/sass/exec/base.rb +188 -0
  29. data/lib/sass/exec/sass_convert.rb +283 -0
  30. data/lib/sass/exec/sass_scss.rb +436 -0
  31. data/lib/sass/exec.rb +9 -0
  32. data/lib/sass/features.rb +48 -0
  33. data/lib/sass/importers/base.rb +182 -0
  34. data/lib/sass/importers/deprecated_path.rb +51 -0
  35. data/lib/sass/importers/filesystem.rb +221 -0
  36. data/lib/sass/importers.rb +23 -0
  37. data/lib/sass/logger/base.rb +47 -0
  38. data/lib/sass/logger/delayed.rb +50 -0
  39. data/lib/sass/logger/log_level.rb +45 -0
  40. data/lib/sass/logger.rb +17 -0
  41. data/lib/sass/media.rb +210 -0
  42. data/lib/sass/plugin/compiler.rb +552 -0
  43. data/lib/sass/plugin/configuration.rb +134 -0
  44. data/lib/sass/plugin/generic.rb +15 -0
  45. data/lib/sass/plugin/merb.rb +48 -0
  46. data/lib/sass/plugin/rack.rb +60 -0
  47. data/lib/sass/plugin/rails.rb +47 -0
  48. data/lib/sass/plugin/staleness_checker.rb +199 -0
  49. data/lib/sass/plugin.rb +134 -0
  50. data/lib/sass/railtie.rb +10 -0
  51. data/lib/sass/repl.rb +57 -0
  52. data/lib/sass/root.rb +7 -0
  53. data/lib/sass/script/css_lexer.rb +33 -0
  54. data/lib/sass/script/css_parser.rb +36 -0
  55. data/lib/sass/script/functions.rb +3103 -0
  56. data/lib/sass/script/lexer.rb +518 -0
  57. data/lib/sass/script/parser.rb +1164 -0
  58. data/lib/sass/script/tree/funcall.rb +314 -0
  59. data/lib/sass/script/tree/interpolation.rb +220 -0
  60. data/lib/sass/script/tree/list_literal.rb +119 -0
  61. data/lib/sass/script/tree/literal.rb +49 -0
  62. data/lib/sass/script/tree/map_literal.rb +64 -0
  63. data/lib/sass/script/tree/node.rb +119 -0
  64. data/lib/sass/script/tree/operation.rb +149 -0
  65. data/lib/sass/script/tree/selector.rb +26 -0
  66. data/lib/sass/script/tree/string_interpolation.rb +125 -0
  67. data/lib/sass/script/tree/unary_operation.rb +69 -0
  68. data/lib/sass/script/tree/variable.rb +57 -0
  69. data/lib/sass/script/tree.rb +16 -0
  70. data/lib/sass/script/value/arg_list.rb +36 -0
  71. data/lib/sass/script/value/base.rb +258 -0
  72. data/lib/sass/script/value/bool.rb +35 -0
  73. data/lib/sass/script/value/callable.rb +25 -0
  74. data/lib/sass/script/value/color.rb +704 -0
  75. data/lib/sass/script/value/function.rb +19 -0
  76. data/lib/sass/script/value/helpers.rb +298 -0
  77. data/lib/sass/script/value/list.rb +135 -0
  78. data/lib/sass/script/value/map.rb +70 -0
  79. data/lib/sass/script/value/null.rb +44 -0
  80. data/lib/sass/script/value/number.rb +564 -0
  81. data/lib/sass/script/value/string.rb +138 -0
  82. data/lib/sass/script/value.rb +13 -0
  83. data/lib/sass/script.rb +66 -0
  84. data/lib/sass/scss/css_parser.rb +61 -0
  85. data/lib/sass/scss/parser.rb +1343 -0
  86. data/lib/sass/scss/rx.rb +134 -0
  87. data/lib/sass/scss/static_parser.rb +351 -0
  88. data/lib/sass/scss.rb +14 -0
  89. data/lib/sass/selector/abstract_sequence.rb +112 -0
  90. data/lib/sass/selector/comma_sequence.rb +195 -0
  91. data/lib/sass/selector/pseudo.rb +291 -0
  92. data/lib/sass/selector/sequence.rb +661 -0
  93. data/lib/sass/selector/simple.rb +124 -0
  94. data/lib/sass/selector/simple_sequence.rb +348 -0
  95. data/lib/sass/selector.rb +327 -0
  96. data/lib/sass/shared.rb +76 -0
  97. data/lib/sass/source/map.rb +209 -0
  98. data/lib/sass/source/position.rb +39 -0
  99. data/lib/sass/source/range.rb +41 -0
  100. data/lib/sass/stack.rb +140 -0
  101. data/lib/sass/supports.rb +225 -0
  102. data/lib/sass/tree/at_root_node.rb +83 -0
  103. data/lib/sass/tree/charset_node.rb +22 -0
  104. data/lib/sass/tree/comment_node.rb +82 -0
  105. data/lib/sass/tree/content_node.rb +9 -0
  106. data/lib/sass/tree/css_import_node.rb +68 -0
  107. data/lib/sass/tree/debug_node.rb +18 -0
  108. data/lib/sass/tree/directive_node.rb +59 -0
  109. data/lib/sass/tree/each_node.rb +24 -0
  110. data/lib/sass/tree/error_node.rb +18 -0
  111. data/lib/sass/tree/extend_node.rb +43 -0
  112. data/lib/sass/tree/for_node.rb +36 -0
  113. data/lib/sass/tree/function_node.rb +44 -0
  114. data/lib/sass/tree/if_node.rb +52 -0
  115. data/lib/sass/tree/import_node.rb +75 -0
  116. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  117. data/lib/sass/tree/media_node.rb +48 -0
  118. data/lib/sass/tree/mixin_def_node.rb +38 -0
  119. data/lib/sass/tree/mixin_node.rb +52 -0
  120. data/lib/sass/tree/node.rb +240 -0
  121. data/lib/sass/tree/prop_node.rb +162 -0
  122. data/lib/sass/tree/return_node.rb +19 -0
  123. data/lib/sass/tree/root_node.rb +44 -0
  124. data/lib/sass/tree/rule_node.rb +153 -0
  125. data/lib/sass/tree/supports_node.rb +38 -0
  126. data/lib/sass/tree/trace_node.rb +33 -0
  127. data/lib/sass/tree/variable_node.rb +36 -0
  128. data/lib/sass/tree/visitors/base.rb +72 -0
  129. data/lib/sass/tree/visitors/check_nesting.rb +173 -0
  130. data/lib/sass/tree/visitors/convert.rb +350 -0
  131. data/lib/sass/tree/visitors/cssize.rb +362 -0
  132. data/lib/sass/tree/visitors/deep_copy.rb +107 -0
  133. data/lib/sass/tree/visitors/extend.rb +64 -0
  134. data/lib/sass/tree/visitors/perform.rb +572 -0
  135. data/lib/sass/tree/visitors/set_options.rb +139 -0
  136. data/lib/sass/tree/visitors/to_css.rb +440 -0
  137. data/lib/sass/tree/warn_node.rb +18 -0
  138. data/lib/sass/tree/while_node.rb +18 -0
  139. data/lib/sass/util/multibyte_string_scanner.rb +151 -0
  140. data/lib/sass/util/normalized_map.rb +122 -0
  141. data/lib/sass/util/subset_map.rb +109 -0
  142. data/lib/sass/util/test.rb +9 -0
  143. data/lib/sass/util.rb +1137 -0
  144. data/lib/sass/version.rb +120 -0
  145. data/lib/sass.rb +102 -0
  146. data/rails/init.rb +1 -0
  147. metadata +283 -0
@@ -0,0 +1,24 @@
1
+ require 'sass/tree/node'
2
+
3
+ module Sass::Tree
4
+ # A dynamic node representing a Sass `@each` loop.
5
+ #
6
+ # @see Sass::Tree
7
+ class EachNode < Node
8
+ # The names of the loop variables.
9
+ # @return [Array<String>]
10
+ attr_reader :vars
11
+
12
+ # The parse tree for the list.
13
+ # @return [Script::Tree::Node]
14
+ attr_accessor :list
15
+
16
+ # @param vars [Array<String>] The names of the loop variables
17
+ # @param list [Script::Tree::Node] The parse tree for the list
18
+ def initialize(vars, list)
19
+ @vars = vars
20
+ @list = list
21
+ super()
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ module Sass
2
+ module Tree
3
+ # A dynamic node representing a Sass `@error` statement.
4
+ #
5
+ # @see Sass::Tree
6
+ class ErrorNode < Node
7
+ # The expression to print.
8
+ # @return [Script::Tree::Node]
9
+ attr_accessor :expr
10
+
11
+ # @param expr [Script::Tree::Node] The expression to print
12
+ def initialize(expr)
13
+ @expr = expr
14
+ super()
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,43 @@
1
+ require 'sass/tree/node'
2
+
3
+ module Sass::Tree
4
+ # A static node representing an `@extend` directive.
5
+ #
6
+ # @see Sass::Tree
7
+ class ExtendNode < Node
8
+ # The parsed selector after interpolation has been resolved.
9
+ # Only set once {Tree::Visitors::Perform} has been run.
10
+ #
11
+ # @return [Selector::CommaSequence]
12
+ attr_accessor :resolved_selector
13
+
14
+ # The CSS selector to extend, interspersed with {Sass::Script::Tree::Node}s
15
+ # representing `#{}`-interpolation.
16
+ #
17
+ # @return [Array<String, Sass::Script::Tree::Node>]
18
+ attr_accessor :selector
19
+
20
+ # The extended selector source range.
21
+ #
22
+ # @return [Sass::Source::Range]
23
+ attr_accessor :selector_source_range
24
+
25
+ # Whether the `@extend` is allowed to match no selectors or not.
26
+ #
27
+ # @return [Boolean]
28
+ def optional?; @optional; end
29
+
30
+ # @param selector [Array<String, Sass::Script::Tree::Node>]
31
+ # The CSS selector to extend,
32
+ # interspersed with {Sass::Script::Tree::Node}s
33
+ # representing `#{}`-interpolation.
34
+ # @param optional [Boolean] See \{ExtendNode#optional?}
35
+ # @param selector_source_range [Sass::Source::Range] The extended selector source range.
36
+ def initialize(selector, optional, selector_source_range)
37
+ @selector = selector
38
+ @optional = optional
39
+ @selector_source_range = selector_source_range
40
+ super()
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,36 @@
1
+ require 'sass/tree/node'
2
+
3
+ module Sass::Tree
4
+ # A dynamic node representing a Sass `@for` loop.
5
+ #
6
+ # @see Sass::Tree
7
+ class ForNode < Node
8
+ # The name of the loop variable.
9
+ # @return [String]
10
+ attr_reader :var
11
+
12
+ # The parse tree for the initial expression.
13
+ # @return [Script::Tree::Node]
14
+ attr_accessor :from
15
+
16
+ # The parse tree for the final expression.
17
+ # @return [Script::Tree::Node]
18
+ attr_accessor :to
19
+
20
+ # Whether to include `to` in the loop or stop just before.
21
+ # @return [Boolean]
22
+ attr_reader :exclusive
23
+
24
+ # @param var [String] See \{#var}
25
+ # @param from [Script::Tree::Node] See \{#from}
26
+ # @param to [Script::Tree::Node] See \{#to}
27
+ # @param exclusive [Boolean] See \{#exclusive}
28
+ def initialize(var, from, to, exclusive)
29
+ @var = var
30
+ @from = from
31
+ @to = to
32
+ @exclusive = exclusive
33
+ super()
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,44 @@
1
+ module Sass
2
+ module Tree
3
+ # A dynamic node representing a function definition.
4
+ #
5
+ # @see Sass::Tree
6
+ class FunctionNode < Node
7
+ # The name of the function.
8
+ # @return [String]
9
+ attr_reader :name
10
+
11
+ # The arguments to the function. Each element is a tuple
12
+ # containing the variable for argument and the parse tree for
13
+ # the default value of the argument
14
+ #
15
+ # @return [Array<Script::Tree::Node>]
16
+ attr_accessor :args
17
+
18
+ # The splat argument for this function, if one exists.
19
+ #
20
+ # @return [Script::Tree::Node?]
21
+ attr_accessor :splat
22
+
23
+ # Strips out any vendor prefixes.
24
+ # @return [String] The normalized name of the directive.
25
+ def normalized_name
26
+ @normalized_name ||= name.gsub(/^(?:-[a-zA-Z0-9]+-)?/, '\1')
27
+ end
28
+
29
+ # @param name [String] The function name
30
+ # @param args [Array<(Script::Tree::Node, Script::Tree::Node)>]
31
+ # The arguments for the function.
32
+ # @param splat [Script::Tree::Node] See \{#splat}
33
+ def initialize(name, args, splat)
34
+ @name = name
35
+ @args = args
36
+ @splat = splat
37
+ super()
38
+
39
+ return unless %w(and or not).include?(name)
40
+ raise Sass::SyntaxError.new("Invalid function name \"#{name}\".")
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,52 @@
1
+ require 'sass/tree/node'
2
+
3
+ module Sass::Tree
4
+ # A dynamic node representing a Sass `@if` statement.
5
+ #
6
+ # {IfNode}s are a little odd, in that they also represent `@else` and `@else if`s.
7
+ # This is done as a linked list:
8
+ # each {IfNode} has a link (\{#else}) to the next {IfNode}.
9
+ #
10
+ # @see Sass::Tree
11
+ class IfNode < Node
12
+ # The conditional expression.
13
+ # If this is nil, this is an `@else` node, not an `@else if`.
14
+ #
15
+ # @return [Script::Expr]
16
+ attr_accessor :expr
17
+
18
+ # The next {IfNode} in the if-else list, or `nil`.
19
+ #
20
+ # @return [IfNode]
21
+ attr_accessor :else
22
+
23
+ # @param expr [Script::Expr] See \{#expr}
24
+ def initialize(expr)
25
+ @expr = expr
26
+ @last_else = self
27
+ super()
28
+ end
29
+
30
+ # Append an `@else` node to the end of the list.
31
+ #
32
+ # @param node [IfNode] The `@else` node to append
33
+ def add_else(node)
34
+ @last_else.else = node
35
+ @last_else = node
36
+ end
37
+
38
+ def _dump(f)
39
+ Marshal.dump([expr, self.else, children])
40
+ end
41
+
42
+ def self._load(data)
43
+ expr, else_, children = Marshal.load(data)
44
+ node = IfNode.new(expr)
45
+ node.else = else_
46
+ node.children = children
47
+ node.instance_variable_set('@last_else',
48
+ node.else ? node.else.instance_variable_get('@last_else') : node)
49
+ node
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,75 @@
1
+ module Sass
2
+ module Tree
3
+ # A static node that wraps the {Sass::Tree} for an `@import`ed file.
4
+ # It doesn't have a functional purpose other than to add the `@import`ed file
5
+ # to the backtrace if an error occurs.
6
+ class ImportNode < RootNode
7
+ # The name of the imported file as it appears in the Sass document.
8
+ #
9
+ # @return [String]
10
+ attr_reader :imported_filename
11
+
12
+ # Sets the imported file.
13
+ attr_writer :imported_file
14
+
15
+ # @param imported_filename [String] The name of the imported file
16
+ def initialize(imported_filename)
17
+ @imported_filename = imported_filename
18
+ super(nil)
19
+ end
20
+
21
+ def invisible?; to_s.empty?; end
22
+
23
+ # Returns the imported file.
24
+ #
25
+ # @return [Sass::Engine]
26
+ # @raise [Sass::SyntaxError] If no file could be found to import.
27
+ def imported_file
28
+ @imported_file ||= import
29
+ end
30
+
31
+ # Returns whether or not this import should emit a CSS @import declaration
32
+ #
33
+ # @return [Boolean] Whether or not this is a simple CSS @import declaration.
34
+ def css_import?
35
+ if @imported_filename =~ /\.css$/
36
+ @imported_filename
37
+ elsif imported_file.is_a?(String) && imported_file =~ /\.css$/
38
+ imported_file
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def import
45
+ paths = @options[:load_paths]
46
+
47
+ if @options[:importer]
48
+ f = @options[:importer].find_relative(
49
+ @imported_filename, @options[:filename], options_for_importer)
50
+ return f if f
51
+ end
52
+
53
+ paths.each do |p|
54
+ f = p.find(@imported_filename, options_for_importer)
55
+ return f if f
56
+ end
57
+
58
+ lines = ["File to import not found or unreadable: #{@imported_filename}."]
59
+
60
+ if paths.size == 1
61
+ lines << "Load path: #{paths.first}"
62
+ elsif !paths.empty?
63
+ lines << "Load paths:\n #{paths.join("\n ")}"
64
+ end
65
+ raise SyntaxError.new(lines.join("\n"))
66
+ rescue SyntaxError => e
67
+ raise SyntaxError.new(e.message, :line => line, :filename => @filename)
68
+ end
69
+
70
+ def options_for_importer
71
+ @options.merge(:_from_import_node => true)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,15 @@
1
+ module Sass::Tree
2
+ class KeyframeRuleNode < Node
3
+ # The text of the directive after any interpolated SassScript has been resolved.
4
+ # Since this is only a static node, this is the only value property.
5
+ #
6
+ # @return [String]
7
+ attr_accessor :resolved_value
8
+
9
+ # @param resolved_value [String] See \{#resolved_value}
10
+ def initialize(resolved_value)
11
+ @resolved_value = resolved_value
12
+ super()
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,48 @@
1
+ module Sass::Tree
2
+ # A static node representing a `@media` rule.
3
+ # `@media` rules behave differently from other directives
4
+ # in that when they're nested within rules,
5
+ # they bubble up to top-level.
6
+ #
7
+ # @see Sass::Tree
8
+ class MediaNode < DirectiveNode
9
+ # TODO: parse and cache the query immediately if it has no dynamic elements
10
+
11
+ # The media query for this rule, interspersed with {Sass::Script::Tree::Node}s
12
+ # representing `#{}`-interpolation. Any adjacent strings will be merged
13
+ # together.
14
+ #
15
+ # @return [Array<String, Sass::Script::Tree::Node>]
16
+ attr_accessor :query
17
+
18
+ # The media query for this rule, without any unresolved interpolation. It's
19
+ # only set once {Tree::Visitors::Perform} has been run.
20
+ #
21
+ # @return [Sass::Media::QueryList]
22
+ attr_accessor :resolved_query
23
+
24
+ # @param query [Array<String, Sass::Script::Tree::Node>] See \{#query}
25
+ def initialize(query)
26
+ @query = query
27
+ super('')
28
+ end
29
+
30
+ # @see DirectiveNode#value
31
+ def value; raise NotImplementedError; end
32
+
33
+ # @see DirectiveNode#name
34
+ def name; '@media'; end
35
+
36
+ # @see DirectiveNode#resolved_value
37
+ def resolved_value
38
+ @resolved_value ||= "@media #{resolved_query.to_css}"
39
+ end
40
+
41
+ # True when the directive has no visible children.
42
+ #
43
+ # @return [Boolean]
44
+ def invisible?
45
+ children.all? {|c| c.invisible?}
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,38 @@
1
+ module Sass
2
+ module Tree
3
+ # A dynamic node representing a mixin definition.
4
+ #
5
+ # @see Sass::Tree
6
+ class MixinDefNode < Node
7
+ # The mixin name.
8
+ # @return [String]
9
+ attr_reader :name
10
+
11
+ # The arguments for the mixin.
12
+ # Each element is a tuple containing the variable for argument
13
+ # and the parse tree for the default value of the argument.
14
+ #
15
+ # @return [Array<(Script::Tree::Node, Script::Tree::Node)>]
16
+ attr_accessor :args
17
+
18
+ # The splat argument for this mixin, if one exists.
19
+ #
20
+ # @return [Script::Tree::Node?]
21
+ attr_accessor :splat
22
+
23
+ # Whether the mixin uses `@content`. Set during the nesting check phase.
24
+ # @return [Boolean]
25
+ attr_accessor :has_content
26
+
27
+ # @param name [String] The mixin name
28
+ # @param args [Array<(Script::Tree::Node, Script::Tree::Node)>] See \{#args}
29
+ # @param splat [Script::Tree::Node] See \{#splat}
30
+ def initialize(name, args, splat)
31
+ @name = name
32
+ @args = args
33
+ @splat = splat
34
+ super()
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,52 @@
1
+ require 'sass/tree/node'
2
+
3
+ module Sass::Tree
4
+ # A static node representing a mixin include.
5
+ # When in a static tree, the sole purpose is to wrap exceptions
6
+ # to add the mixin to the backtrace.
7
+ #
8
+ # @see Sass::Tree
9
+ class MixinNode < Node
10
+ # The name of the mixin.
11
+ # @return [String]
12
+ attr_reader :name
13
+
14
+ # The arguments to the mixin.
15
+ # @return [Array<Script::Tree::Node>]
16
+ attr_accessor :args
17
+
18
+ # A hash from keyword argument names to values.
19
+ # @return [Sass::Util::NormalizedMap<Script::Tree::Node>]
20
+ attr_accessor :keywords
21
+
22
+ # The first splat argument for this mixin, if one exists.
23
+ #
24
+ # This could be a list of positional arguments, a map of keyword
25
+ # arguments, or an arglist containing both.
26
+ #
27
+ # @return [Node?]
28
+ attr_accessor :splat
29
+
30
+ # The second splat argument for this mixin, if one exists.
31
+ #
32
+ # If this exists, it's always a map of keyword arguments, and
33
+ # \{#splat} is always either a list or an arglist.
34
+ #
35
+ # @return [Node?]
36
+ attr_accessor :kwarg_splat
37
+
38
+ # @param name [String] The name of the mixin
39
+ # @param args [Array<Script::Tree::Node>] See \{#args}
40
+ # @param splat [Script::Tree::Node] See \{#splat}
41
+ # @param kwarg_splat [Script::Tree::Node] See \{#kwarg_splat}
42
+ # @param keywords [Sass::Util::NormalizedMap<Script::Tree::Node>] See \{#keywords}
43
+ def initialize(name, args, keywords, splat, kwarg_splat)
44
+ @name = name
45
+ @args = args
46
+ @keywords = keywords
47
+ @splat = splat
48
+ @kwarg_splat = kwarg_splat
49
+ super()
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,240 @@
1
+ module Sass
2
+ # A namespace for nodes in the Sass parse tree.
3
+ #
4
+ # The Sass parse tree has three states: dynamic, static Sass, and static CSS.
5
+ #
6
+ # When it's first parsed, a Sass document is in the dynamic state.
7
+ # It has nodes for mixin definitions and `@for` loops and so forth,
8
+ # in addition to nodes for CSS rules and properties.
9
+ # Nodes that only appear in this state are called **dynamic nodes**.
10
+ #
11
+ # {Tree::Visitors::Perform} creates a static Sass tree, which is
12
+ # different. It still has nodes for CSS rules and properties but it
13
+ # doesn't have any dynamic-generation-related nodes. The nodes in
14
+ # this state are in a similar structure to the Sass document: rules
15
+ # and properties are nested beneath one another, although the
16
+ # {Tree::RuleNode} selectors are already in their final state. Nodes
17
+ # that can be in this state or in the dynamic state are called
18
+ # **static nodes**; nodes that can only be in this state are called
19
+ # **solely static nodes**.
20
+ #
21
+ # {Tree::Visitors::Cssize} is then used to create a static CSS tree.
22
+ # This is like a static Sass tree,
23
+ # but the structure exactly mirrors that of the generated CSS.
24
+ # Rules and properties can't be nested beneath one another in this state.
25
+ #
26
+ # Finally, {Tree::Visitors::ToCss} can be called on a static CSS tree
27
+ # to get the actual CSS code as a string.
28
+ module Tree
29
+ # The abstract superclass of all parse-tree nodes.
30
+ class Node
31
+ include Enumerable
32
+
33
+ def self.inherited(base)
34
+ node_name = base.name.gsub(/.*::(.*?)Node$/, '\\1').downcase
35
+ base.instance_eval <<-METHODS
36
+ # @return [Symbol] The name that is used for this node when visiting.
37
+ def node_name
38
+ :#{node_name}
39
+ end
40
+
41
+ # @return [Symbol] The method that is used on the visitor to visit nodes of this type.
42
+ def visit_method
43
+ :visit_#{node_name}
44
+ end
45
+
46
+ # @return [Symbol] The method name that determines if the parent is invalid.
47
+ def invalid_child_method_name
48
+ :"invalid_#{node_name}_child?"
49
+ end
50
+
51
+ # @return [Symbol] The method name that determines if the node is an invalid parent.
52
+ def invalid_parent_method_name
53
+ :"invalid_#{node_name}_parent?"
54
+ end
55
+ METHODS
56
+ end
57
+
58
+ # The child nodes of this node.
59
+ #
60
+ # @return [Array<Tree::Node>]
61
+ attr_reader :children
62
+
63
+ # Whether or not this node has child nodes.
64
+ # This may be true even when \{#children} is empty,
65
+ # in which case this node has an empty block (e.g. `{}`).
66
+ #
67
+ # @return [Boolean]
68
+ attr_accessor :has_children
69
+
70
+ # The line of the document on which this node appeared.
71
+ #
72
+ # @return [Integer]
73
+ attr_accessor :line
74
+
75
+ # The source range in the document on which this node appeared.
76
+ #
77
+ # @return [Sass::Source::Range]
78
+ attr_accessor :source_range
79
+
80
+ # The name of the document on which this node appeared.
81
+ #
82
+ # @return [String]
83
+ attr_writer :filename
84
+
85
+ # The options hash for the node.
86
+ # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
87
+ #
88
+ # @return [{Symbol => Object}]
89
+ attr_reader :options
90
+
91
+ def initialize
92
+ @children = []
93
+ @filename = nil
94
+ @options = nil
95
+ end
96
+
97
+ # Sets the options hash for the node and all its children.
98
+ #
99
+ # @param options [{Symbol => Object}] The options
100
+ # @see #options
101
+ def options=(options)
102
+ Sass::Tree::Visitors::SetOptions.visit(self, options)
103
+ end
104
+
105
+ # @private
106
+ def children=(children)
107
+ self.has_children ||= !children.empty?
108
+ @children = children
109
+ end
110
+
111
+ # The name of the document on which this node appeared.
112
+ #
113
+ # @return [String]
114
+ def filename
115
+ @filename || (@options && @options[:filename])
116
+ end
117
+
118
+ # Appends a child to the node.
119
+ #
120
+ # @param child [Tree::Node, Array<Tree::Node>] The child node or nodes
121
+ # @raise [Sass::SyntaxError] if `child` is invalid
122
+ def <<(child)
123
+ return if child.nil?
124
+ if child.is_a?(Array)
125
+ child.each {|c| self << c}
126
+ else
127
+ self.has_children = true
128
+ @children << child
129
+ end
130
+ end
131
+
132
+ # Compares this node and another object (only other {Tree::Node}s will be equal).
133
+ # This does a structural comparison;
134
+ # if the contents of the nodes and all the child nodes are equivalent,
135
+ # then the nodes are as well.
136
+ #
137
+ # Only static nodes need to override this.
138
+ #
139
+ # @param other [Object] The object to compare with
140
+ # @return [Boolean] Whether or not this node and the other object
141
+ # are the same
142
+ # @see Sass::Tree
143
+ def ==(other)
144
+ self.class == other.class && other.children == children
145
+ end
146
+
147
+ # True if \{#to\_s} will return `nil`;
148
+ # that is, if the node shouldn't be rendered.
149
+ # Should only be called in a static tree.
150
+ #
151
+ # @return [Boolean]
152
+ def invisible?; false; end
153
+
154
+ # The output style. See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
155
+ #
156
+ # @return [Symbol]
157
+ def style
158
+ @options[:style]
159
+ end
160
+
161
+ # Computes the CSS corresponding to this static CSS tree.
162
+ #
163
+ # @return [String] The resulting CSS
164
+ # @see Sass::Tree
165
+ def css
166
+ Sass::Tree::Visitors::ToCss.new.visit(self)
167
+ end
168
+
169
+ # Computes the CSS corresponding to this static CSS tree, along with
170
+ # the respective source map.
171
+ #
172
+ # @return [(String, Sass::Source::Map)] The resulting CSS and the source map
173
+ # @see Sass::Tree
174
+ def css_with_sourcemap
175
+ visitor = Sass::Tree::Visitors::ToCss.new(:build_source_mapping)
176
+ result = visitor.visit(self)
177
+ return result, visitor.source_mapping
178
+ end
179
+
180
+ # Returns a representation of the node for debugging purposes.
181
+ #
182
+ # @return [String]
183
+ def inspect
184
+ return self.class.to_s unless has_children
185
+ "(#{self.class} #{children.map {|c| c.inspect}.join(' ')})"
186
+ end
187
+
188
+ # Iterates through each node in the tree rooted at this node
189
+ # in a pre-order walk.
190
+ #
191
+ # @yield node
192
+ # @yieldparam node [Node] a node in the tree
193
+ def each
194
+ yield self
195
+ children.each {|c| c.each {|n| yield n}}
196
+ end
197
+
198
+ # Converts a node to Sass code that will generate it.
199
+ #
200
+ # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
201
+ # @return [String] The Sass code corresponding to the node
202
+ def to_sass(options = {})
203
+ Sass::Tree::Visitors::Convert.visit(self, options, :sass)
204
+ end
205
+
206
+ # Converts a node to SCSS code that will generate it.
207
+ #
208
+ # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
209
+ # @return [String] The Sass code corresponding to the node
210
+ def to_scss(options = {})
211
+ Sass::Tree::Visitors::Convert.visit(self, options, :scss)
212
+ end
213
+
214
+ # Return a deep clone of this node.
215
+ # The child nodes are cloned, but options are not.
216
+ #
217
+ # @return [Node]
218
+ def deep_copy
219
+ Sass::Tree::Visitors::DeepCopy.visit(self)
220
+ end
221
+
222
+ # Whether or not this node bubbles up through RuleNodes.
223
+ #
224
+ # @return [Boolean]
225
+ def bubbles?
226
+ false
227
+ end
228
+
229
+ protected
230
+
231
+ # @see Sass::Shared.balance
232
+ # @raise [Sass::SyntaxError] if the brackets aren't balanced
233
+ def balance(*args)
234
+ res = Sass::Shared.balance(*args)
235
+ return res if res
236
+ raise Sass::SyntaxError.new("Unbalanced brackets.", :line => line)
237
+ end
238
+ end
239
+ end
240
+ end