sassc 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
- - ">="
|