sassc 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -8
- data/Rakefile +4 -14
- data/ext/Rakefile +3 -0
- data/ext/libsass/SECURITY.md +10 -0
- data/ext/libsass/cssize.cpp +4 -2
- data/ext/libsass/debugger.hpp +9 -7
- data/ext/libsass/eval.cpp +28 -5
- data/ext/libsass/functions.cpp +5 -5
- data/ext/libsass/inspect.cpp +2 -0
- data/ext/libsass/parser.cpp +43 -19
- data/ext/libsass/parser.hpp +1 -0
- data/ext/libsass/sass_functions.cpp +3 -0
- data/lib/sassc/functions_handler.rb +48 -13
- data/lib/sassc/native.rb +2 -1
- data/lib/sassc/native/native_functions_api.rb +6 -0
- data/lib/sassc/native/sass_value.rb +2 -2
- data/lib/sassc/script.rb +2 -3
- data/lib/sassc/script/string.rb +2 -2
- data/lib/sassc/version.rb +1 -1
- data/lib/tasks/libsass.rb +21 -0
- data/sassc.gemspec +3 -2
- data/test/functions_test.rb +89 -23
- data/test/native_test.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b426c4e47a4d6d041b25565605d6fe708847faaa
|
4
|
+
data.tar.gz: 36fd1baaa0ba7aaeb6b9100ee60aaeea8fa3d974
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54261aec807408c986d337a75e9051062ea0d5cb45affc510285a310cc5b0d95b6d1b810afe24f9e9d0f4762a6f2bbf3615ebbabc51a9bac5d26ac08a41fe0c4
|
7
|
+
data.tar.gz: 8264a970a2f76968e9eab211983c0f2c09c5f5a2ed634891146f239c25a5db7d8aa0bfa2a970e4136b1568dff97613e51e20578b112436cdffd10fa3e5db54c3
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ This gem combines the speed of `libsass`, the [Sass C implementation](https://gi
|
|
6
6
|
|
7
7
|
### libsass Version
|
8
8
|
|
9
|
-
[3.2.
|
9
|
+
[3.2.5](https://github.com/sass/libsass/releases/tag/3.2.5)
|
10
10
|
|
11
11
|
## Usage
|
12
12
|
|
@@ -23,14 +23,13 @@ Additionally, you can use `SassC::Sass2Scss` to convert Sass syntax to Scss synt
|
|
23
23
|
### Project Setup
|
24
24
|
|
25
25
|
1. Clone repo
|
26
|
-
|
27
|
-
|
28
|
-
4. Run the tests - `bundle exec rake test`
|
26
|
+
1. Install dependencies - `bundle install`
|
27
|
+
1. Run the tests - `bundle exec rake test`
|
29
28
|
|
30
29
|
### Code Changes
|
31
30
|
|
32
31
|
1. Fork it ( https://github.com/[my-github-username]/sassc/fork )
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
1. Create your feature branch (`git checkout -b my-new-feature`)
|
33
|
+
1. Commit your changes (`git commit -am 'Add some feature'`) - try to include tests
|
34
|
+
1. Push to the branch (`git push origin my-new-feature`)
|
35
|
+
1. Create a new Pull Request
|
data/Rakefile
CHANGED
@@ -4,22 +4,12 @@ rescue LoadError
|
|
4
4
|
puts 'Cannot load bundler/gem_tasks'
|
5
5
|
end
|
6
6
|
|
7
|
-
|
7
|
+
require 'tasks/libsass'
|
8
8
|
|
9
|
-
task
|
9
|
+
task default: :test
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
cd "ext/libsass"
|
14
|
-
sh 'make lib/libsass.so LDFLAGS="-Wall -O2"'
|
15
|
-
cd gem_dir
|
16
|
-
end
|
17
|
-
|
18
|
-
task test: :prepare do
|
11
|
+
desc "Run all tests"
|
12
|
+
task test: 'libsass:compile' do
|
19
13
|
$LOAD_PATH.unshift('lib', 'test')
|
20
14
|
Dir.glob('./test/**/*_test.rb') { |f| require f }
|
21
15
|
end
|
22
|
-
|
23
|
-
task :submodule do
|
24
|
-
sh "git submodule update --init"
|
25
|
-
end
|
data/ext/Rakefile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
Serious about security
|
2
|
+
======================
|
3
|
+
|
4
|
+
The LibSass team recognizes the important contributions the security research
|
5
|
+
community can make. We therefore encourage reporting security issues with the
|
6
|
+
code contained in this repository.
|
7
|
+
|
8
|
+
If you believe you have discovered a security vulnerability, please report it at
|
9
|
+
https://hackerone.com/libsass instead of GitHub.
|
10
|
+
|
data/ext/libsass/cssize.cpp
CHANGED
@@ -423,8 +423,10 @@ namespace Sass {
|
|
423
423
|
else
|
424
424
|
{
|
425
425
|
List* mq = merge_media_queries(static_cast<Media_Block*>(b->node()), static_cast<Media_Block*>(parent));
|
426
|
-
|
427
|
-
|
426
|
+
if (mq->length()) {
|
427
|
+
static_cast<Media_Block*>(b->node())->media_queries(mq);
|
428
|
+
ss = b->node();
|
429
|
+
}
|
428
430
|
}
|
429
431
|
|
430
432
|
if (!ss) continue;
|
data/ext/libsass/debugger.hpp
CHANGED
@@ -203,8 +203,8 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
|
|
203
203
|
cerr << " (" << pstate_source_position(node) << ")";
|
204
204
|
cerr << (block->is_interpolated() ? " [is_interpolated]": " -")
|
205
205
|
<< endl;
|
206
|
-
debug_ast(block->feature(), ind + "
|
207
|
-
debug_ast(block->value(), ind + "
|
206
|
+
debug_ast(block->feature(), ind + " feature) ");
|
207
|
+
debug_ast(block->value(), ind + " value) ");
|
208
208
|
|
209
209
|
} else if (dynamic_cast<Media_Query*>(node)) {
|
210
210
|
Media_Query* block = dynamic_cast<Media_Query*>(node);
|
@@ -452,10 +452,10 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
|
|
452
452
|
cerr << ind << "Map " << expression;
|
453
453
|
cerr << " (" << pstate_source_position(node) << ")";
|
454
454
|
cerr << " [Hashed]" << endl;
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
455
|
+
for (auto i : expression->elements()) {
|
456
|
+
debug_ast(i.first, ind + " key: ");
|
457
|
+
debug_ast(i.second, ind + " val: ");
|
458
|
+
}
|
459
459
|
} else if (dynamic_cast<List*>(node)) {
|
460
460
|
List* expression = dynamic_cast<List*>(node);
|
461
461
|
cerr << ind << "List " << expression;
|
@@ -499,7 +499,9 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
|
|
499
499
|
} else if (dynamic_cast<String_Constant*>(node)) {
|
500
500
|
String_Constant* expression = dynamic_cast<String_Constant*>(node);
|
501
501
|
cerr << ind << "String_Constant " << expression;
|
502
|
-
|
502
|
+
if (expression->concrete_type()) {
|
503
|
+
cerr << " " << expression->concrete_type();
|
504
|
+
}
|
503
505
|
cerr << " (" << pstate_source_position(node) << ")";
|
504
506
|
cerr << " [" << prettyprint(expression->value()) << "]";
|
505
507
|
if (expression->is_delayed()) cerr << " [delayed]";
|
data/ext/libsass/eval.cpp
CHANGED
@@ -393,16 +393,24 @@ namespace Sass {
|
|
393
393
|
Expression* Eval::operator()(Map* m)
|
394
394
|
{
|
395
395
|
if (m->is_expanded()) return m;
|
396
|
+
|
397
|
+
// make sure we're not starting with duplicate keys.
|
398
|
+
// the duplicate key state will have been set in the parser phase.
|
399
|
+
if (m->has_duplicate_key()) {
|
400
|
+
To_String to_string(&ctx);
|
401
|
+
error("Duplicate key \"" + m->get_duplicate_key()->perform(&to_string) + "\" in map " + m->perform(&to_string) + ".", m->pstate());
|
402
|
+
}
|
403
|
+
|
396
404
|
Map* mm = new (ctx.mem) Map(m->pstate(),
|
397
405
|
m->length());
|
398
406
|
for (auto key : m->keys()) {
|
399
|
-
*mm << std::make_pair(key->perform(this), m->at(key)->perform(this))
|
407
|
+
*mm << std::make_pair(key->perform(this), m->at(key)->perform(this));;
|
400
408
|
}
|
401
409
|
|
402
|
-
// check
|
410
|
+
// check the evaluated keys aren't duplicates.
|
403
411
|
if (mm->has_duplicate_key()) {
|
404
412
|
To_String to_string(&ctx);
|
405
|
-
error("Duplicate key \"" + mm->get_duplicate_key()->perform(&to_string) + "\" in map " +
|
413
|
+
error("Duplicate key \"" + mm->get_duplicate_key()->perform(&to_string) + "\" in map " + mm->perform(&to_string) + ".", mm->pstate());
|
406
414
|
}
|
407
415
|
|
408
416
|
mm->is_expanded(true);
|
@@ -790,6 +798,10 @@ namespace Sass {
|
|
790
798
|
zero);
|
791
799
|
break;
|
792
800
|
case Textual::HEX: {
|
801
|
+
if (t->value().substr(0, 1) != "#") {
|
802
|
+
result = new (ctx.mem) String_Constant(t->pstate(), t->value());
|
803
|
+
break;
|
804
|
+
}
|
793
805
|
string hext(t->value().substr(1)); // chop off the '#'
|
794
806
|
if (hext.length() == 6) {
|
795
807
|
string r(hext.substr(0,2));
|
@@ -923,6 +935,7 @@ namespace Sass {
|
|
923
935
|
} else if (str->quote_mark()) {
|
924
936
|
str->quote_mark('*');
|
925
937
|
}
|
938
|
+
str->is_delayed(true);
|
926
939
|
return str;
|
927
940
|
}
|
928
941
|
|
@@ -997,8 +1010,16 @@ namespace Sass {
|
|
997
1010
|
{
|
998
1011
|
Expression* feature = e->feature();
|
999
1012
|
feature = (feature ? feature->perform(this) : 0);
|
1013
|
+
if (feature && dynamic_cast<String_Quoted*>(feature)) {
|
1014
|
+
feature = new (ctx.mem) String_Constant(feature->pstate(),
|
1015
|
+
dynamic_cast<String_Quoted*>(feature)->value());
|
1016
|
+
}
|
1000
1017
|
Expression* value = e->value();
|
1001
1018
|
value = (value ? value->perform(this) : 0);
|
1019
|
+
if (value && dynamic_cast<String_Quoted*>(value)) {
|
1020
|
+
value = new (ctx.mem) String_Constant(value->pstate(),
|
1021
|
+
dynamic_cast<String_Quoted*>(value)->value());
|
1022
|
+
}
|
1002
1023
|
return new (ctx.mem) Media_Query_Expression(e->pstate(),
|
1003
1024
|
feature,
|
1004
1025
|
value,
|
@@ -1102,8 +1123,10 @@ namespace Sass {
|
|
1102
1123
|
} break;
|
1103
1124
|
|
1104
1125
|
case Expression::STRING: {
|
1105
|
-
|
1106
|
-
|
1126
|
+
string slhs = static_cast<String_Quoted*>(lhs)->value();
|
1127
|
+
string srhs = static_cast<String_Quoted*>(rhs)->value();
|
1128
|
+
return unquote(slhs) == unquote(srhs) &&
|
1129
|
+
(!(is_quoted(slhs) || is_quoted(srhs)) || slhs[0] == srhs[0]);
|
1107
1130
|
} break;
|
1108
1131
|
|
1109
1132
|
case Expression::LIST: {
|
data/ext/libsass/functions.cpp
CHANGED
@@ -221,9 +221,9 @@ namespace Sass {
|
|
221
221
|
double w2 = 1 - w1;
|
222
222
|
|
223
223
|
return new (ctx.mem) Color(pstate,
|
224
|
-
std::
|
225
|
-
std::
|
226
|
-
std::
|
224
|
+
std::round(w1*color1->r() + w2*color2->r()),
|
225
|
+
std::round(w1*color1->g() + w2*color2->g()),
|
226
|
+
std::round(w1*color1->b() + w2*color2->b()),
|
227
227
|
color1->a()*p + color2->a()*(1-p));
|
228
228
|
}
|
229
229
|
|
@@ -1051,8 +1051,8 @@ namespace Sass {
|
|
1051
1051
|
Number* least = 0;
|
1052
1052
|
for (size_t i = 0, L = arglist->length(); i < L; ++i) {
|
1053
1053
|
Number* xi = dynamic_cast<Number*>(arglist->value_at_index(i));
|
1054
|
+
if (!xi) error("`" + string(sig) + "` only takes numeric arguments", pstate);
|
1054
1055
|
if (least) {
|
1055
|
-
if (!xi) error("`" + string(sig) + "` only takes numeric arguments", pstate);
|
1056
1056
|
if (lt(xi, least, ctx)) least = xi;
|
1057
1057
|
} else least = xi;
|
1058
1058
|
}
|
@@ -1066,8 +1066,8 @@ namespace Sass {
|
|
1066
1066
|
Number* greatest = 0;
|
1067
1067
|
for (size_t i = 0, L = arglist->length(); i < L; ++i) {
|
1068
1068
|
Number* xi = dynamic_cast<Number*>(arglist->value_at_index(i));
|
1069
|
+
if (!xi) error("`" + string(sig) + "` only takes numeric arguments", pstate);
|
1069
1070
|
if (greatest) {
|
1070
|
-
if (!xi) error("`" + string(sig) + "` only takes numeric arguments", pstate);
|
1071
1071
|
if (lt(greatest, xi, ctx)) greatest = xi;
|
1072
1072
|
} else greatest = xi;
|
1073
1073
|
}
|
data/ext/libsass/inspect.cpp
CHANGED
data/ext/libsass/parser.cpp
CHANGED
@@ -85,6 +85,8 @@ namespace Sass {
|
|
85
85
|
}
|
86
86
|
}
|
87
87
|
|
88
|
+
bool semicolon = false;
|
89
|
+
string(error_message);
|
88
90
|
lex< optional_spaces >();
|
89
91
|
Selector_Lookahead lookahead_result;
|
90
92
|
while (position < end) {
|
@@ -97,14 +99,16 @@ namespace Sass {
|
|
97
99
|
(*root) << new (ctx.mem) Import_Stub(pstate, imp->files()[i]);
|
98
100
|
}
|
99
101
|
}
|
100
|
-
|
102
|
+
semicolon = true;
|
103
|
+
error_message = "top-level @import directive must be terminated by ';'";
|
101
104
|
}
|
102
105
|
else if (peek< kwd_mixin >() || peek< kwd_function >()) {
|
103
106
|
(*root) << parse_definition();
|
104
107
|
}
|
105
108
|
else if (peek< variable >()) {
|
106
109
|
(*root) << parse_assignment();
|
107
|
-
|
110
|
+
semicolon = true;
|
111
|
+
error_message = "top-level variable binding must be terminated by ';'";
|
108
112
|
}
|
109
113
|
/*else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
|
110
114
|
(*root) << parse_propset();
|
@@ -112,7 +116,10 @@ namespace Sass {
|
|
112
116
|
else if (peek< kwd_include >() /* || peek< exactly<'+'> >() */) {
|
113
117
|
Mixin_Call* mixin_call = parse_mixin_call();
|
114
118
|
(*root) << mixin_call;
|
115
|
-
if (!mixin_call->block()
|
119
|
+
if (!mixin_call->block()) {
|
120
|
+
semicolon = true;
|
121
|
+
error_message = "top-level @include directive must be terminated by ';'";
|
122
|
+
}
|
116
123
|
}
|
117
124
|
else if (peek< kwd_if_directive >()) {
|
118
125
|
(*root) << parse_if_directive();
|
@@ -137,15 +144,18 @@ namespace Sass {
|
|
137
144
|
}
|
138
145
|
else if (peek< kwd_warn >()) {
|
139
146
|
(*root) << parse_warning();
|
140
|
-
|
147
|
+
semicolon = true;
|
148
|
+
error_message = "top-level @warn directive must be terminated by ';'";
|
141
149
|
}
|
142
150
|
else if (peek< kwd_err >()) {
|
143
151
|
(*root) << parse_error();
|
144
|
-
|
152
|
+
semicolon = true;
|
153
|
+
error_message = "top-level @error directive must be terminated by ';'";
|
145
154
|
}
|
146
155
|
else if (peek< kwd_dbg >()) {
|
147
156
|
(*root) << parse_debug();
|
148
|
-
|
157
|
+
semicolon = true;
|
158
|
+
error_message = "top-level @debug directive must be terminated by ';'";
|
149
159
|
}
|
150
160
|
// ignore the @charset directive for now
|
151
161
|
else if (lex< exactly< charset_kwd > >()) {
|
@@ -155,7 +165,10 @@ namespace Sass {
|
|
155
165
|
else if (peek< at_keyword >()) {
|
156
166
|
At_Rule* at_rule = parse_at_rule();
|
157
167
|
(*root) << at_rule;
|
158
|
-
if (!at_rule->block()
|
168
|
+
if (!at_rule->block()){
|
169
|
+
semicolon = true;
|
170
|
+
error_message = "top-level directive must be terminated by ';'";
|
171
|
+
}
|
159
172
|
}
|
160
173
|
else if ((lookahead_result = lookahead_for_selector(position)).found) {
|
161
174
|
(*root) << parse_ruleset(lookahead_result);
|
@@ -168,6 +181,11 @@ namespace Sass {
|
|
168
181
|
if (position >= end) break;
|
169
182
|
error("invalid top-level expression", after_token);
|
170
183
|
}
|
184
|
+
if (semicolon) {
|
185
|
+
if (!lex< one_plus< exactly<';'> > >() && peek_css< optional_css_whitespace >() != end)
|
186
|
+
{ error(error_message, pstate); }
|
187
|
+
semicolon = false;
|
188
|
+
}
|
171
189
|
lex< optional_spaces >();
|
172
190
|
}
|
173
191
|
block_stack.pop_back();
|
@@ -636,7 +654,7 @@ namespace Sass {
|
|
636
654
|
seq->media_block(last_media_block);
|
637
655
|
seq->last_block(block_stack.back());
|
638
656
|
bool sawsomething = false;
|
639
|
-
if (
|
657
|
+
if (lex_css< exactly<'&'> >()) {
|
640
658
|
// check if we have a parent selector on the root level block
|
641
659
|
if (block_stack.back() && block_stack.back()->is_root()) {
|
642
660
|
//error("Base-level rules cannot contain the parent-selector-referencing character '&'.", pstate);
|
@@ -985,6 +1003,7 @@ namespace Sass {
|
|
985
1003
|
}
|
986
1004
|
else if (lex< sequence< optional< exactly<'*'> >, identifier > >()) {
|
987
1005
|
prop = new (ctx.mem) String_Quoted(pstate, lexed);
|
1006
|
+
prop->is_delayed(true);
|
988
1007
|
}
|
989
1008
|
else {
|
990
1009
|
error("invalid property name", pstate);
|
@@ -1086,12 +1105,6 @@ namespace Sass {
|
|
1086
1105
|
(*map) << make_pair(key, value);
|
1087
1106
|
}
|
1088
1107
|
|
1089
|
-
// Check was moved to eval step
|
1090
|
-
// if (map->has_duplicate_key()) {
|
1091
|
-
// To_String to_string(&ctx);
|
1092
|
-
// error("Duplicate key \"" + map->get_duplicate_key()->perform(&to_string) + "\" in map " + map->perform(&to_string) + ".", pstate);
|
1093
|
-
// }
|
1094
|
-
|
1095
1108
|
ParserState ps = map->pstate();
|
1096
1109
|
ps.offset = pstate - ps + pstate.offset;
|
1097
1110
|
map->pstate(ps);
|
@@ -1174,7 +1187,7 @@ namespace Sass {
|
|
1174
1187
|
exactly<ellipsis>,
|
1175
1188
|
default_flag,
|
1176
1189
|
global_flag
|
1177
|
-
> >(position))
|
1190
|
+
> >(position)) && peek_css< optional_css_whitespace >() != end
|
1178
1191
|
) {
|
1179
1192
|
(*space_list) << parse_disjunction();
|
1180
1193
|
}
|
@@ -1453,6 +1466,14 @@ namespace Sass {
|
|
1453
1466
|
return schema;
|
1454
1467
|
}
|
1455
1468
|
|
1469
|
+
String_Constant* Parser::parse_static_expression()
|
1470
|
+
{
|
1471
|
+
if (peek< sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number > >()) {
|
1472
|
+
return parse_static_value();
|
1473
|
+
}
|
1474
|
+
return 0;
|
1475
|
+
}
|
1476
|
+
|
1456
1477
|
String_Constant* Parser::parse_static_value()
|
1457
1478
|
{
|
1458
1479
|
lex< static_value >();
|
@@ -1461,7 +1482,7 @@ namespace Sass {
|
|
1461
1482
|
--position;
|
1462
1483
|
|
1463
1484
|
String_Constant* str_node = new (ctx.mem) String_Constant(pstate, str.time_wspace());
|
1464
|
-
|
1485
|
+
str_node->is_delayed(true);
|
1465
1486
|
return str_node;
|
1466
1487
|
}
|
1467
1488
|
|
@@ -1555,8 +1576,12 @@ namespace Sass {
|
|
1555
1576
|
}
|
1556
1577
|
else if (lex< interpolant >()) {
|
1557
1578
|
Token insides(Token(lexed.begin + 2, lexed.end - 1));
|
1558
|
-
Expression* interp_node
|
1559
|
-
|
1579
|
+
Expression* interp_node;
|
1580
|
+
Parser p = Parser::from_token(insides, ctx, pstate);
|
1581
|
+
if (!(interp_node = p.parse_static_expression())) {
|
1582
|
+
interp_node = p.parse_list();
|
1583
|
+
interp_node->is_interpolant(true);
|
1584
|
+
}
|
1560
1585
|
(*schema) << interp_node;
|
1561
1586
|
}
|
1562
1587
|
else if (lex< exactly<'%'> >()) {
|
@@ -2135,7 +2160,6 @@ namespace Sass {
|
|
2135
2160
|
(q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
|
2136
2161
|
(q = peek< percentage >(p)) ||
|
2137
2162
|
(q = peek< dimension >(p)) ||
|
2138
|
-
(q = peek< variable >(p)) ||
|
2139
2163
|
(q = peek< quoted_string >(p)) ||
|
2140
2164
|
(q = peek< exactly<'*'> >(p)) ||
|
2141
2165
|
(q = peek< exactly<'('> >(p)) ||
|
data/ext/libsass/parser.hpp
CHANGED
@@ -241,6 +241,7 @@ namespace Sass {
|
|
241
241
|
Function_Call_Schema* parse_function_call_schema();
|
242
242
|
String* parse_interpolated_chunk(Token, bool constant = false);
|
243
243
|
String* parse_string();
|
244
|
+
String_Constant* parse_static_expression();
|
244
245
|
String_Constant* parse_static_value();
|
245
246
|
String* parse_ie_property();
|
246
247
|
String* parse_ie_keyword_arg();
|
@@ -88,6 +88,9 @@ extern "C" {
|
|
88
88
|
return (Sass_Importer_List) calloc(length + 1, sizeof(Sass_Importer_Entry));
|
89
89
|
}
|
90
90
|
|
91
|
+
Sass_Importer_Entry ADDCALL sass_importer_get_list_entry(Sass_Importer_List list, size_t idx) { return list[idx]; }
|
92
|
+
void ADDCALL sass_importer_set_list_entry(Sass_Importer_List list, size_t idx, Sass_Importer_Entry cb) { list[idx] = cb; }
|
93
|
+
|
91
94
|
// Creator for sass custom importer return argument list
|
92
95
|
Sass_Import_List ADDCALL sass_make_import_list(size_t length)
|
93
96
|
{
|
@@ -10,25 +10,45 @@ module SassC
|
|
10
10
|
|
11
11
|
list = Native.make_function_list(Script.custom_functions.count)
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
functions = FunctionWrapper.extend(Script::Functions)
|
14
|
+
functions.options = @options
|
15
15
|
|
16
16
|
Script.custom_functions.each_with_index do |custom_function, i|
|
17
|
-
@callbacks[custom_function] = FFI::Function.new(:pointer, [:pointer, :pointer]) do |
|
18
|
-
|
17
|
+
@callbacks[custom_function] = FFI::Function.new(:pointer, [:pointer, :pointer]) do |native_argument_list, cookie|
|
18
|
+
native_argument_list_length = Native.list_get_length(native_argument_list)
|
19
|
+
custom_function_arguments = []
|
20
|
+
error_tag = nil
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
+
(0...native_argument_list_length).each do |i|
|
23
|
+
native_value = Native.list_get_value(native_argument_list, i)
|
22
24
|
|
23
|
-
|
25
|
+
case value_tag = Native.value_get_tag(native_value)
|
26
|
+
when :sass_null
|
27
|
+
# no-op
|
28
|
+
when :sass_string
|
29
|
+
native_string = Native.string_get_value(native_value)
|
30
|
+
argument = Script::String.new(Script::String.unquote(native_string), Script::String.type(native_string))
|
24
31
|
|
25
|
-
|
32
|
+
custom_function_arguments << argument
|
33
|
+
else
|
34
|
+
error_tag = error("Sass argument of type #{value_tag} unsupported")
|
35
|
+
break
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
next error_tag if error_tag
|
26
40
|
|
27
|
-
|
28
|
-
value =
|
29
|
-
|
30
|
-
|
31
|
-
|
41
|
+
begin
|
42
|
+
value = functions.send(custom_function, *custom_function_arguments)
|
43
|
+
|
44
|
+
if value
|
45
|
+
value = Script::String.new(Script::String.unquote(value.to_s), value.type)
|
46
|
+
value.to_native
|
47
|
+
else
|
48
|
+
Script::String.new("").to_native
|
49
|
+
end
|
50
|
+
rescue StandardError => exception
|
51
|
+
error(exception.message)
|
32
52
|
end
|
33
53
|
end
|
34
54
|
|
@@ -48,6 +68,21 @@ module SassC
|
|
48
68
|
|
49
69
|
private
|
50
70
|
|
71
|
+
def error(message)
|
72
|
+
value = Native::SassValue.new
|
73
|
+
value[:unknown] = Native::SassUnknown.new
|
74
|
+
|
75
|
+
error = Native::SassError.new
|
76
|
+
error[:tag] = :sass_error
|
77
|
+
|
78
|
+
Native.error_set_message(error, Native.native_string(message))
|
79
|
+
$stderr.puts "[SassC::FunctionsHandler] #{message}"
|
80
|
+
|
81
|
+
value[:unknown][:tag] = :sass_error
|
82
|
+
value[:error] = error
|
83
|
+
value.pointer
|
84
|
+
end
|
85
|
+
|
51
86
|
class FunctionWrapper
|
52
87
|
class << self
|
53
88
|
attr_accessor :options
|
data/lib/sassc/native.rb
CHANGED
@@ -19,6 +19,7 @@ module SassC
|
|
19
19
|
|
20
20
|
# ADDAPI enum Sass_Tag ADDCALL sass_value_get_tag (const union Sass_Value* v);
|
21
21
|
attach_function :sass_value_get_tag, [:sass_value_ptr], SassTag
|
22
|
+
attach_function :sass_value_is_null, [:sass_value_ptr], :bool
|
22
23
|
|
23
24
|
# ADDAPI const char* ADDCALL sass_string_get_value (const union Sass_Value* v);
|
24
25
|
attach_function :sass_string_get_value, [:sass_value_ptr], :string
|
@@ -28,6 +29,11 @@ module SassC
|
|
28
29
|
attach_function :sass_list_get_length, [:sass_value_ptr], :size_t
|
29
30
|
attach_function :sass_list_get_value, [:sass_value_ptr, :size_t], :sass_value_ptr
|
30
31
|
|
32
|
+
# ADDAPI char* ADDCALL sass_error_get_message (const union Sass_Value* v);
|
33
|
+
# ADDAPI void ADDCALL sass_error_set_message (union Sass_Value* v, char* msg);
|
34
|
+
attach_function :sass_error_get_message, [:sass_value_ptr], :string
|
35
|
+
attach_function :sass_error_set_message, [:sass_value_ptr, :pointer], :void
|
36
|
+
|
31
37
|
# Getters for custom function descriptors
|
32
38
|
# ADDAPI const char* ADDCALL sass_function_get_signature (Sass_C_Function_Callback fn);
|
33
39
|
# ADDAPI Sass_C_Function ADDCALL sass_function_get_function (Sass_C_Function_Callback fn);
|
@@ -71,12 +71,12 @@ module SassC
|
|
71
71
|
|
72
72
|
class SassError < FFI::Struct
|
73
73
|
layout :tag, SassTag,
|
74
|
-
:
|
74
|
+
:message, :string
|
75
75
|
end
|
76
76
|
|
77
77
|
class SassWarning < FFI::Struct
|
78
78
|
layout :tag, SassTag,
|
79
|
-
:
|
79
|
+
:message, :string
|
80
80
|
end
|
81
81
|
|
82
82
|
class SassValue # < FFI::Union
|
data/lib/sassc/script.rb
CHANGED
@@ -8,10 +8,9 @@ module SassC
|
|
8
8
|
|
9
9
|
def self.formatted_function_name(function_name)
|
10
10
|
params = Functions.instance_method(function_name).parameters
|
11
|
-
params = params.
|
12
|
-
.map(&:first)
|
13
|
-
.map { |p| "$#{p}" }
|
11
|
+
params = params.map { |param_type, name| "$#{name}#{': null' if param_type == :opt}" }
|
14
12
|
.join(", ")
|
13
|
+
|
15
14
|
"#{function_name}(#{params})"
|
16
15
|
end
|
17
16
|
end
|
data/lib/sassc/script/string.rb
CHANGED
@@ -66,12 +66,12 @@ module SassC
|
|
66
66
|
def self.unquote(contents)
|
67
67
|
s = contents.dup
|
68
68
|
|
69
|
-
case contents[0,1]
|
69
|
+
case contents[0, 1]
|
70
70
|
when "'", '"', '`'
|
71
71
|
s[0] = ''
|
72
72
|
end
|
73
73
|
|
74
|
-
case contents[-1,1]
|
74
|
+
case contents[-1, 1]
|
75
75
|
when "'", '"', '`'
|
76
76
|
s[-1] = ''
|
77
77
|
end
|
data/lib/sassc/version.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
namespace :libsass do
|
2
|
+
desc "Compile libsass"
|
3
|
+
task compile: "ext/libsass/lib/libsass.so"
|
4
|
+
|
5
|
+
file "ext/libsass/.git" do
|
6
|
+
sh "git submodule update --init"
|
7
|
+
end
|
8
|
+
|
9
|
+
file "ext/libsass/lib/libsass.so" => "ext/libsass/.git" do
|
10
|
+
libsass_path = ""
|
11
|
+
if Dir.pwd.end_with?('/ext')
|
12
|
+
libsass_path = "libsass"
|
13
|
+
else
|
14
|
+
libsass_path = "ext/libsass"
|
15
|
+
end
|
16
|
+
|
17
|
+
cd libsass_path do
|
18
|
+
sh 'make lib/libsass.so LDFLAGS="-Wall -O2"'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/sassc.gemspec
CHANGED
@@ -16,9 +16,10 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = ["lib", "ext"]
|
20
19
|
|
21
|
-
spec.
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.extensions = ["ext/Rakefile"]
|
22
23
|
|
23
24
|
spec.add_development_dependency "rake"
|
24
25
|
spec.add_development_dependency "minitest", "~> 5.5.1"
|
data/test/functions_test.rb
CHANGED
@@ -1,34 +1,16 @@
|
|
1
1
|
require_relative "test_helper"
|
2
|
+
require "stringio"
|
2
3
|
|
3
4
|
module SassC
|
4
5
|
class FunctionsTest < MiniTest::Test
|
5
6
|
include FixtureHelper
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
value
|
10
|
-
end
|
8
|
+
def setup
|
9
|
+
@real_stderr, $stderr = $stderr, StringIO.new
|
11
10
|
end
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
Script::String.new("/js/#{path.value}", :string)
|
16
|
-
end
|
17
|
-
|
18
|
-
def no_return_path(path)
|
19
|
-
nil
|
20
|
-
end
|
21
|
-
|
22
|
-
def sass_return_path(path)
|
23
|
-
return SassString.new("'#{path.value}'", :string)
|
24
|
-
end
|
25
|
-
|
26
|
-
module Compass
|
27
|
-
def stylesheet_path(path)
|
28
|
-
Script::String.new("/css/#{path.value}", :identifier)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
include Compass
|
12
|
+
def teardown
|
13
|
+
$stderr = @real_stderr
|
32
14
|
end
|
33
15
|
|
34
16
|
def test_functions_may_return_sass_string_type
|
@@ -70,5 +52,89 @@ div {
|
|
70
52
|
url: url(); }
|
71
53
|
EOS
|
72
54
|
end
|
55
|
+
|
56
|
+
def test_function_with_optional_arguments
|
57
|
+
engine = Engine.new("div {url: optional_arguments('first'); url: optional_arguments('second', 'qux')}")
|
58
|
+
assert_equal <<-EOS, engine.render
|
59
|
+
div {
|
60
|
+
url: "first/bar";
|
61
|
+
url: "second/qux"; }
|
62
|
+
EOS
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_function_with_unsupported_tag
|
66
|
+
engine = Engine.new("div {url: function_with_unsupported_tag(red);}")
|
67
|
+
|
68
|
+
exception = assert_raises(SassC::SyntaxError) do
|
69
|
+
engine.render
|
70
|
+
end
|
71
|
+
|
72
|
+
assert_equal "Error: error in C function function_with_unsupported_tag: Sass argument of type sass_color unsupported\n\n Backtrace:\n \tstdin:1, in function `function_with_unsupported_tag`\n \tstdin:1\n on line 1 of stdin\n>> div {url: function_with_unsupported_tag(red);}\n ----------^\n", exception.message
|
73
|
+
|
74
|
+
assert_equal "[SassC::FunctionsHandler] Sass argument of type sass_color unsupported", stderr_output
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_function_with_error
|
78
|
+
engine = Engine.new("div {url: function_that_raises_errors();}")
|
79
|
+
exception = assert_raises(SassC::SyntaxError) do
|
80
|
+
engine.render
|
81
|
+
end
|
82
|
+
|
83
|
+
assert_equal "Error: error in C function function_that_raises_errors: Intentional wrong thing happened somewhere inside the custom function
|
84
|
+
|
85
|
+
Backtrace:
|
86
|
+
\tstdin:1, in function `function_that_raises_errors`
|
87
|
+
\tstdin:1
|
88
|
+
on line 1 of stdin
|
89
|
+
>> div {url: function_that_raises_errors();}
|
90
|
+
----------^
|
91
|
+
", exception.message
|
92
|
+
|
93
|
+
assert_equal "[SassC::FunctionsHandler] Intentional wrong thing happened somewhere inside the custom function", stderr_output
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
SassString = Struct.new(:value, :type) do
|
99
|
+
def to_s
|
100
|
+
value
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
module Script::Functions
|
105
|
+
def javascript_path(path)
|
106
|
+
Script::String.new("/js/#{path.value}", :string)
|
107
|
+
end
|
108
|
+
|
109
|
+
def no_return_path(path)
|
110
|
+
nil
|
111
|
+
end
|
112
|
+
|
113
|
+
def sass_return_path(path)
|
114
|
+
return SassString.new("'#{path.value}'", :string)
|
115
|
+
end
|
116
|
+
|
117
|
+
def optional_arguments(path, optional = "bar")
|
118
|
+
return SassString.new("#{path}/#{optional}", :string)
|
119
|
+
end
|
120
|
+
|
121
|
+
def function_that_raises_errors()
|
122
|
+
raise StandardError, "Intentional wrong thing happened somewhere inside the custom function"
|
123
|
+
end
|
124
|
+
|
125
|
+
def function_with_unsupported_tag(color)
|
126
|
+
end
|
127
|
+
|
128
|
+
module Compass
|
129
|
+
def stylesheet_path(path)
|
130
|
+
Script::String.new("/css/#{path.value}", :identifier)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
include Compass
|
134
|
+
end
|
135
|
+
|
136
|
+
def stderr_output
|
137
|
+
$stderr.string.gsub("\u0000\n", '')
|
138
|
+
end
|
73
139
|
end
|
74
140
|
end
|
data/test/native_test.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sassc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Boland
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -113,7 +113,7 @@ email:
|
|
113
113
|
- bolandryanm@gmail.com
|
114
114
|
executables: []
|
115
115
|
extensions:
|
116
|
-
- Rakefile
|
116
|
+
- ext/Rakefile
|
117
117
|
extra_rdoc_files: []
|
118
118
|
files:
|
119
119
|
- ".gitignore"
|
@@ -123,6 +123,7 @@ files:
|
|
123
123
|
- LICENSE.txt
|
124
124
|
- README.md
|
125
125
|
- Rakefile
|
126
|
+
- ext/Rakefile
|
126
127
|
- ext/libsass/.editorconfig
|
127
128
|
- ext/libsass/.gitattributes
|
128
129
|
- ext/libsass/.gitignore
|
@@ -133,6 +134,7 @@ files:
|
|
133
134
|
- ext/libsass/Makefile
|
134
135
|
- ext/libsass/Makefile.am
|
135
136
|
- ext/libsass/Readme.md
|
137
|
+
- ext/libsass/SECURITY.md
|
136
138
|
- ext/libsass/appveyor.yml
|
137
139
|
- ext/libsass/ast.cpp
|
138
140
|
- ext/libsass/ast.hpp
|
@@ -288,6 +290,7 @@ files:
|
|
288
290
|
- lib/sassc/script/functions.rb
|
289
291
|
- lib/sassc/script/string.rb
|
290
292
|
- lib/sassc/version.rb
|
293
|
+
- lib/tasks/libsass.rb
|
291
294
|
- sassc.gemspec
|
292
295
|
- test/custom_importer_test.rb
|
293
296
|
- test/engine_test.rb
|
@@ -305,7 +308,6 @@ post_install_message:
|
|
305
308
|
rdoc_options: []
|
306
309
|
require_paths:
|
307
310
|
- lib
|
308
|
-
- ext
|
309
311
|
required_ruby_version: !ruby/object:Gem::Requirement
|
310
312
|
requirements:
|
311
313
|
- - ">="
|