sass 3.3.0.alpha.61 → 3.3.0.alpha.64
Sign up to get free protection for your applications and to get access to all the features.
- data/REVISION +1 -1
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/lib/sass/css.rb +2 -2
- data/lib/sass/engine.rb +26 -16
- data/lib/sass/exec.rb +3 -1
- data/lib/sass/importers/base.rb +12 -0
- data/lib/sass/script/lexer.rb +1 -1
- data/lib/sass/script/parser.rb +1 -1
- data/lib/sass/scss/parser.rb +7 -3
- data/lib/sass/source/map.rb +56 -14
- data/lib/sass/source/range.rb +9 -1
- data/lib/sass/tree/rule_node.rb +1 -1
- data/lib/sass/tree/visitors/perform.rb +8 -4
- data/lib/sass/util.rb +2 -2
- data/test/sass/importer_test.rb +142 -17
- data/test/sass/scss/css_test.rb +1 -1
- data/test/sass/scss/scss_test.rb +1 -1
- data/test/sass/source_map_test.rb +17 -4
- data/test/sass/util_test.rb +1 -1
- metadata +4 -4
data/REVISION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
62e36b0efe872f1be55de2a82b01d88f2b08d9b1
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.3.0.alpha.
|
1
|
+
3.3.0.alpha.64
|
data/VERSION_DATE
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
27 December 2012 22:38:55 GMT
|
data/lib/sass/css.rb
CHANGED
@@ -76,7 +76,7 @@ module Sass
|
|
76
76
|
#
|
77
77
|
# @return [Tree::Node] The root node of the parsed tree
|
78
78
|
def build_tree
|
79
|
-
root = Sass::SCSS::CssParser.new(@template, @options[:filename]).parse
|
79
|
+
root = Sass::SCSS::CssParser.new(@template, @options[:filename], nil).parse
|
80
80
|
parse_selectors root
|
81
81
|
expand_commas root
|
82
82
|
nest_seqs root
|
@@ -96,7 +96,7 @@ module Sass
|
|
96
96
|
root.children.each do |child|
|
97
97
|
next parse_selectors(child) if child.is_a?(Tree::DirectiveNode)
|
98
98
|
next unless child.is_a?(Tree::RuleNode)
|
99
|
-
parser = Sass::SCSS::CssParser.new(child.rule.first, child.filename, child.line)
|
99
|
+
parser = Sass::SCSS::CssParser.new(child.rule.first, child.filename, nil, child.line)
|
100
100
|
child.parsed_rules = parser.parse_selector
|
101
101
|
end
|
102
102
|
end
|
data/lib/sass/engine.rb
CHANGED
@@ -269,9 +269,10 @@ module Sass
|
|
269
269
|
# Render the template to CSS and return the source map.
|
270
270
|
#
|
271
271
|
# @param sourcemap_uri [String] The sourcemap URI to use in the
|
272
|
-
#
|
273
|
-
# the CSS file.
|
274
|
-
# @return [(String, Sass::Source::Map)] The rendered CSS and the associated
|
272
|
+
# `@sourceMappingURL` comment. If this is relative, it should be relative
|
273
|
+
# to the location of the CSS file.
|
274
|
+
# @return [(String, Sass::Source::Map)] The rendered CSS and the associated
|
275
|
+
# source map
|
275
276
|
# @raise [Sass::SyntaxError] if there's an error in the document
|
276
277
|
# @raise [Encoding::UndefinedConversionError] if the source encoding
|
277
278
|
# cannot be converted to UTF-8
|
@@ -369,7 +370,7 @@ module Sass
|
|
369
370
|
check_encoding!
|
370
371
|
|
371
372
|
if @options[:syntax] == :scss
|
372
|
-
root = Sass::SCSS::Parser.new(@template, @options[:filename]).parse
|
373
|
+
root = Sass::SCSS::Parser.new(@template, @options[:filename], @options[:importer]).parse
|
373
374
|
else
|
374
375
|
root = Tree::RootNode.new(@template)
|
375
376
|
append_children(root, tree(tabulate(@template)).first, true)
|
@@ -599,7 +600,7 @@ WARNING
|
|
599
600
|
property.name_source_range = Sass::Source::Range.new(
|
600
601
|
Sass::Source::Position.new(@line, to_parser_offset(name_start_offset)),
|
601
602
|
Sass::Source::Position.new(@line, to_parser_offset(name_end_offset)),
|
602
|
-
@options[:filename])
|
603
|
+
@options[:filename], @options[:importer])
|
603
604
|
property
|
604
605
|
end
|
605
606
|
when ?$
|
@@ -628,7 +629,9 @@ WARNING
|
|
628
629
|
hack_char = scanner.scan(/[:\*\.]|\#(?!\{)/)
|
629
630
|
offset = line.offset
|
630
631
|
offset += hack_char.length if hack_char
|
631
|
-
parser = Sass::SCSS::Parser.new(scanner,
|
632
|
+
parser = Sass::SCSS::Parser.new(scanner,
|
633
|
+
@options[:filename], @options[:importer],
|
634
|
+
@line, to_parser_offset(offset))
|
632
635
|
|
633
636
|
unless res = parser.parse_interp_ident
|
634
637
|
return Tree::RuleNode.new(parse_interp(line.text, line.offset))
|
@@ -637,7 +640,7 @@ WARNING
|
|
637
640
|
ident_range = Sass::Source::Range.new(
|
638
641
|
Sass::Source::Position.new(@line, to_parser_offset(offset)),
|
639
642
|
Sass::Source::Position.new(@line, parser.offset),
|
640
|
-
@options[:filename])
|
643
|
+
@options[:filename], @options[:importer])
|
641
644
|
offset = parser.offset - 1
|
642
645
|
res.unshift(hack_char) if hack_char
|
643
646
|
if comment = scanner.scan(Sass::SCSS::RX::COMMENT)
|
@@ -669,7 +672,7 @@ WARNING
|
|
669
672
|
node.value_source_range = Sass::Source::Range.new(
|
670
673
|
Sass::Source::Position.new(line.index, to_parser_offset(start_offset)),
|
671
674
|
Sass::Source::Position.new(line.index, to_parser_offset(end_offset)),
|
672
|
-
@options[:filename])
|
675
|
+
@options[:filename], @options[:importer])
|
673
676
|
if value.strip.empty? && line.children.empty?
|
674
677
|
raise SyntaxError.new(
|
675
678
|
"Invalid property: \"#{node.declaration}\" (no value)." +
|
@@ -779,7 +782,9 @@ WARNING
|
|
779
782
|
:line => @line + 1) unless line.children.empty?
|
780
783
|
Tree::CharsetNode.new(name)
|
781
784
|
when 'media'
|
782
|
-
parser = Sass::SCSS::Parser.new(value,
|
785
|
+
parser = Sass::SCSS::Parser.new(value,
|
786
|
+
@options[:filename], @options[:importer],
|
787
|
+
@line, to_parser_offset(@offset))
|
783
788
|
Tree::MediaNode.new(parser.parse_media_query_list.to_a)
|
784
789
|
else
|
785
790
|
Tree::DirectiveNode.new(
|
@@ -874,7 +879,9 @@ WARNING
|
|
874
879
|
script_parser = Sass::Script::Parser.new(scanner, @line, to_parser_offset(offset), @options)
|
875
880
|
str = script_parser.parse_string
|
876
881
|
|
877
|
-
media_parser = Sass::SCSS::Parser.new(scanner,
|
882
|
+
media_parser = Sass::SCSS::Parser.new(scanner,
|
883
|
+
@options[:filename], @options[:importer],
|
884
|
+
@line, str.source_range.end_pos.offset)
|
878
885
|
if media = media_parser.parse_media_query_list
|
879
886
|
end_pos = Sass::Source::Position.new(@line, media_parser.offset + 1)
|
880
887
|
node = Tree::CssImportNode.new(str, media.to_a)
|
@@ -883,7 +890,9 @@ WARNING
|
|
883
890
|
node = Tree::CssImportNode.new(str)
|
884
891
|
end
|
885
892
|
|
886
|
-
node.source_range = Sass::Source::Range.new(
|
893
|
+
node.source_range = Sass::Source::Range.new(
|
894
|
+
str.source_range.start_pos, end_pos,
|
895
|
+
@options[:filename], @options[:importer])
|
887
896
|
return node
|
888
897
|
end
|
889
898
|
|
@@ -894,7 +903,7 @@ WARNING
|
|
894
903
|
node.source_range = Sass::Source::Range.new(
|
895
904
|
Sass::Source::Position.new(@line, start_parser_offset),
|
896
905
|
Sass::Source::Position.new(@line, start_parser_offset + scanned.length),
|
897
|
-
@options[:filename])
|
906
|
+
@options[:filename], @options[:importer])
|
898
907
|
return node
|
899
908
|
end
|
900
909
|
|
@@ -904,25 +913,26 @@ WARNING
|
|
904
913
|
scanned = scanner.scan(/\s*/)
|
905
914
|
if !scanner.match?(/[,;]|$/)
|
906
915
|
offset += scanned.length if scanned
|
907
|
-
media_parser = Sass::SCSS::Parser.new(scanner,
|
916
|
+
media_parser = Sass::SCSS::Parser.new(scanner,
|
917
|
+
@options[:filename], @options[:importer], @line, offset)
|
908
918
|
media = media_parser.parse_media_query_list
|
909
919
|
node = Tree::CssImportNode.new(str || uri, media.to_a)
|
910
920
|
node.source_range = Sass::Source::Range.new(
|
911
921
|
Sass::Source::Position.new(@line, to_parser_offset(start_offset)),
|
912
922
|
Sass::Source::Position.new(@line, media_parser.offset),
|
913
|
-
@options[:filename])
|
923
|
+
@options[:filename], @options[:importer])
|
914
924
|
elsif val =~ /^(https?:)?\/\//
|
915
925
|
node = Tree::CssImportNode.new("url(#{val})")
|
916
926
|
node.source_range = Sass::Source::Range.new(
|
917
927
|
Sass::Source::Position.new(@line, to_parser_offset(start_offset)),
|
918
928
|
Sass::Source::Position.new(@line, to_parser_offset(offset)),
|
919
|
-
@options[:filename])
|
929
|
+
@options[:filename], @options[:importer])
|
920
930
|
else
|
921
931
|
node = Tree::ImportNode.new(val)
|
922
932
|
node.source_range = Sass::Source::Range.new(
|
923
933
|
Sass::Source::Position.new(@line, to_parser_offset(start_offset)),
|
924
934
|
Sass::Source::Position.new(@line, to_parser_offset(offset)),
|
925
|
-
@options[:filename])
|
935
|
+
@options[:filename], @options[:importer])
|
926
936
|
end
|
927
937
|
node
|
928
938
|
end
|
data/lib/sass/exec.rb
CHANGED
@@ -342,7 +342,9 @@ END
|
|
342
342
|
relative_path_from(Pathname.new(@options[:output_filename]).dirname)
|
343
343
|
rendered, mapping = engine.render_with_sourcemap(relative_sourcemap_path.to_s)
|
344
344
|
output.write(rendered)
|
345
|
-
sourcemap.puts(mapping.to_json(
|
345
|
+
sourcemap.puts(mapping.to_json(
|
346
|
+
:css_path => @options[:output_filename],
|
347
|
+
:sourcemap_path => @options[:sourcemap_filename]))
|
346
348
|
else
|
347
349
|
output.write(engine.render)
|
348
350
|
end
|
data/lib/sass/importers/base.rb
CHANGED
@@ -122,6 +122,18 @@ module Sass
|
|
122
122
|
Sass::Util.abstract(self)
|
123
123
|
end
|
124
124
|
|
125
|
+
# Get the publicly-visible URL for an imported file. This URL is used by
|
126
|
+
# source maps to link to the source stylesheet. This may return `nil` to
|
127
|
+
# indicate that no public URL is available; however, this will cause
|
128
|
+
# sourcemap generation to fail if any CSS is generated from files imported
|
129
|
+
# from this importer.
|
130
|
+
#
|
131
|
+
# @param uri [String] A URI known to be valid for this importer.
|
132
|
+
# @return [String?] The publicly-visible URL for this file.
|
133
|
+
def public_url(uri)
|
134
|
+
nil
|
135
|
+
end
|
136
|
+
|
125
137
|
# A string representation of the importer.
|
126
138
|
# Should be overridden by subclasses.
|
127
139
|
#
|
data/lib/sass/script/lexer.rb
CHANGED
@@ -361,7 +361,7 @@ MESSAGE
|
|
361
361
|
end
|
362
362
|
|
363
363
|
def range(start_pos, end_pos = source_position)
|
364
|
-
Sass::Source::Range.new(start_pos, end_pos, @options[:filename])
|
364
|
+
Sass::Source::Range.new(start_pos, end_pos, @options[:filename], @options[:importer])
|
365
365
|
end
|
366
366
|
|
367
367
|
def source_position
|
data/lib/sass/script/parser.rb
CHANGED
@@ -259,7 +259,7 @@ RUBY
|
|
259
259
|
end
|
260
260
|
|
261
261
|
def range(start_pos, end_pos=source_position)
|
262
|
-
Sass::Source::Range.new(start_pos, end_pos, @options[:filename])
|
262
|
+
Sass::Source::Range.new(start_pos, end_pos, @options[:filename], @options[:importer])
|
263
263
|
end
|
264
264
|
|
265
265
|
# @private
|
data/lib/sass/scss/parser.rb
CHANGED
@@ -12,15 +12,19 @@ module Sass
|
|
12
12
|
# @param str [String, StringScanner] The source document to parse.
|
13
13
|
# Note that `Parser` *won't* raise a nice error message if this isn't properly parsed;
|
14
14
|
# for that, you should use the higher-level {Sass::Engine} or {Sass::CSS}.
|
15
|
-
# @param filename [String] The name of the file being parsed. Used for
|
15
|
+
# @param filename [String] The name of the file being parsed. Used for
|
16
|
+
# warnings and source maps.
|
17
|
+
# @param importer [Sass::Importers::Base] The importer used to import the
|
18
|
+
# file being parsed. Used for source maps.
|
16
19
|
# @param line [Fixnum] The 1-based line on which the source string appeared,
|
17
20
|
# if it's part of another document.
|
18
21
|
# @param offset [Fixnum] The 1-based character (not byte) offset in the line on
|
19
22
|
# which the source string starts. Used for error reporting and sourcemap
|
20
23
|
# building.
|
21
|
-
def initialize(str, filename, line = 1, offset = 1)
|
24
|
+
def initialize(str, filename, importer, line = 1, offset = 1)
|
22
25
|
@template = str
|
23
26
|
@filename = filename
|
27
|
+
@importer = importer
|
24
28
|
@line = line
|
25
29
|
@offset = offset
|
26
30
|
@strs = []
|
@@ -69,7 +73,7 @@ module Sass
|
|
69
73
|
end
|
70
74
|
|
71
75
|
def range(start_pos, end_pos=source_position)
|
72
|
-
Sass::Source::Range.new(start_pos, end_pos, @filename)
|
76
|
+
Sass::Source::Range.new(start_pos, end_pos, @filename, @importer)
|
73
77
|
end
|
74
78
|
|
75
79
|
def init_scanner!
|
data/lib/sass/source/map.rb
CHANGED
@@ -64,16 +64,49 @@ module Sass::Source
|
|
64
64
|
|
65
65
|
# Returns the standard JSON representation of the source map.
|
66
66
|
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
67
|
+
# If the `:css_uri` option isn't specified, the `:css_path` and
|
68
|
+
# `:sourcemap_path` options must both be specified. Any options may also be
|
69
|
+
# specified alongside the `:css_uri` option. If `:css_uri` isn't specified,
|
70
|
+
# it will be inferred from `:css_path` and `:sourcemap_path` using the
|
71
|
+
# assumption that the local file system has the same layout as the server.
|
72
|
+
#
|
73
|
+
# If any source stylesheets use the default filesystem importer, sourcemap
|
74
|
+
# generation will fail unless the ``:sourcemap_path` option is specified.
|
75
|
+
# The layout of the local file system is assumed to be the same as the
|
76
|
+
# layout of the server for the purposes of linking to source stylesheets
|
77
|
+
# that use the filesystem importer.
|
78
|
+
#
|
79
|
+
# Regardless of which options are passed to this method, sourcemap
|
80
|
+
# generation will fail if the source stylesheet contains any import that
|
81
|
+
# uses a non-default importer that doesn't implement
|
82
|
+
# \{Sass::Importers::Base#public\_url\}.
|
83
|
+
#
|
84
|
+
# @option options :css_uri [String]
|
85
|
+
# The publicly-visible URI of the CSS output file.
|
86
|
+
# @option options :css_path [String]
|
87
|
+
# The local path of the CSS output file.
|
88
|
+
# @option options :sourcemap_path [String]
|
89
|
+
# The (eventual) local path of the sourcemap file.
|
70
90
|
# @return [String] The JSON string.
|
71
|
-
|
91
|
+
# @raise [ArgumentError] If neither `:css_uri` nor `:css_path` are
|
92
|
+
# specified.
|
93
|
+
# @raise [Sass::SyntaxError] If the public URL of a stylesheet cannot be
|
94
|
+
# determined.
|
95
|
+
def to_json(options)
|
96
|
+
css_uri, css_path, sourcemap_path = [:css_uri, :css_path, :sourcemap_path].map {|o| options[o]}
|
97
|
+
unless css_uri || (css_path && sourcemap_path)
|
98
|
+
raise ArgumentError.new("Sass::Source::Map#to_json requires either " +
|
99
|
+
"the :css_uri option or both the :css_path and :soucemap_path options.")
|
100
|
+
end
|
101
|
+
css_path &&= Pathname.pwd.join(Pathname.new(css_path)).cleanpath
|
102
|
+
sourcemap_path &&= Pathname.pwd.join(Pathname.new(sourcemap_path)).cleanpath
|
103
|
+
css_uri ||= css_path.relative_path_from(sourcemap_path.dirname).to_s
|
104
|
+
|
72
105
|
result = "{\n"
|
73
106
|
write_json_field(result, "version", "3", true)
|
74
107
|
|
75
|
-
|
76
|
-
|
108
|
+
source_uri_to_id = {}
|
109
|
+
id_to_source_uri = {}
|
77
110
|
next_source_id = 0
|
78
111
|
line_data = []
|
79
112
|
segment_data_for_line = []
|
@@ -85,17 +118,26 @@ module Sass::Source
|
|
85
118
|
previous_source_offset = 1
|
86
119
|
previous_source_id = 0
|
87
120
|
|
88
|
-
target_pathname = Pathname.pwd.join(Pathname.new(target_filename)).cleanpath
|
89
121
|
@data.each do |m|
|
90
|
-
|
91
|
-
|
92
|
-
|
122
|
+
file, importer = m.input.file, m.input.importer
|
123
|
+
unless source_uri = importer && importer.public_url(file)
|
124
|
+
if importer.is_a?(Sass::Importers::Filesystem) && sourcemap_path
|
125
|
+
file_path = Pathname.new(importer.root).join(file)
|
126
|
+
source_uri = file_path.relative_path_from(sourcemap_path.dirname).to_s
|
127
|
+
else
|
128
|
+
raise Sass::SyntaxError.new(<<ERR)
|
129
|
+
Error generating source map: couldn't determine public URL for "#{file}".
|
130
|
+
ERR
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
current_source_id = source_uri_to_id[source_uri]
|
93
135
|
unless current_source_id
|
94
136
|
current_source_id = next_source_id
|
95
137
|
next_source_id += 1
|
96
138
|
|
97
|
-
|
98
|
-
|
139
|
+
source_uri_to_id[source_uri] = current_source_id
|
140
|
+
id_to_source_uri[current_source_id] = source_uri
|
99
141
|
end
|
100
142
|
|
101
143
|
[
|
@@ -138,9 +180,9 @@ module Sass::Source
|
|
138
180
|
write_json_field(result, "mappings", line_data.join(";"))
|
139
181
|
|
140
182
|
source_names = []
|
141
|
-
(0...next_source_id).each {|id| source_names.push(
|
183
|
+
(0...next_source_id).each {|id| source_names.push(id_to_source_uri[id].to_s)}
|
142
184
|
write_json_field(result, "sources", source_names)
|
143
|
-
write_json_field(result, "file",
|
185
|
+
write_json_field(result, "file", css_uri)
|
144
186
|
|
145
187
|
result << "\n}"
|
146
188
|
result
|
data/lib/sass/source/range.rb
CHANGED
@@ -16,13 +16,21 @@ module Sass::Source
|
|
16
16
|
# @return [String]
|
17
17
|
attr_accessor :file
|
18
18
|
|
19
|
+
# The importer that imported the file in which this source range appears.
|
20
|
+
# This is nil for target ranges.
|
21
|
+
#
|
22
|
+
# @return [Sass::Importers::Base]
|
23
|
+
attr_accessor :importer
|
24
|
+
|
19
25
|
# @param start_pos [Sass::Source::Position] See \{#start_pos}
|
20
26
|
# @param end_pos [Sass::Source::Position] See \{#end_pos}
|
21
27
|
# @param file [String] See \{#file}
|
22
|
-
|
28
|
+
# @param importer [Sass::Importers::Base] See \{#importer}
|
29
|
+
def initialize(start_pos, end_pos, file, importer=nil)
|
23
30
|
@start_pos = start_pos
|
24
31
|
@end_pos = end_pos
|
25
32
|
@file = file
|
33
|
+
@importer = importer
|
26
34
|
end
|
27
35
|
|
28
36
|
# @return [String] A string representation of the source range.
|
data/lib/sass/tree/rule_node.rb
CHANGED
@@ -124,7 +124,7 @@ module Sass::Tree
|
|
124
124
|
if @rule.all? {|t| t.kind_of?(String)}
|
125
125
|
# We don't use real filename/line info because we don't have it yet.
|
126
126
|
# When we get it, we'll set it on the parsed rules if possible.
|
127
|
-
parser = Sass::SCSS::StaticParser.new(@rule.join.strip, '', 1)
|
127
|
+
parser = Sass::SCSS::StaticParser.new(@rule.join.strip, '', nil, 1)
|
128
128
|
@parsed_rules = parser.parse_selector rescue nil
|
129
129
|
end
|
130
130
|
end
|
@@ -164,7 +164,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
164
164
|
# Runs SassScript interpolation in the selector,
|
165
165
|
# and then parses the result into a {Sass::Selector::CommaSequence}.
|
166
166
|
def visit_extend(node)
|
167
|
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.selector),
|
167
|
+
parser = Sass::SCSS::StaticParser.new(run_interp(node.selector),
|
168
|
+
node.filename, node.options[:importer], node.line)
|
168
169
|
node.resolved_selector = parser.parse_selector
|
169
170
|
node
|
170
171
|
end
|
@@ -311,7 +312,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
311
312
|
def visit_rule(node)
|
312
313
|
rule = node.rule
|
313
314
|
rule = rule.map {|e| e.is_a?(String) && e != ' ' ? e.strip : e} if node.style == :compressed
|
314
|
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.rule),
|
315
|
+
parser = Sass::SCSS::StaticParser.new(run_interp(node.rule),
|
316
|
+
node.filename, node.options[:importer], node.line)
|
315
317
|
node.parsed_rules ||= parser.parse_selector
|
316
318
|
if node.options[:trace_selectors]
|
317
319
|
@stack.push(:filename => node.filename, :line => node.line)
|
@@ -363,7 +365,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
363
365
|
end
|
364
366
|
|
365
367
|
def visit_media(node)
|
366
|
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.query),
|
368
|
+
parser = Sass::SCSS::StaticParser.new(run_interp(node.query),
|
369
|
+
node.filename, node.options[:importer], node.line)
|
367
370
|
node.resolved_query ||= parser.parse_media_query_list
|
368
371
|
yield
|
369
372
|
end
|
@@ -377,7 +380,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
377
380
|
def visit_cssimport(node)
|
378
381
|
node.resolved_uri = run_interp([node.uri])
|
379
382
|
if node.query
|
380
|
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.query),
|
383
|
+
parser = Sass::SCSS::StaticParser.new(run_interp(node.query),
|
384
|
+
node.filename, node.options[:importer], node.line)
|
381
385
|
node.resolved_query ||= parser.parse_media_query_list
|
382
386
|
end
|
383
387
|
yield
|
data/lib/sass/util.rb
CHANGED
@@ -803,12 +803,12 @@ MSG
|
|
803
803
|
# @param s [String] The string to be escaped
|
804
804
|
# @return [String] The escaped string
|
805
805
|
def json_escape_string(s)
|
806
|
-
return s if s !~ /["
|
806
|
+
return s if s !~ /["\\\b\f\n\r\t]/
|
807
807
|
|
808
808
|
result = ""
|
809
809
|
s.split("").each do |c|
|
810
810
|
case c
|
811
|
-
when '"', "\\"
|
811
|
+
when '"', "\\"
|
812
812
|
result << "\\" << c
|
813
813
|
when "\n" then result << "\\n"
|
814
814
|
when "\t" then result << "\\t"
|
data/test/sass/importer_test.rb
CHANGED
@@ -8,29 +8,37 @@ class ImporterTest < Test::Unit::TestCase
|
|
8
8
|
|
9
9
|
class FruitImporter < Sass::Importers::Base
|
10
10
|
def find(name, context = nil)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
"blue"
|
20
|
-
end
|
21
|
-
contents = %Q{
|
22
|
-
$#{fruit}-color: #{color} !default;
|
23
|
-
@mixin #{fruit} {
|
24
|
-
color: $#{fruit}-color;
|
25
|
-
}
|
26
|
-
}
|
27
|
-
Sass::Engine.new(contents, :filename => name, :syntax => :scss, :importer => self)
|
11
|
+
return unless fruit = parse(name)
|
12
|
+
color = case fruit
|
13
|
+
when "apple"
|
14
|
+
"red"
|
15
|
+
when "orange"
|
16
|
+
"orange"
|
17
|
+
else
|
18
|
+
"blue"
|
28
19
|
end
|
20
|
+
contents = %Q{
|
21
|
+
$#{fruit}-color: #{color} !default;
|
22
|
+
@mixin #{fruit} {
|
23
|
+
color: $#{fruit}-color;
|
24
|
+
}
|
25
|
+
}
|
26
|
+
Sass::Engine.new(contents, :filename => name, :syntax => :scss, :importer => self)
|
29
27
|
end
|
30
28
|
|
31
29
|
def key(name, context)
|
32
30
|
[self.class.name, name]
|
33
31
|
end
|
32
|
+
|
33
|
+
def public_url(name)
|
34
|
+
"http://#{parse(name)}.example.com/style.scss"
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def parse(name)
|
40
|
+
name[%r{fruits/(\w+)(\.s[ac]ss)?}, 1]
|
41
|
+
end
|
34
42
|
end
|
35
43
|
|
36
44
|
# This class proves that you can override the extension scheme for importers
|
@@ -177,6 +185,123 @@ CSS
|
|
177
185
|
)
|
178
186
|
end
|
179
187
|
|
188
|
+
def test_source_map_with_only_css_uri_supports_public_url_imports
|
189
|
+
fruit_importer = FruitImporter.new
|
190
|
+
|
191
|
+
options = {
|
192
|
+
:filename => 'fruits/orange',
|
193
|
+
:importer => fruit_importer,
|
194
|
+
:syntax => :scss
|
195
|
+
}
|
196
|
+
|
197
|
+
engine = Sass::Engine.new(<<SCSS, options)
|
198
|
+
.orchard {
|
199
|
+
color: blue;
|
200
|
+
}
|
201
|
+
SCSS
|
202
|
+
|
203
|
+
rendered, sourcemap = engine.render_with_sourcemap('sourcemap_uri')
|
204
|
+
assert_equal <<JSON.strip, sourcemap.to_json(:css_uri => 'css_uri')
|
205
|
+
{
|
206
|
+
"version": "3",
|
207
|
+
"mappings": ";EACE,KAAK,EAAE,IAAI",
|
208
|
+
"sources": ["http://orange.example.com/style.scss"],
|
209
|
+
"file": "css_uri"
|
210
|
+
}
|
211
|
+
JSON
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_source_map_with_only_css_uri_doesnt_support_filesystem_importer
|
215
|
+
file_system_importer = Sass::Importers::Filesystem.new('.')
|
216
|
+
options = {
|
217
|
+
:filename => filename_for_test(:scss),
|
218
|
+
:importer => file_system_importer,
|
219
|
+
:syntax => :scss
|
220
|
+
}
|
221
|
+
|
222
|
+
engine = Sass::Engine.new(<<SCSS, options)
|
223
|
+
.foo {a: b}
|
224
|
+
SCSS
|
225
|
+
|
226
|
+
rendered, sourcemap = engine.render_with_sourcemap('http://1.example.com/style.map')
|
227
|
+
|
228
|
+
assert_raise_message(Sass::SyntaxError, <<MESSAGE) {sourcemap.to_json(:css_uri => 'css_uri')}
|
229
|
+
Error generating source map: couldn't determine public URL for "#{filename_for_test(:scss)}".
|
230
|
+
MESSAGE
|
231
|
+
end
|
232
|
+
|
233
|
+
def test_source_map_with_css_uri_and_css_path_doesnt_support_filesystem_importer
|
234
|
+
file_system_importer = Sass::Importers::Filesystem.new('.')
|
235
|
+
options = {
|
236
|
+
:filename => filename_for_test(:scss),
|
237
|
+
:importer => file_system_importer,
|
238
|
+
:syntax => :scss
|
239
|
+
}
|
240
|
+
|
241
|
+
engine = Sass::Engine.new(<<SCSS, options)
|
242
|
+
.foo {a: b}
|
243
|
+
SCSS
|
244
|
+
|
245
|
+
rendered, sourcemap = engine.render_with_sourcemap('http://1.example.com/style.map')
|
246
|
+
|
247
|
+
assert_raise_message(Sass::SyntaxError, <<MESSAGE) {sourcemap.to_json(:css_uri => 'css_uri', :css_path => 'css_path')}
|
248
|
+
Error generating source map: couldn't determine public URL for "#{filename_for_test(:scss)}".
|
249
|
+
MESSAGE
|
250
|
+
end
|
251
|
+
|
252
|
+
def test_source_map_with_css_uri_and_sourcemap_path_supports_filesystem_importer
|
253
|
+
file_system_importer = Sass::Importers::Filesystem.new('.')
|
254
|
+
options = {
|
255
|
+
:filename => 'sass/style.scss',
|
256
|
+
:importer => file_system_importer,
|
257
|
+
:syntax => :scss
|
258
|
+
}
|
259
|
+
|
260
|
+
engine = Sass::Engine.new(<<SCSS, options)
|
261
|
+
.foo {a: b}
|
262
|
+
SCSS
|
263
|
+
|
264
|
+
rendered, sourcemap = engine.render_with_sourcemap('http://1.example.com/style.map')
|
265
|
+
|
266
|
+
|
267
|
+
rendered, sourcemap = engine.render_with_sourcemap('http://map.example.com/map/style.map')
|
268
|
+
css_uri = 'css_uri'
|
269
|
+
sourcemap_path = 'map/style.map'
|
270
|
+
assert_equal <<JSON.strip, sourcemap.to_json(:css_uri => css_uri, :sourcemap_path => sourcemap_path)
|
271
|
+
{
|
272
|
+
"version": "3",
|
273
|
+
"mappings": ";EAAM,CAAC,EAAE,CAAC",
|
274
|
+
"sources": ["../sass/style.scss"],
|
275
|
+
"file": "css_uri"
|
276
|
+
}
|
277
|
+
JSON
|
278
|
+
end
|
279
|
+
|
280
|
+
def test_source_map_with_css_path_and_sourcemap_path_supports_file_system_importer
|
281
|
+
file_system_importer = Sass::Importers::Filesystem.new('.')
|
282
|
+
options = {
|
283
|
+
:filename => 'sass/style.scss',
|
284
|
+
:importer => file_system_importer,
|
285
|
+
:syntax => :scss
|
286
|
+
}
|
287
|
+
|
288
|
+
engine = Sass::Engine.new(<<SCSS, options)
|
289
|
+
.foo {a: b}
|
290
|
+
SCSS
|
291
|
+
|
292
|
+
rendered, sourcemap = engine.render_with_sourcemap('http://map.example.com/map/style.map')
|
293
|
+
css_path = 'static/style.css'
|
294
|
+
sourcemap_path = 'map/style.map'
|
295
|
+
assert_equal <<JSON.strip, sourcemap.to_json(:css_path => css_path, :sourcemap_path => sourcemap_path)
|
296
|
+
{
|
297
|
+
"version": "3",
|
298
|
+
"mappings": ";EAAM,CAAC,EAAE,CAAC",
|
299
|
+
"sources": ["../sass/style.scss"],
|
300
|
+
"file": "../static/style.css"
|
301
|
+
}
|
302
|
+
JSON
|
303
|
+
end
|
304
|
+
|
180
305
|
def fixture_dir
|
181
306
|
File.join(File.dirname(__FILE__), "fixtures")
|
182
307
|
end
|
data/test/sass/scss/css_test.rb
CHANGED
@@ -1064,7 +1064,7 @@ SCSS
|
|
1064
1064
|
end
|
1065
1065
|
|
1066
1066
|
def render(scss, options = {})
|
1067
|
-
tree = Sass::SCSS::CssParser.new(scss, options[:filename]).parse
|
1067
|
+
tree = Sass::SCSS::CssParser.new(scss, options[:filename], nil).parse
|
1068
1068
|
tree.options = Sass::Engine::DEFAULT_OPTIONS.merge(options)
|
1069
1069
|
tree.render
|
1070
1070
|
end
|
data/test/sass/scss/scss_test.rb
CHANGED
@@ -4,6 +4,13 @@ require File.dirname(__FILE__) + '/../test_helper'
|
|
4
4
|
require File.dirname(__FILE__) + '/test_helper'
|
5
5
|
|
6
6
|
class SourcemapTest < Test::Unit::TestCase
|
7
|
+
def test_to_json_requires_args
|
8
|
+
rendered, sourcemap = render_with_sourcemap('')
|
9
|
+
assert_raise(ArgumentError) {sourcemap.to_json({})}
|
10
|
+
assert_raise(ArgumentError) {sourcemap.to_json({:css_path => 'foo'})}
|
11
|
+
assert_raise(ArgumentError) {sourcemap.to_json({:sourcemap_path => 'foo'})}
|
12
|
+
end
|
13
|
+
|
7
14
|
def test_simple_mapping_scss
|
8
15
|
assert_parses_with_sourcemap <<SCSS, <<CSS, <<JSON
|
9
16
|
a {
|
@@ -70,7 +77,7 @@ CSS
|
|
70
77
|
{
|
71
78
|
"version": "3",
|
72
79
|
"mappings": ";EACE,GAAG,EAAE,GAAG;;EAER,SAAS,EAAE,IAAI",
|
73
|
-
"sources": ["
|
80
|
+
"sources": ["../scss/style.scss"],
|
74
81
|
"file": "style.css"
|
75
82
|
}
|
76
83
|
JSON
|
@@ -94,7 +101,7 @@ CSS
|
|
94
101
|
{
|
95
102
|
"version": "3",
|
96
103
|
"mappings": ";EACE,GAAG,EAAE,GAAG;;EAEP,SAAS,EAAC,IAAI",
|
97
|
-
"sources": ["
|
104
|
+
"sources": ["../sass/style.sass"],
|
98
105
|
"file": "style.css"
|
99
106
|
}
|
100
107
|
JSON
|
@@ -725,7 +732,9 @@ CSS
|
|
725
732
|
assert(!closing || start_positions[name], "Closing annotation #{name} found before opening one.")
|
726
733
|
position = Sass::Source::Position.new(line, offset)
|
727
734
|
if closing
|
728
|
-
ranges[name] << Sass::Source::Range.new(
|
735
|
+
ranges[name] << Sass::Source::Range.new(
|
736
|
+
start_positions[name], position, file_name,
|
737
|
+
Sass::Importers::Filesystem.new('.'))
|
729
738
|
start_positions.delete name
|
730
739
|
else
|
731
740
|
assert(!start_positions[name], "Overlapping range annotation #{name} encountered on line #{line}")
|
@@ -795,8 +804,12 @@ MESSAGE
|
|
795
804
|
|
796
805
|
def assert_parses_with_sourcemap(source, css, sourcemap_json, options={})
|
797
806
|
rendered, sourcemap = render_with_sourcemap(source, options)
|
807
|
+
css_path = options[:output] || "test.css"
|
808
|
+
sourcemap_path = Sass::Util.sourcemap_name(css_path)
|
809
|
+
rendered_json = sourcemap.to_json(:css_path => css_path, :sourcemap_path => sourcemap_path)
|
810
|
+
|
798
811
|
assert_equal css.rstrip, rendered.rstrip
|
799
|
-
assert_equal sourcemap_json.rstrip,
|
812
|
+
assert_equal sourcemap_json.rstrip, rendered_json
|
800
813
|
end
|
801
814
|
|
802
815
|
def render_with_sourcemap(source, options={})
|
data/test/sass/util_test.rb
CHANGED
@@ -314,7 +314,7 @@ class UtilTest < Test::Unit::TestCase
|
|
314
314
|
assert_json_string "", ""
|
315
315
|
alphanum = (("0".."9").to_a).concat(("a".."z").to_a).concat(("A".."Z").to_a).join
|
316
316
|
assert_json_string alphanum, alphanum
|
317
|
-
assert_json_string "'\"
|
317
|
+
assert_json_string "'\"\\'", "'\\\"\\\\'"
|
318
318
|
assert_json_string "\b\f\n\r\t", "\\b\\f\\n\\r\\t"
|
319
319
|
end
|
320
320
|
|
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:
|
4
|
+
hash: 592302989
|
5
5
|
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 3
|
8
8
|
- 3
|
9
9
|
- 0
|
10
10
|
- alpha
|
11
|
-
-
|
12
|
-
version: 3.3.0.alpha.
|
11
|
+
- 64
|
12
|
+
version: 3.3.0.alpha.64
|
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: 2012-12-
|
22
|
+
date: 2012-12-27 00:00:00 -05:00
|
23
23
|
default_executable:
|
24
24
|
dependencies:
|
25
25
|
- !ruby/object:Gem::Dependency
|