diff --git a/package.json b/package.json index 466e3ab63..5c5972278 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "node-sass", "version": "4.9.0", - "libsass": "3.5.2", + "libsass": "3.5.3", "description": "Wrapper around libsass", "license": "MIT", "bugs": "https://github.com/sass/node-sass/issues", @@ -83,7 +83,7 @@ "object-merge": "^2.5.1", "read-yaml": "^1.0.0", "rimraf": "^2.5.2", - "sass-spec": "^3.5.1", + "sass-spec": "3.5.3", "unique-temp-dir": "^1.0.0" } } diff --git a/src/libsass/GNUmakefile.am b/src/libsass/GNUmakefile.am index 9e658a415..d197261e7 100644 --- a/src/libsass/GNUmakefile.am +++ b/src/libsass/GNUmakefile.am @@ -25,63 +25,49 @@ else AM_CXXFLAGS += -std=c++0x endif +TEST_EXTENSIONS = .rb + if ENABLE_TESTS -noinst_PROGRAMS = tester +SASS_SASSC_PATH ?= $(top_srcdir)/sassc +SASS_SPEC_PATH ?= $(top_srcdir)/sass-spec +noinst_PROGRAMS = tester tester_LDADD = src/libsass.la -tester_SOURCES = $(SASS_SASSC_PATH)/sassc.c -tester_VERSION ?= `cd "$(SASS_SASSC_PATH)" && ./version.sh` -tester_CFLAGS = $(AM_CFLAGS) -DSASSC_VERSION="\"$(tester_VERSION)\"" -tester_CXXFLAGS = $(AM_CXXFLAGS) -DSASSC_VERSION="\"$(tester_VERSION)\"" tester_LDFLAGS = $(AM_LDFLAGS) +nodist_tester_SOURCES = $(SASS_SASSC_PATH)/sassc.c +SASS_SASSC_VERSION ?= `cd "$(SASS_SASSC_PATH)" && ./version.sh` +tester_CFLAGS = $(AM_CFLAGS) -DSASSC_VERSION="\"$(SASS_SASSC_VERSION)\"" +tester_CXXFLAGS = $(AM_CXXFLAGS) -DSASSC_VERSION="\"$(SASS_SASSC_VERSION)\"" if ENABLE_COVERAGE nodist_EXTRA_tester_SOURCES = non-existent-file-to-force-CXX-linking.cxx endif -SASS_SASSC_PATH ?= $(top_srcdir)/sassc -SASS_SPEC_PATH ?= $(top_srcdir)/sass-spec - -TESTS = \ - $(SASS_SPEC_PATH)/spec/basic \ - $(SASS_SPEC_PATH)/spec/css \ - $(SASS_SPEC_PATH)/spec/extend-tests \ - $(SASS_SPEC_PATH)/spec/extends \ - $(SASS_SPEC_PATH)/spec/libsass \ - $(SASS_SPEC_PATH)/spec/libsass-closed-issues \ - $(SASS_SPEC_PATH)/spec/maps \ - $(SASS_SPEC_PATH)/spec/misc \ - $(SASS_SPEC_PATH)/spec/regressions \ - $(SASS_SPEC_PATH)/spec/scss \ - $(SASS_SPEC_PATH)/spec/scss-tests \ - $(SASS_SPEC_PATH)/spec/types +TESTS = $(SASS_SPEC_PATH)/sass-spec.rb +RB_LOG_COMPILER = ./script/tap-runner +AM_RB_LOG_FLAGS = $(RUBY) SASS_TEST_FLAGS = -V 3.5 --impl libsass -LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) ./script/tap-driver -AM_LOG_FLAGS = -c ./tester $(LOG_FLAGS) -if USE_TAP - AM_LOG_FLAGS += -t - SASS_TEST_FLAGS += -t | tapout - LOG_COMPILER = ./script/tap-runner $(RUBY) $(SASS_SPEC_PATH)/sass-spec.rb -else - LOG_COMPILER = $(RUBY) $(SASS_SPEC_PATH)/sass-spec.rb -endif +SASS_TEST_FLAGS += -r $(SASS_SPEC_PATH) +SASS_TEST_FLAGS += -c $(top_srcdir)/tester$(EXEEXT) +AM_TESTS_ENVIRONMENT = TEST_FLAGS='$(SASS_TEST_FLAGS)' SASS_TESTER = $(RUBY) $(SASS_SPEC_PATH)/sass-spec.rb -SASS_TESTER += -c $(top_srcdir)/tester$(EXEEXT) test: - $(SASS_TESTER) $(LOG_FLAGS) $(SASS_SPEC_PATH) $(SASS_TEST_FLAGS) + $(SASS_TESTER) $(SASS_TEST_FLAGS) test_build: - $(SASS_TESTER) $(LOG_FLAGS) $(SASS_SPEC_PATH) $(SASS_TEST_FLAGS) + $(SASS_TESTER) $(SASS_TEST_FLAGS) test_full: - $(SASS_TESTER) --run-todo $(LOG_FLAGS) $(SASS_SPEC_PATH) $(SASS_TEST_FLAGS) + $(SASS_TESTER) --run-todo $(SASS_TEST_FLAGS) test_probe: - $(SASS_TESTER) --probe-todo $(LOG_FLAGS) $(SASS_SPEC_PATH) $(SASS_TEST_FLAGS) + $(SASS_TESTER) --probe-todo $(SASS_TEST_FLAGS) + +.PHONY: test test_build test_full test_probe endif diff --git a/src/libsass/configure.ac b/src/libsass/configure.ac index bf05dbfaa..b5a943217 100644 --- a/src/libsass/configure.ac +++ b/src/libsass/configure.ac @@ -9,6 +9,7 @@ AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([src/config.h]) AC_CONFIG_FILES([include/sass/version.h]) AC_CONFIG_AUX_DIR([script]) + # These are flags passed to automake # Though they look like gcc flags! AM_INIT_AUTOMAKE([foreign parallel-tests -Wall]) @@ -93,21 +94,16 @@ the --with-sass-spec-dir= argument. ;; esac AC_SUBST(SASS_SPEC_PATH) - - # TODO: Remove this when automake requirements are 1.12+ - AC_MSG_CHECKING([whether we can use TAP mode]) - tmp=`$AWK '/TEST_LOG_DRIVER/' $srcdir/GNUmakefile.in` - if test "x$tmp" != "x"; then - use_tap=yes - else - use_tap=no - fi - AC_MSG_RESULT([$use_tap]) - +else + # we do not really need these paths for non test build + # but automake may error if we do not define them here + SASS_SPEC_PATH=sass-spec + SASS_SASSC_PATH=sassc + AC_SUBST(SASS_SPEC_PATH) + AC_SUBST(SASS_SASSC_PATH) fi AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "xyes") -AM_CONDITIONAL(USE_TAP, test "x$use_tap" = "xyes") AC_ARG_ENABLE([coverage], [AS_HELP_STRING([--enable-coverage], diff --git a/src/libsass/docs/implementations.md b/src/libsass/docs/implementations.md index 4814cdd8e..5239adcde 100644 --- a/src/libsass/docs/implementations.md +++ b/src/libsass/docs/implementations.md @@ -14,6 +14,17 @@ There are several implementations of `libsass` for a variety of languages. Here * [go_sass](https://github.com/suapapa/go_sass) * [go-sass](https://github.com/SamWhited/go-sass) +### Haskell +* [hLibsass](https://github.com/jakubfijalkowski/hlibsass) +* [hSass](https://github.com/jakubfijalkowski/hsass) + +### Java +* [libsass-maven-plugin](https://github.com/warmuuh/libsass-maven-plugin) +* [jsass](https://github.com/bit3/jsass) + +### JavaScript +* [sass.js](https://github.com/medialize/sass.js) + ### Lua * [lua-sass](https://github.com/craigbarnes/lua-sass) @@ -21,16 +32,14 @@ There are several implementations of `libsass` for a variety of languages. Here * [libsass-net](https://github.com/darrenkopp/libsass-net) * [NSass](https://github.com/TBAPI-0KA/NSass) * [Sass.Net](https://github.com/andyalm/Sass.Net) +* [SharpScss](https://github.com/xoofx/SharpScss) +* [LibSassHost](https://github.com/Taritsyn/LibSassHost) -### node.js -* [node-sass](https://github.com/andrew/node-sass) - -### Java -* [libsass-maven-plugin](https://github.com/warmuuh/libsass-maven-plugin) -* [jsass](https://github.com/bit3/jsass) +### Nim +* [nim-sass](https://github.com/zacharycarter/nim-sass) -### JavaScript -* [sass.js](https://github.com/medialize/sass.js) +### node.js +* [node-sass](https://github.com/sass/node-sass) ### Perl * [CSS::Sass](https://github.com/caldwell/CSS-Sass) diff --git a/src/libsass/docs/unicode.md b/src/libsass/docs/unicode.md index 3897dcd6c..a1eb5b1cf 100644 --- a/src/libsass/docs/unicode.md +++ b/src/libsass/docs/unicode.md @@ -1,27 +1,33 @@ -LibSass currently expects all input to be utf8 encoded (and outputs only utf8), if you actually have any unicode characters at all. We do not support conversion between encodings, even if you declare it with a `@charset` rule. The text below was originally posted as an [issue](https://github.com/sass/libsass/issues/381) on the LibSass tracker. +LibSass currently expects all input to be utf8 encoded (and outputs only utf8), if you actually have any unicode characters at all. We do not support conversion between encodings, even if you declare it with a `@charset` rule. The text below was originally posted as an [issue](https://github.com/sass/libsass/issues/381) on the LibSass tracker. Since then the status is outdated as LibSass now expects your +input to be utf8/ascii compatible, as it has been proven that reading ANSI (e.g. single byte encodings) as utf8 can lead to unexpected +behavior, which can in the worst case lead to buffer overruns/segfaults. Therefore LibSass now checks your input to be valid utf8 encoded! ### [Declaring character encodings in CSS](http://www.w3.org/International/questions/qa-css-charset.en) -This [explains](http://www.w3.org/International/questions/qa-css-charset.en) how the character encoding of a css file is determined. Since we are only dealing with local files, we never have a HTTP header. So the precedence should be 'charset' rule, byte-order mark (BOM) or auto-detection (finally falling back to system default/UTF-8). This may not sound too hard to implement, but what about import rules? The CSS specs do not forbid the mixing of different encodings! I solved that by converting all files to UTF-8 internally. On writing there is an option to tell the tool what encoding it should be (UTF-8 by default). One can also define if it should write a BOM or not and if it should add the charset declaration. +This [explains](http://www.w3.org/International/questions/qa-css-charset.en) how the character encoding of a css file is determined. Since we are only dealing with local files, we never have a HTTP header. So the precedence should be 'charset' rule, byte-order mark (BOM) or auto-detection (finally falling back to system default/UTF-8). This may not sound too hard to implement, but what about import rules? The CSS specs do not forbid the mixing of different encodings! I [solved that](https://github.com/mgreter/webmerge/) by converting all files to UTF-8 internally. On writing there is an option to tell the tool what encoding it should be (UTF-8 by default). One can also define if it should write a BOM or not and if it should add the charset declaration. -Since my tool is written in perl, I have a lot of utilities at hand to deal with different unicode charsets. I'm pretty sure that most OSS uses [libiconv](https://www.gnu.org/software/libiconv/) to convert between different encodings. But I have now idea how easy/hard this would be to integrate platform independent (it seems doable). +Since my [tool]((https://github.com/mgreter/webmerge/)) is written in perl, I have a lot of utilities at hand to deal with different unicode charsets. I'm pretty sure that most OSS uses [ICU](http://site.icu-project.org/) or [libiconv](https://www.gnu.org/software/libiconv/) to convert between different encodings. But I have now idea how easy/hard this would be to integrate platform independent (it seems doable). ANSII (single byte encoding) to utf8 is basically just a conversion table (for every supported code-page). ### Current status on LibSass unicode support -Currently LibSass seems to handle the common UTF-8 case pretty well. I believe it should correctly support all ASCII compatible encodings (like UTF-8 or Latin-1). If all includes use the same encoding, the output should be correct (in the same encoding). It should also handle unicode chars in [selectors, variable names and other identifiers](https://github.com/hcatlin/libsass/issues/244#issuecomment-34681227). This is true for all ASCII compatible encodings. So the main incompatible encodings (I'm aware of) are UTF-16/UTF-32 (which could be converted to UTF-8 with libiconv). +LibSass should/is fully UTF (and therefore plain ASCII) compatible. + +~~Currently LibSass seems to handle the common UTF-8 case pretty well. I believe it should correctly support all ASCII compatible encodings (like UTF-8 or Latin-1). If all includes use the same encoding, the output should be correct (in the same encoding). It should also handle unicode chars in [selectors, variable names and other identifiers](https://github.com/hcatlin/libsass/issues/244#issuecomment-34681227). This is true for all ASCII compatible encodings. So the main incompatible encodings (I'm aware of) are UTF-16/UTF-32 (which could be converted to UTF-8 with libiconv).~~ + +LibSass 3.5 will enforce that your input is either plain ASCII (chars below 127) or utf8. It does not handle anything else, but therefore ensures that the output is in a valid form. Before version 3.5 you were able to mix different code-pages, which yielded unexpected behavior. ### Current encoding auto detection -LibSass currently reads all kind of BOMs and will error out if it finds something it doesn't know how to handle! It seems that it throws away the optional UTF-8 BOM (if any is found). IMO it would be nice if users could configure that (also if a charset rule should be added to the output). +LibSass currently reads all kind of BOMs and will error out if it finds something it doesn't know how to handle! It seems that it throws away the optional UTF-8 BOM (if any is found). IMO it would be nice if users could configure that (also if a charset rule should be added to the output). But it does not really take any `@charset` into account, it always assumes your input is utf8 and ignores any given `@charset`! ### What is currently not supported -- Using non ASCII compatible encodings (like UTF-16) +- Using non ASCII compatible encodings (like UTF-16, Latin-1 etc.) - Using non ASCII characters in different encodings in different includes ### What is missing to support the above cases -- A way to convert between encodings (like libiconv) +- A way to convert between encodings (like libiconv/ICU) - Sniffing the charset inside the file (source is available) - Handling the conversion on import (and export) - Optional: Make output encoding configurable @@ -31,9 +37,9 @@ LibSass currently reads all kind of BOMs and will error out if it finds somethin I guess the current implementation should handle more than 99% of all real world use cases. A) Unicode characters are still seldomly seen (as they can be written escaped) -B) It will still work if it's UTF-8 or in any of the most common known western ISO codepages. -Although I'm not sure how this applies to asian and other "exotic" codepages! +~~B) It will still work if it's UTF-8 or in any of the most common known western ISO codepages. +Although I'm not sure how this applies to asian and other "exotic" codepages!~~ -I guess the biggest Problem is to have libiconv (or some other) library as a dependency. Since it contains a lot of rules for the conversions, I see it as the only way to handle this correctly. Once that is sorted out it should be pretty much straight forward to implement the missing pieces (in parser.cpp - Parser::parse should return encoding and add Parser::sniff_charset, then convert the source byte stream to UTF-8). +I guess the biggest Problem is to have libiconv/ICU (or some other) library as a dependency. Since it contains a lot of rules for the conversions, I see it as the only way to handle this correctly. Once that is sorted out it should be pretty much straight forward to implement the missing pieces (in parser.cpp - Parser::parse should return encoding and add Parser::sniff_charset, then convert the source byte stream to UTF-8). I hope the statements above all hold true. Unicode is really not the easiest topic to wrap your head around. But since I did all the above recently in Perl, I wanted to document it here. Feel free to extend or criticize. diff --git a/src/libsass/include/sass/context.h b/src/libsass/include/sass/context.h index 2f88d6888..29754b75f 100644 --- a/src/libsass/include/sass/context.h +++ b/src/libsass/include/sass/context.h @@ -149,6 +149,9 @@ ADDAPI size_t ADDCALL sass_compiler_get_callee_stack_size(struct Sass_Compiler* ADDAPI Sass_Callee_Entry ADDCALL sass_compiler_get_last_callee(struct Sass_Compiler* compiler); ADDAPI Sass_Callee_Entry ADDCALL sass_compiler_get_callee_entry(struct Sass_Compiler* compiler, size_t idx); +// Push function for import extenions +ADDAPI void ADDCALL sass_option_push_import_extension (struct Sass_Options* options, const char* ext); + // Push function for paths (no manipulation support for now) ADDAPI void ADDCALL sass_option_push_plugin_path (struct Sass_Options* options, const char* path); ADDAPI void ADDCALL sass_option_push_include_path (struct Sass_Options* options, const char* path); diff --git a/src/libsass/script/tap-runner b/src/libsass/script/tap-runner index 4adecafb9..56c13bfb4 100755 --- a/src/libsass/script/tap-runner +++ b/src/libsass/script/tap-runner @@ -1 +1 @@ -$@ | tapout tap \ No newline at end of file +$@ $TEST_FLAGS --tap --silent | tapout tap diff --git a/src/libsass/src/context.cpp b/src/libsass/src/context.cpp index dae2cbd75..b199412cd 100644 --- a/src/libsass/src/context.cpp +++ b/src/libsass/src/context.cpp @@ -96,6 +96,8 @@ namespace Sass { // include_paths.push_back(CWD); // collect more paths from different options + collect_extensions(c_options.extension); + collect_extensions(c_options.extensions); collect_include_paths(c_options.include_path); collect_include_paths(c_options.include_paths); collect_plugin_paths(c_options.plugin_path); @@ -166,6 +168,37 @@ namespace Sass { { } + void Context::collect_extensions(const char* exts_str) + { + if (exts_str) { + const char* beg = exts_str; + const char* end = Prelexer::find_first(beg); + + while (end) { + std::string ext(beg, end - beg); + if (!ext.empty()) { + extensions.push_back(ext); + } + beg = end + 1; + end = Prelexer::find_first(beg); + } + + std::string ext(beg); + if (!ext.empty()) { + extensions.push_back(ext); + } + } + } + + void Context::collect_extensions(string_list* paths_array) + { + while (paths_array) + { + collect_extensions(paths_array->string); + paths_array = paths_array->next; + } + } + void Context::collect_include_paths(const char* paths_str) { if (paths_str) { @@ -236,15 +269,20 @@ namespace Sass { // looks for alternatives and returns a list from one directory std::vector Context::find_includes(const Importer& import) { + // include configured extensions + std::vector exts(File::defaultExtensions); + if (extensions.size() > 0) { + exts.insert(exts.end(), extensions.begin(), extensions.end()); + } // make sure we resolve against an absolute path std::string base_path(rel2abs(import.base_path)); // first try to resolve the load path relative to the base path - std::vector vec(resolve_includes(base_path, import.imp_path)); + std::vector vec(resolve_includes(base_path, import.imp_path, exts)); // then search in every include path (but only if nothing found yet) for (size_t i = 0, S = include_paths.size(); vec.size() == 0 && i < S; ++i) { // call resolve_includes and individual base path and append all results - std::vector resolved(resolve_includes(include_paths[i], import.imp_path)); + std::vector resolved(resolve_includes(include_paths[i], import.imp_path, exts)); if (resolved.size()) vec.insert(vec.end(), resolved.begin(), resolved.end()); } // return vector @@ -365,6 +403,14 @@ namespace Sass { // process the resolved entry else if (resolved.size() == 1) { bool use_cache = c_importers.size() == 0; + if (resolved[0].deprecated) { + // emit deprecation warning when import resolves to a .css file + deprecated( + "Including .css files with @import is non-standard behaviour which will be removed in future versions of LibSass.", + "Use a custom importer to maintain this behaviour. Check your implementations documentation on how to create a custom importer.", + true, pstate + ); + } // use cache for the resource loading if (use_cache && sheets.count(resolved[0].abs_path)) return resolved[0]; // try to read the content of the resolved file entry diff --git a/src/libsass/src/context.hpp b/src/libsass/src/context.hpp index d3caba13e..f14e69f6d 100644 --- a/src/libsass/src/context.hpp +++ b/src/libsass/src/context.hpp @@ -67,6 +67,7 @@ namespace Sass { std::vector plugin_paths; // relative paths to load plugins std::vector include_paths; // lookup paths for includes + std::vector extensions; // lookup extensions for imports` @@ -109,6 +110,8 @@ namespace Sass { void collect_plugin_paths(string_list* paths_array); void collect_include_paths(const char* paths_str); void collect_include_paths(string_list* paths_array); + void collect_extensions(const char* extensions_str); + void collect_extensions(string_list* extensions_array); std::string format_embedded_source_map(); std::string format_source_mapping_url(const std::string& out_path); diff --git a/src/libsass/src/cssize.cpp b/src/libsass/src/cssize.cpp index 4c062a628..6a12fdf7b 100644 --- a/src/libsass/src/cssize.cpp +++ b/src/libsass/src/cssize.cpp @@ -559,7 +559,7 @@ namespace Sass { std::string m1 = std::string(mq1->is_restricted() ? "only" : mq1->is_negated() ? "not" : ""); std::string t1 = mq1->media_type() ? mq1->media_type()->to_string(ctx.c_options) : ""; - std::string m2 = std::string(mq2->is_restricted() ? "only" : mq1->is_negated() ? "not" : ""); + std::string m2 = std::string(mq2->is_restricted() ? "only" : mq2->is_negated() ? "not" : ""); std::string t2 = mq2->media_type() ? mq2->media_type()->to_string(ctx.c_options) : ""; diff --git a/src/libsass/src/debugger.hpp b/src/libsass/src/debugger.hpp index ee0d6eba7..f1ceabd9a 100644 --- a/src/libsass/src/debugger.hpp +++ b/src/libsass/src/debugger.hpp @@ -627,6 +627,7 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env) Number_Ptr expression = Cast(node); std::cerr << ind << "Number " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; + std::cerr << " [delayed: " << expression->is_delayed() << "] "; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->value() << expression->unit() << "]" << " [hash: " << expression->hash() << "] " << diff --git a/src/libsass/src/eval.cpp b/src/libsass/src/eval.cpp index 2ddfa93ea..841f7277b 100644 --- a/src/libsass/src/eval.cpp +++ b/src/libsass/src/eval.cpp @@ -599,10 +599,6 @@ namespace Sass { switch (op_type) { case Sass_OP::EQ: return *l_n == *r_c ? bool_true : bool_false; case Sass_OP::NEQ: return *l_n == *r_c ? bool_false : bool_true; - case Sass_OP::LT: return *l_n < *r_c ? bool_true : bool_false; - case Sass_OP::GTE: return *l_n < *r_c ? bool_false : bool_true; - case Sass_OP::LTE: return *l_n < *r_c || *l_n == *r_c ? bool_true : bool_false; - case Sass_OP::GT: return *l_n < *r_c || *l_n == *r_c ? bool_false : bool_true; case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD: return Operators::op_number_color(op_type, *l_n, *r_c, ctx.c_options, b_in->pstate()); default: break; @@ -643,10 +639,6 @@ namespace Sass { switch (op_type) { case Sass_OP::EQ: return *l_c == *r_n ? bool_true : bool_false; case Sass_OP::NEQ: return *l_c == *r_n ? bool_false : bool_true; - case Sass_OP::LT: return *l_c < *r_n ? bool_true : bool_false; - case Sass_OP::GTE: return *l_c < *r_n ? bool_false : bool_true; - case Sass_OP::LTE: return *l_c < *r_n || *l_c == *r_n ? bool_true : bool_false; - case Sass_OP::GT: return *l_c < *r_n || *l_c == *r_n ? bool_false : bool_true; case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD: return Operators::op_color_number(op_type, *l_c, *r_n, ctx.c_options, b_in->pstate()); default: break; @@ -1265,13 +1257,6 @@ namespace Sass { Expression_Ptr Eval::operator()(String_Constant_Ptr s) { - if (!s->is_delayed() && name_to_color(s->value())) { - Color_Ptr c = SASS_MEMORY_COPY(name_to_color(s->value())); // copy - c->pstate(s->pstate()); - c->disp(s->value()); - c->is_delayed(true); - return c; - } return s; } diff --git a/src/libsass/src/expand.cpp b/src/libsass/src/expand.cpp index ccd2822df..d8dc03f14 100644 --- a/src/libsass/src/expand.cpp +++ b/src/libsass/src/expand.cpp @@ -246,7 +246,8 @@ namespace Sass { std::string str(prop->to_string(ctx.c_options)); new_p = SASS_MEMORY_NEW(String_Constant, old_p->pstate(), str); } - Expression_Obj value = d->value()->perform(&eval); + Expression_Obj value = d->value(); + if (value) value = value->perform(&eval); Block_Obj bb = ab ? operator()(ab) : NULL; if (!bb) { if (!value || (value->is_invisible() && !d->is_important())) return 0; diff --git a/src/libsass/src/file.cpp b/src/libsass/src/file.cpp index 32d4a7c63..ab2065194 100644 --- a/src/libsass/src/file.cpp +++ b/src/libsass/src/file.cpp @@ -342,13 +342,13 @@ namespace Sass { for(auto ext : exts) { rel_path = join_paths(base, "_" + name + ext); abs_path = join_paths(root, rel_path); - if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); + if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path, ext == ".css" }); } // next test plain name with exts for(auto ext : exts) { rel_path = join_paths(base, name + ext); abs_path = join_paths(root, rel_path); - if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); + if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path, ext == ".css" }); } // nothing found return includes; diff --git a/src/libsass/src/file.hpp b/src/libsass/src/file.hpp index 279b9e9f6..a043bea7a 100644 --- a/src/libsass/src/file.hpp +++ b/src/libsass/src/file.hpp @@ -89,9 +89,14 @@ namespace Sass { public: // resolved absolute path std::string abs_path; + // is a deprecated file type + bool deprecated; public: + Include(const Importer& imp, std::string abs_path, bool deprecated) + : Importer(imp), abs_path(abs_path), deprecated(deprecated) + { } Include(const Importer& imp, std::string abs_path) - : Importer(imp), abs_path(abs_path) + : Importer(imp), abs_path(abs_path), deprecated(false) { } }; @@ -121,11 +126,12 @@ namespace Sass { namespace File { - static std::vector defaultExtensions = { ".scss", ".sass", ".css" }; + static std::vector defaultExtensions = { ".scss", ".sass" }; std::vector resolve_includes(const std::string& root, const std::string& file, const std::vector& exts = defaultExtensions); + } } diff --git a/src/libsass/src/functions.cpp b/src/libsass/src/functions.cpp index 9b118d53a..c9999fc3a 100644 --- a/src/libsass/src/functions.cpp +++ b/src/libsass/src/functions.cpp @@ -467,7 +467,7 @@ namespace Sass { double s; double l = (max + min) / 2.0; - if (max == min) { + if (NEAR_EQUAL(max, min)) { h = s = 0; // achromatic } else { diff --git a/src/libsass/src/inspect.cpp b/src/libsass/src/inspect.cpp index 273ff8fb1..b4a66fab8 100644 --- a/src/libsass/src/inspect.cpp +++ b/src/libsass/src/inspect.cpp @@ -658,6 +658,9 @@ namespace Sass { } std::stringstream hexlet; + // dart sass compressed all colors in regular css always + // ruby sass and libsass does it only when not delayed + // since color math is going to be removed, this can go too bool compressed = opt.output_style == COMPRESSED; hexlet << '#' << std::setw(1) << std::setfill('0'); // create a short color hexlet if there is any need for it @@ -681,9 +684,6 @@ namespace Sass { if (name != "") { ss << name; } - else if (r == 0 && g == 0 && b == 0 && a == 0) { - ss << "transparent"; - } else if (a >= 1) { if (res_name != "") { if (compressed && hexlet.str().size() < res_name.size()) { diff --git a/src/libsass/src/operators.cpp b/src/libsass/src/operators.cpp index 65885bf19..02e303738 100644 --- a/src/libsass/src/operators.cpp +++ b/src/libsass/src/operators.cpp @@ -127,15 +127,15 @@ namespace Sass { double lval = lhs.value(); double rval = rhs.value(); + if (op == Sass_OP::MOD && rval == 0) { + return SASS_MEMORY_NEW(String_Quoted, pstate, "NaN"); + } + if (op == Sass_OP::DIV && rval == 0) { std::string result(lval ? "Infinity" : "NaN"); return SASS_MEMORY_NEW(String_Quoted, pstate, result); } - if (op == Sass_OP::MOD && rval == 0) { - throw Exception::ZeroDivisionError(lhs, rhs); - } - size_t l_n_units = lhs.numerators.size(); size_t l_d_units = lhs.numerators.size(); size_t r_n_units = rhs.denominators.size(); diff --git a/src/libsass/src/parser.cpp b/src/libsass/src/parser.cpp index 525f199d1..ee51d56b6 100644 --- a/src/libsass/src/parser.cpp +++ b/src/libsass/src/parser.cpp @@ -1578,7 +1578,7 @@ namespace Sass { return nr; } - Expression_Ptr Parser::lexed_hex_color(const ParserState& pstate, const std::string& parsed) + Value_Ptr Parser::lexed_hex_color(const ParserState& pstate, const std::string& parsed) { Color_Ptr color = NULL; if (parsed[0] != '#') { @@ -1628,6 +1628,19 @@ namespace Sass { return color; } + Value_Ptr Parser::color_or_string(const std::string& lexed) const + { + if (auto color = name_to_color(lexed)) { + auto c = SASS_MEMORY_NEW(Color, color); + c->is_delayed(true); + c->pstate(pstate); + c->disp(lexed); + return c; + } else { + return SASS_MEMORY_NEW(String_Constant, pstate, lexed); + } + } + // parse one value for a list Expression_Obj Parser::parse_value() { @@ -1670,7 +1683,7 @@ namespace Sass { { return SASS_MEMORY_NEW(Null, pstate); } if (lex< identifier >()) { - return SASS_MEMORY_NEW(String_Constant, pstate, lexed); + return color_or_string(lexed); } if (lex< percentage >()) @@ -1841,7 +1854,7 @@ namespace Sass { return schema->length() > 0 ? schema.detach() : NULL; } - String_Constant_Obj Parser::parse_static_value() + Value_Obj Parser::parse_static_value() { lex< static_value >(); Token str(lexed); @@ -1852,8 +1865,7 @@ namespace Sass { --str.end; --position; - String_Constant_Ptr str_node = SASS_MEMORY_NEW(String_Constant, pstate, str.time_wspace()); - return str_node; + return color_or_string(str.time_wspace());; } String_Obj Parser::parse_string() @@ -1962,7 +1974,7 @@ namespace Sass { if (lex< re_static_expression >()) { ex = SASS_MEMORY_NEW(String_Constant, pstate, lexed); } else { - ex = parse_list(); + ex = parse_list(true); } ex->is_interpolant(true); schema->append(ex); @@ -1986,7 +1998,7 @@ namespace Sass { } if (peek < exactly < '-' > >()) break; } - else if (lex< sequence < identifier > >()) { + else if (lex< identifier >()) { schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed)); if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) { // need_space = true; @@ -2691,7 +2703,9 @@ namespace Sass { if ((rv = lex_interp_string())) return rv; if ((rv = lex_interp_uri())) return rv; if ((rv = lex_interpolation())) return rv; - return rv; + if (lex< alternatives< hex, hex0 > >()) + { return lexed_hex_color(lexed); } + return rv; } String_Schema_Obj Parser::parse_almost_any_value() @@ -2777,6 +2791,7 @@ namespace Sass { >(p) ) { bool could_be_property = peek< sequence< exactly<'-'>, exactly<'-'> > >(p) != 0; + bool could_be_escaped = false; while (p < q) { // did we have interpolations? if (*p == '#' && *(p+1) == '{') { @@ -2785,9 +2800,10 @@ namespace Sass { } // A property that's ambiguous with a nested selector is interpreted as a // custom property. - if (*p == ':') { + if (*p == ':' && !could_be_escaped) { rv.is_custom_property = could_be_property || p+1 == q || peek< space >(p+1); } + could_be_escaped = *p == '\\'; ++ p; } // store anyway } diff --git a/src/libsass/src/parser.hpp b/src/libsass/src/parser.hpp index 83c7f34ba..d2a6ddc1a 100644 --- a/src/libsass/src/parser.hpp +++ b/src/libsass/src/parser.hpp @@ -290,7 +290,7 @@ namespace Sass { String_Obj parse_url_function_argument(); String_Obj parse_interpolated_chunk(Token, bool constant = false, bool css = true); String_Obj parse_string(); - String_Constant_Obj parse_static_value(); + Value_Obj parse_static_value(); String_Schema_Obj parse_css_variable_value(bool top_level = true); String_Schema_Obj parse_css_variable_value_token(bool top_level = true); String_Obj parse_ie_property(); @@ -325,6 +325,8 @@ namespace Sass { Error_Obj parse_error(); Debug_Obj parse_debug(); + Value_Ptr color_or_string(const std::string& lexed) const; + // be more like ruby sass Expression_Obj lex_almost_any_value_token(); Expression_Obj lex_almost_any_value_chars(); @@ -380,12 +382,12 @@ namespace Sass { static Number_Ptr lexed_number(const ParserState& pstate, const std::string& parsed); static Number_Ptr lexed_dimension(const ParserState& pstate, const std::string& parsed); static Number_Ptr lexed_percentage(const ParserState& pstate, const std::string& parsed); - static Expression_Ptr lexed_hex_color(const ParserState& pstate, const std::string& parsed); + static Value_Ptr lexed_hex_color(const ParserState& pstate, const std::string& parsed); private: Number_Ptr lexed_number(const std::string& parsed) { return lexed_number(pstate, parsed); }; Number_Ptr lexed_dimension(const std::string& parsed) { return lexed_dimension(pstate, parsed); }; Number_Ptr lexed_percentage(const std::string& parsed) { return lexed_percentage(pstate, parsed); }; - Expression_Ptr lexed_hex_color(const std::string& parsed) { return lexed_hex_color(pstate, parsed); }; + Value_Ptr lexed_hex_color(const std::string& parsed) { return lexed_hex_color(pstate, parsed); }; static const char* re_attr_sensitive_close(const char* src); static const char* re_attr_insensitive_close(const char* src); diff --git a/src/libsass/src/prelexer.cpp b/src/libsass/src/prelexer.cpp index d0edbc9cc..a43b1ee3c 100644 --- a/src/libsass/src/prelexer.cpp +++ b/src/libsass/src/prelexer.cpp @@ -1606,7 +1606,7 @@ namespace Sass { >(src); } - extern const char css_variable_url_top_level_negates[] = "()[]{}\"'#/;!"; + extern const char css_variable_url_top_level_negates[] = "()[]{}\"'#/;"; const char* css_variable_top_level_value(const char* src) { return sequence< alternatives< diff --git a/src/libsass/src/sass.cpp b/src/libsass/src/sass.cpp index 98e349f48..72edd7ced 100644 --- a/src/libsass/src/sass.cpp +++ b/src/libsass/src/sass.cpp @@ -33,8 +33,10 @@ extern "C" { void* ADDCALL sass_alloc_memory(size_t size) { void* ptr = malloc(size); - if (ptr == NULL) - out_of_memory(); + if (ptr == NULL) { + std::cerr << "Out of memory.\n"; + exit(EXIT_FAILURE); + } return ptr; } diff --git a/src/libsass/src/sass2scss.cpp b/src/libsass/src/sass2scss.cpp index 56333b38e..8645d0c37 100644 --- a/src/libsass/src/sass2scss.cpp +++ b/src/libsass/src/sass2scss.cpp @@ -154,6 +154,21 @@ namespace Sass } + static size_t findFirstCharacter (std::string& sass, size_t pos) + { + return sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos); + } + + static size_t findLastCharacter (std::string& sass, size_t pos) + { + return sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE, pos); + } + + static bool isUrl (std::string& sass, size_t pos) + { + return sass[pos] == 'u' && sass[pos+1] == 'r' && sass[pos+2] == 'l' && sass[pos+3] == '('; + } + // check if there is some char data // will ignore everything in comments static bool hasCharData (std::string& sass) @@ -587,6 +602,7 @@ namespace Sass sass.substr(pos_left, 5) == "@warn" || sass.substr(pos_left, 6) == "@debug" || sass.substr(pos_left, 6) == "@error" || + sass.substr(pos_left, 6) == "@value" || sass.substr(pos_left, 8) == "@charset" || sass.substr(pos_left, 10) == "@namespace" ) { sass = indent + sass.substr(pos_left); } @@ -606,23 +622,38 @@ namespace Sass { // get positions for the actual import url size_t pos_import = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left + 7); - size_t pos_quote = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_import); - // leave proper urls untouched - if (sass.substr(pos_quote, 4) != "url(") - { - // check if the url appears to be already quoted - if (sass.substr(pos_quote, 1) != "\"" && sass.substr(pos_quote, 1) != "\'") - { - // get position of the last char on the line - size_t pos_end = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE); - // assertion check for valid result - if (pos_end != std::string::npos) - { - // add quotes around the full line after the import statement - sass = sass.substr(0, pos_quote) + "\"" + sass.substr(pos_quote, pos_end - pos_quote + 1) + "\""; + size_t pos = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_import); + size_t start = pos; + bool in_dqstr = false; + bool in_sqstr = false; + bool is_escaped = false; + do { + if (is_escaped) { + is_escaped = false; + } + else if (sass[pos] == '\\') { + is_escaped = true; + } + else if (sass[pos] == '"') { + if (!in_sqstr) in_dqstr = ! in_dqstr; + } + else if (sass[pos] == '\'') { + if (!in_dqstr) in_sqstr = ! in_sqstr; + } + else if (in_dqstr || in_sqstr) { + // skip over quoted stuff + } + else if (sass[pos] == ',' || sass[pos] == 0) { + if (sass[start] != '"' && sass[start] != '\'' && !isUrl(sass, start)) { + size_t end = findLastCharacter(sass, pos - 1) + 1; + sass = sass.replace(end, 0, "\""); + sass = sass.replace(start, 0, "\""); + pos += 2; } + start = findFirstCharacter(sass, pos + 1); } } + while (sass[pos++] != 0); } else if ( diff --git a/src/libsass/src/sass_context.cpp b/src/libsass/src/sass_context.cpp index afadc66e1..7a0a49ce1 100644 --- a/src/libsass/src/sass_context.cpp +++ b/src/libsass/src/sass_context.cpp @@ -74,14 +74,14 @@ namespace Sass { // move line_beg pointer to line start while (line_beg && *line_beg && lines != 0) { if (*line_beg == '\n') --lines; - utf8::unchecked::next(line_beg); + utf8::unchecked::next(line_beg); } const char* line_end = line_beg; // move line_end before next newline character while (line_end && *line_end && *line_end != '\n') { if (*line_end == '\n') break; if (*line_end == '\r') break; - utf8::unchecked::next(line_end); + utf8::unchecked::next(line_end); } if (line_end && *line_end != 0) ++ line_end; size_t line_len = line_end - line_beg; @@ -524,6 +524,7 @@ extern "C" { options->c_headers = 0; options->plugin_paths = 0; options->include_paths = 0; + options->extensions = 0; } // helper function, not exported, only accessible locally @@ -558,6 +559,18 @@ extern "C" { cur = next; } } + // Deallocate extension + if (options->extensions) { + struct string_list* cur; + struct string_list* next; + cur = options->extensions; + while (cur) { + next = cur->next; + free(cur->string); + free(cur); + cur = next; + } + } // Free options strings free(options->input_path); free(options->output_path); @@ -577,6 +590,7 @@ extern "C" { options->c_headers = 0; options->plugin_paths = 0; options->include_paths = 0; + options->extensions = 0; } // helper function, not exported, only accessible locally @@ -713,6 +727,22 @@ extern "C" { IMPLEMENT_SASS_CONTEXT_TAKER(char*, source_map_string); IMPLEMENT_SASS_CONTEXT_TAKER(char**, included_files); + // Push function for import extenions + void ADDCALL sass_option_push_import_extension(struct Sass_Options* options, const char* ext) + { + struct string_list* extension = (struct string_list*) calloc(1, sizeof(struct string_list)); + if (extension == 0) return; + extension->string = ext ? sass_copy_c_string(ext) : 0; + struct string_list* last = options->extensions; + if (!options->extensions) { + options->extensions = extension; + } else { + while (last->next) + last = last->next; + last->next = extension; + } + } + // Push function for include paths (no manipulation support for now) void ADDCALL sass_option_push_include_path(struct Sass_Options* options, const char* path) { diff --git a/src/libsass/src/sass_context.hpp b/src/libsass/src/sass_context.hpp index 8ae1fb12c..9d192a301 100644 --- a/src/libsass/src/sass_context.hpp +++ b/src/libsass/src/sass_context.hpp @@ -40,9 +40,12 @@ struct Sass_Options : Sass_Output_Options { // Colon-separated list of paths // Semicolon-separated on Windows // Maybe use array interface instead? + char* extension; char* include_path; char* plugin_path; + // Extensions (linked string list) + struct string_list* extensions; // Include paths (linked string list) struct string_list* include_paths; // Plugin paths (linked string list) @@ -126,4 +129,4 @@ struct Sass_Compiler { Sass::Block_Obj root; }; -#endif \ No newline at end of file +#endif diff --git a/src/libsass/src/util.hpp b/src/libsass/src/util.hpp index 4313d502d..f23475fe0 100644 --- a/src/libsass/src/util.hpp +++ b/src/libsass/src/util.hpp @@ -12,11 +12,6 @@ namespace Sass { - #define out_of_memory() do { \ - std::cerr << "Out of memory.\n"; \ - exit(EXIT_FAILURE); \ - } while (0) - double round(double val, size_t precision = 0); double sass_strtod(const char* str); const char* safe_str(const char *, const char* = "");