sassc 2.1.0.pre1-x86-linux

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +11 -0
  5. data/CHANGELOG.md +66 -0
  6. data/CODE_OF_CONDUCT.md +10 -0
  7. data/Gemfile +2 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +68 -0
  10. data/Rakefile +30 -0
  11. data/lib/sassc.rb +57 -0
  12. data/lib/sassc/dependency.rb +17 -0
  13. data/lib/sassc/engine.rb +139 -0
  14. data/lib/sassc/error.rb +37 -0
  15. data/lib/sassc/functions_handler.rb +75 -0
  16. data/lib/sassc/import_handler.rb +50 -0
  17. data/lib/sassc/importer.rb +31 -0
  18. data/lib/sassc/native.rb +70 -0
  19. data/lib/sassc/native/lib_c.rb +21 -0
  20. data/lib/sassc/native/native_context_api.rb +147 -0
  21. data/lib/sassc/native/native_functions_api.rb +164 -0
  22. data/lib/sassc/native/sass2scss_api.rb +10 -0
  23. data/lib/sassc/native/sass_input_style.rb +13 -0
  24. data/lib/sassc/native/sass_output_style.rb +12 -0
  25. data/lib/sassc/native/sass_value.rb +97 -0
  26. data/lib/sassc/native/string_list.rb +10 -0
  27. data/lib/sassc/sass_2_scss.rb +9 -0
  28. data/lib/sassc/script.rb +19 -0
  29. data/lib/sassc/script/functions.rb +8 -0
  30. data/lib/sassc/script/value.rb +137 -0
  31. data/lib/sassc/script/value/bool.rb +32 -0
  32. data/lib/sassc/script/value/color.rb +95 -0
  33. data/lib/sassc/script/value/list.rb +136 -0
  34. data/lib/sassc/script/value/map.rb +69 -0
  35. data/lib/sassc/script/value/number.rb +389 -0
  36. data/lib/sassc/script/value/string.rb +96 -0
  37. data/lib/sassc/script/value_conversion.rb +69 -0
  38. data/lib/sassc/script/value_conversion/base.rb +13 -0
  39. data/lib/sassc/script/value_conversion/bool.rb +13 -0
  40. data/lib/sassc/script/value_conversion/color.rb +18 -0
  41. data/lib/sassc/script/value_conversion/list.rb +25 -0
  42. data/lib/sassc/script/value_conversion/map.rb +21 -0
  43. data/lib/sassc/script/value_conversion/number.rb +13 -0
  44. data/lib/sassc/script/value_conversion/string.rb +17 -0
  45. data/lib/sassc/util.rb +231 -0
  46. data/lib/sassc/util/normalized_map.rb +117 -0
  47. data/lib/sassc/version.rb +5 -0
  48. data/sassc.gemspec +57 -0
  49. data/test/custom_importer_test.rb +127 -0
  50. data/test/engine_test.rb +314 -0
  51. data/test/error_test.rb +29 -0
  52. data/test/fixtures/paths.scss +10 -0
  53. data/test/functions_test.rb +303 -0
  54. data/test/native_test.rb +213 -0
  55. data/test/output_style_test.rb +107 -0
  56. data/test/sass_2_scss_test.rb +14 -0
  57. data/test/test_helper.rb +45 -0
  58. metadata +242 -0
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+
5
+ # A hash that normalizes its string keys while still allowing you to get back
6
+ # to the original keys that were stored. If several different values normalize
7
+ # to the same value, whichever is stored last wins.
8
+
9
+ class SassC::Util::NormalizedMap
10
+
11
+ # Create a normalized map
12
+ def initialize(map = nil)
13
+ @key_strings = {}
14
+ @map = {}
15
+ map.each {|key, value| self[key] = value} if map
16
+ end
17
+
18
+ # Specifies how to transform the key.
19
+ # This can be overridden to create other normalization behaviors.
20
+ def normalize(key)
21
+ key.tr("-", "_")
22
+ end
23
+
24
+ # Returns the version of `key` as it was stored before
25
+ # normalization. If `key` isn't in the map, returns it as it was
26
+ # passed in.
27
+ # @return [String]
28
+ def denormalize(key)
29
+ @key_strings[normalize(key)] || key
30
+ end
31
+
32
+ # @private
33
+ def []=(k, v)
34
+ normalized = normalize(k)
35
+ @map[normalized] = v
36
+ @key_strings[normalized] = k
37
+ v
38
+ end
39
+
40
+ # @private
41
+ def [](k)
42
+ @map[normalize(k)]
43
+ end
44
+
45
+ # @private
46
+ def has_key?(k)
47
+ @map.has_key?(normalize(k))
48
+ end
49
+
50
+ # @private
51
+ def delete(k)
52
+ normalized = normalize(k)
53
+ @key_strings.delete(normalized)
54
+ @map.delete(normalized)
55
+ end
56
+
57
+ # @return [Hash] Hash with the keys as they were stored (before normalization).
58
+ def as_stored
59
+ SassC::Util.map_keys(@map) {|k| @key_strings[k]}
60
+ end
61
+
62
+ def empty?
63
+ @map.empty?
64
+ end
65
+
66
+ def values
67
+ @map.values
68
+ end
69
+
70
+ def keys
71
+ @map.keys
72
+ end
73
+
74
+ def each
75
+ @map.each {|k, v| yield(k, v)}
76
+ end
77
+
78
+ def size
79
+ @map.size
80
+ end
81
+
82
+ def to_hash
83
+ @map.dup
84
+ end
85
+
86
+ def to_a
87
+ @map.to_a
88
+ end
89
+
90
+ def map
91
+ @map.map {|k, v| yield(k, v)}
92
+ end
93
+
94
+ def dup
95
+ d = super
96
+ d.send(:instance_variable_set, "@map", @map.dup)
97
+ d
98
+ end
99
+
100
+ def sort_by
101
+ @map.sort_by {|k, v| yield k, v}
102
+ end
103
+
104
+ def update(map)
105
+ map = map.as_stored if map.is_a?(NormalizedMap)
106
+ map.each {|k, v| self[k] = v}
107
+ end
108
+
109
+ def method_missing(method, *args, &block)
110
+ @map.send(method, *args, &block)
111
+ end
112
+
113
+ def respond_to_missing?(method, include_private = false)
114
+ @map.respond_to?(method, include_private)
115
+ end
116
+
117
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SassC
4
+ VERSION = "2.1.0.pre1"
5
+ end
data/sassc.gemspec ADDED
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "sassc/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+
9
+ spec.name = "sassc"
10
+ spec.version = SassC::VERSION
11
+ spec.authors = ["Ryan Boland"]
12
+ spec.email = ["ryan@tanookilabs.com"]
13
+ spec.summary = "Use libsass with Ruby!"
14
+ spec.description = "Use libsass with Ruby!"
15
+ spec.homepage = "https://github.com/sass/sassc-ruby"
16
+ spec.license = "MIT"
17
+
18
+ spec.files = `git ls-files -z`.split("\x0")
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+
22
+ spec.required_ruby_version = ">= 2.3.3"
23
+
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.platform = Gem::Platform::RUBY
27
+ spec.extensions = ["ext/extconf.rb"]
28
+
29
+ spec.add_development_dependency "minitest", "~> 5.5.1"
30
+ spec.add_development_dependency "minitest-around"
31
+ spec.add_development_dependency "test_construct"
32
+ spec.add_development_dependency "pry"
33
+ spec.add_development_dependency "bundler"
34
+ spec.add_development_dependency "rake"
35
+ spec.add_development_dependency "rake-compiler"
36
+ spec.add_development_dependency "rake-compiler-dock"
37
+
38
+ spec.add_dependency "ffi", "~> 1.9"
39
+
40
+ gem_dir = File.expand_path(File.dirname(__FILE__)) + "/"
41
+
42
+ libsass_dir = File.join(gem_dir, 'ext', 'libsass')
43
+ if !File.directory?(libsass_dir)
44
+ $stderr.puts "Error: ext/libsass not checked out. Please run:\n\n"\
45
+ " git submodule update --init"
46
+ exit 1
47
+ end
48
+
49
+ Dir.chdir(libsass_dir) do
50
+ submodule_relative_path = File.join('ext', 'libsass')
51
+ `git ls-files`.split($\).each do |filename|
52
+ next if filename =~ %r{(^("?test|docs|script)/)|\.md$|\.yml$}
53
+ spec.files << File.join(submodule_relative_path, filename)
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+
5
+ module SassC
6
+ class CustomImporterTest < MiniTest::Test
7
+ include TempFileTest
8
+
9
+ class CustomImporter < Importer
10
+ def imports(path, parent_path)
11
+ if path =~ /styles/
12
+ [
13
+ Import.new("#{path}1.scss", source: "$var1: #000;"),
14
+ Import.new("#{path}2.scss")
15
+ ]
16
+ else
17
+ Import.new(path)
18
+ end
19
+ end
20
+ end
21
+
22
+ class NoFilesImporter < Importer
23
+ def imports(path, parent_path)
24
+ []
25
+ end
26
+ end
27
+
28
+ class OptionsImporter < Importer
29
+ def imports(path, parent_path)
30
+ Import.new("name.scss", source: options[:custom_option_source])
31
+ end
32
+ end
33
+
34
+ class ParentImporter < Importer
35
+ def imports(path, parent_path)
36
+ Import.new("name.scss", source: ".#{parent_path} { color: red; }")
37
+ end
38
+ end
39
+
40
+ def test_custom_importer_works
41
+ temp_file("styles2.scss", ".hi { color: $var1; }")
42
+ temp_file("fonts.scss", ".font { color: $var1; }")
43
+
44
+ data = <<SCSS
45
+ @import "styles";
46
+ @import "fonts";
47
+ SCSS
48
+
49
+ engine = Engine.new(data, {
50
+ importer: CustomImporter
51
+ })
52
+
53
+ assert_equal <<CSS, engine.render
54
+ .hi {
55
+ color: #000; }
56
+
57
+ .font {
58
+ color: #000; }
59
+ CSS
60
+ end
61
+
62
+ def test_dependency_list
63
+ base = temp_dir("").to_s
64
+
65
+ temp_dir("fonts")
66
+ temp_dir("fonts/sub")
67
+ temp_file("fonts/sub/sub_fonts.scss", "$font: arial;")
68
+ temp_file("styles2.scss", ".hi { color: $var1; }")
69
+ temp_file "fonts/fonts.scss", <<SCSS
70
+ @import "sub/sub_fonts";
71
+ .font { font-familiy: $font; color: $var1; }
72
+ SCSS
73
+
74
+ data = <<SCSS
75
+ @import "styles";
76
+ @import "fonts";
77
+ SCSS
78
+
79
+ engine = Engine.new(data, {
80
+ importer: CustomImporter,
81
+ load_paths: ["fonts"]
82
+ })
83
+ engine.render
84
+
85
+ dependencies = engine.dependencies.map(&:filename).map { |f| f.gsub(base, "") }
86
+
87
+ assert_equal [
88
+ "/fonts/sub/sub_fonts.scss",
89
+ "/styles2.scss",
90
+ "fonts/fonts.scss",
91
+ "styles1.scss"
92
+ ], dependencies
93
+ end
94
+
95
+ def test_custom_importer_works_with_no_files
96
+ engine = Engine.new("@import 'fake.scss';", {
97
+ importer: NoFilesImporter
98
+ })
99
+
100
+ assert_equal "", engine.render
101
+ end
102
+
103
+ def test_custom_importer_can_access_sassc_options
104
+ engine = Engine.new("@import 'fake.scss';", {
105
+ importer: OptionsImporter,
106
+ custom_option_source: ".test { width: 30px; }"
107
+ })
108
+
109
+ assert_equal <<CSS, engine.render
110
+ .test {
111
+ width: 30px; }
112
+ CSS
113
+ end
114
+
115
+ def test_parent_path_is_accessible
116
+ engine = Engine.new("@import 'parent.scss';", {
117
+ importer: ParentImporter,
118
+ filename: "import-parent-filename.scss"
119
+ })
120
+
121
+ assert_equal <<CSS, engine.render
122
+ .import-parent-filename.scss {
123
+ color: red; }
124
+ CSS
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,314 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+
5
+ module SassC
6
+ class EngineTest < MiniTest::Test
7
+ include TempFileTest
8
+
9
+ def render(data)
10
+ Engine.new(data).render
11
+ end
12
+
13
+ def test_line_comments
14
+ template = <<-SCSS
15
+ .foo {
16
+ baz: bang; }
17
+ SCSS
18
+ expected_output = <<-CSS
19
+ /* line 1, stdin */
20
+ .foo {
21
+ baz: bang; }
22
+ CSS
23
+ output = Engine.new(template, line_comments: true).render
24
+ assert_equal expected_output, output
25
+ end
26
+
27
+ def test_one_line_comments
28
+ assert_equal <<CSS, render(<<SCSS)
29
+ .foo {
30
+ baz: bang; }
31
+ CSS
32
+ .foo {// bar: baz;}
33
+ baz: bang; //}
34
+ }
35
+ SCSS
36
+ assert_equal <<CSS, render(<<SCSS)
37
+ .foo bar[val="//"] {
38
+ baz: bang; }
39
+ CSS
40
+ .foo bar[val="//"] {
41
+ baz: bang; //}
42
+ }
43
+ SCSS
44
+ end
45
+
46
+ def test_variables
47
+ assert_equal <<CSS, render(<<SCSS)
48
+ blat {
49
+ a: foo; }
50
+ CSS
51
+ $var: foo;
52
+
53
+ blat {a: $var}
54
+ SCSS
55
+
56
+ assert_equal <<CSS, render(<<SCSS)
57
+ foo {
58
+ a: 2;
59
+ b: 6; }
60
+ CSS
61
+ foo {
62
+ $var: 2;
63
+ $another-var: 4;
64
+ a: $var;
65
+ b: $var + $another-var;}
66
+ SCSS
67
+ end
68
+
69
+ def test_precision
70
+ template = <<-SCSS
71
+ $var: 1;
72
+ .foo {
73
+ baz: $var / 3; }
74
+ SCSS
75
+ expected_output = <<-CSS
76
+ .foo {
77
+ baz: 0.33333333; }
78
+ CSS
79
+ output = Engine.new(template, precision: 8).render
80
+ assert_equal expected_output, output
81
+ end
82
+
83
+ def test_precision_not_specified
84
+ template = <<-SCSS
85
+ $var: 1;
86
+ .foo {
87
+ baz: $var / 3; }
88
+ SCSS
89
+ expected_output = <<-CSS
90
+ .foo {
91
+ baz: 0.3333333333; }
92
+ CSS
93
+ output = Engine.new(template).render
94
+ assert_equal expected_output, output
95
+ end
96
+
97
+ def test_dependency_filenames_are_reported
98
+ base = temp_dir("").to_s
99
+
100
+ temp_file("not_included.scss", "$size: 30px;")
101
+ temp_file("import_parent.scss", "$size: 30px;")
102
+ temp_file("import.scss", "@import 'import_parent'; $size: 30px;")
103
+ temp_file("styles.scss", "@import 'import.scss'; .hi { width: $size; }")
104
+
105
+ engine = Engine.new(File.read("styles.scss"))
106
+ engine.render
107
+ deps = engine.dependencies
108
+
109
+ expected = ["/import.scss", "/import_parent.scss"]
110
+ assert_equal expected, deps.map { |dep| dep.options[:filename].gsub(base, "") }.sort
111
+ assert_equal expected, deps.map { |dep| dep.filename.gsub(base, "") }.sort
112
+ end
113
+
114
+ def test_no_dependencies
115
+ engine = Engine.new("$size: 30px;")
116
+ engine.render
117
+ deps = engine.dependencies
118
+ assert_equal [], deps
119
+ end
120
+
121
+ def test_not_rendered_error
122
+ engine = Engine.new("$size: 30px;")
123
+ assert_raises(NotRenderedError) { engine.dependencies }
124
+ end
125
+
126
+ def test_source_map
127
+ temp_dir('admin')
128
+
129
+ temp_file('admin/text-color.scss', <<SCSS)
130
+ p {
131
+ color: red;
132
+ }
133
+ SCSS
134
+ temp_file('style.scss', <<SCSS)
135
+ @import 'admin/text-color';
136
+
137
+ p {
138
+ padding: 20px;
139
+ }
140
+ SCSS
141
+ engine = Engine.new(File.read('style.scss'), {
142
+ source_map_file: "style.scss.map",
143
+ source_map_contents: true
144
+ })
145
+ engine.render
146
+
147
+ assert_equal <<MAP.strip, engine.source_map
148
+ {
149
+ \t"version": 3,
150
+ \t"file": "stdin.css",
151
+ \t"sources": [
152
+ \t\t"stdin",
153
+ \t\t"admin/text-color.scss"
154
+ \t],
155
+ \t"sourcesContent": [
156
+ \t\t"@import 'admin/text-color';\\n\\np {\\n padding: 20px;\\n}\\n",
157
+ \t\t"p {\\n color: red;\\n}\\n"
158
+ \t],
159
+ \t"names": [],
160
+ \t"mappings": "ACAA,AAAA,CAAC,CAAC;EACA,KAAK,EAAE,GAAG,GACX;;ADAD,AAAA,CAAC,CAAC;EACA,OAAO,EAAE,IAAI,GACd"
161
+ }
162
+ MAP
163
+ end
164
+
165
+ def test_no_source_map
166
+ engine = Engine.new("$size: 30px;")
167
+ engine.render
168
+ assert_raises(NotRenderedError) { engine.source_map }
169
+ end
170
+
171
+ def test_omit_source_map_url
172
+ temp_file('style.scss', <<SCSS)
173
+ p {
174
+ padding: 20px;
175
+ }
176
+ SCSS
177
+ engine = Engine.new(File.read('style.scss'), {
178
+ source_map_file: "style.scss.map",
179
+ source_map_contents: true,
180
+ omit_source_map_url: true
181
+ })
182
+ output = engine.render
183
+
184
+ refute_match /sourceMappingURL/, output
185
+ end
186
+
187
+ def test_load_paths
188
+ temp_dir("included_1")
189
+ temp_dir("included_2")
190
+
191
+ temp_file("included_1/import_parent.scss", "$s: 30px;")
192
+ temp_file("included_2/import.scss", "@import 'import_parent'; $size: $s;")
193
+ temp_file("styles.scss", "@import 'import.scss'; .hi { width: $size; }")
194
+
195
+ assert_equal ".hi {\n width: 30px; }\n", Engine.new(
196
+ File.read("styles.scss"),
197
+ load_paths: [ "included_1", "included_2" ]
198
+ ).render
199
+ end
200
+
201
+ def test_global_load_paths
202
+ temp_dir("included_1")
203
+ temp_dir("included_2")
204
+
205
+ temp_file("included_1/import_parent.scss", "$s: 30px;")
206
+ temp_file("included_2/import.scss", "@import 'import_parent'; $size: $s;")
207
+ temp_file("styles.scss", "@import 'import.scss'; .hi { width: $size; }")
208
+
209
+ ::SassC.load_paths << "included_1"
210
+ ::SassC.load_paths << "included_2"
211
+
212
+ assert_equal ".hi {\n width: 30px; }\n", Engine.new(
213
+ File.read("styles.scss"),
214
+ ).render
215
+ ::SassC.load_paths.clear
216
+ end
217
+
218
+ def test_env_load_paths
219
+ expected_load_paths = [ "included_1", "included_2" ]
220
+ ::SassC.instance_eval { @load_paths = nil }
221
+ ENV['SASS_PATH'] = expected_load_paths.join(File::PATH_SEPARATOR)
222
+ assert_equal expected_load_paths, ::SassC.load_paths
223
+ ::SassC.load_paths.clear
224
+ end
225
+
226
+ def test_load_paths_not_configured
227
+ temp_file("included_1/import_parent.scss", "$s: 30px;")
228
+ temp_file("included_2/import.scss", "@import 'import_parent'; $size: $s;")
229
+ temp_file("styles.scss", "@import 'import.scss'; .hi { width: $size; }")
230
+
231
+ assert_raises(SyntaxError) do
232
+ Engine.new(File.read("styles.scss")).render
233
+ end
234
+ end
235
+
236
+ def test_sass_variation
237
+ sass = <<SASS
238
+ $size: 30px
239
+ .foo
240
+ width: $size
241
+ SASS
242
+
243
+ css = <<CSS
244
+ .foo {
245
+ width: 30px; }
246
+ CSS
247
+
248
+ assert_equal css, Engine.new(sass, syntax: :sass).render
249
+ assert_equal css, Engine.new(sass, syntax: "sass").render
250
+ assert_raises(SyntaxError) { Engine.new(sass).render }
251
+ end
252
+
253
+ def test_encoding_matches_input
254
+ input = String.new("$size: 30px;")
255
+ input.force_encoding("UTF-8")
256
+ output = Engine.new(input).render
257
+ assert_equal input.encoding, output.encoding
258
+ end
259
+
260
+ def test_inline_source_maps
261
+ template = <<-SCSS
262
+ .foo {
263
+ baz: bang; }
264
+ SCSS
265
+ expected_output = <<-CSS
266
+ /* line 1, stdin */
267
+ .foo {
268
+ baz: bang; }
269
+ CSS
270
+
271
+ output = Engine.new(template, {
272
+ source_map_file: ".",
273
+ source_map_embed: true,
274
+ source_map_contents: true
275
+ }).render
276
+
277
+ assert_match /sourceMappingURL/, output
278
+ assert_match /.foo/, output
279
+ end
280
+
281
+ def test_empty_template
282
+ output = Engine.new('').render
283
+ assert_equal '', output
284
+ end
285
+
286
+ def test_empty_template_returns_a_new_object
287
+ input = String.new
288
+ output = Engine.new(input).render
289
+ assert input.object_id != output.object_id, 'empty template must return a new object'
290
+ end
291
+
292
+ def test_empty_template_encoding_matches_input
293
+ input = String.new.force_encoding("ISO-8859-1")
294
+ output = Engine.new(input).render
295
+ assert_equal input.encoding, output.encoding
296
+ end
297
+
298
+ def test_handling_of_frozen_strings
299
+ output = Engine.new("body { background-color: red; }".freeze).render
300
+ assert_equal output, "body {\n background-color: red; }\n"
301
+ end
302
+
303
+ def test_import_plain_css
304
+ temp_file("test.css", ".something{color: red}")
305
+ expected_output = <<-CSS
306
+ .something {
307
+ color: red; }
308
+ CSS
309
+
310
+ output = Engine.new("@import 'test'").render
311
+ assert_equal expected_output, output
312
+ end
313
+ end
314
+ end