sassc 2.1.0.pre1-x86-linux

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 (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