-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Conversation
@matryo, first of all thanks for the effort. 👍 Welcome to GitHub! Wow you joined today and your first PR is HUGE! I would have split it into more commits so to make it easy to manage. You are aiming to address #332, which is in our Regarding the implementation, we decided to implement this format: sass.render({
data: '#{headings(2,5)} { color: #08c; }',
functions: { // the idea is to make these definitions least complicated
headings: function (from, to) {
return processIt(from, to);
},
anotherHeadings: function (from, to, done) {
done(processIt(from, to));
}
}
}); which is achievable if you keep the custom functions definition in C++ keeping JS usage syntax flexible (the way we did for custom importer). Thirdly, I am not sure about the new Sass types you added ( |
Looks pretty ok to me 👍 Also the new types are pretty much expected from my side, since they represent the values possbile in libsass (https://github.com/sass/libsass/wiki/API-Sass-Value). I personally also implemented/overloaded "toString" for each type, which could solve some "issues" @matryo mentioned when handling such types in node (maybe take a look at how I did it for I also like the config part by @matryo better, since the snippet @am11 posted is not taking advantage of the "possibilities" libsass offers here. There is no way to define default values for arguments, if you do not explicitly include the function signature somewhere. And I also do it that way in |
@am1 I understand that this PR's approach is pretty unusual and I am really sorry about that. I did not realize any one had already started a draft in that direction. Just to give you some context this was a major road block for us and I needed to get this completed in a couple of days, which unfortunately did not leave me much opportunity to consult the maintainers team. That said I am totally eager to take all the time necessary to make sure this code is in line with what you guys envisioned for 2.1 if you think this is a solid base for #332. |
@mgreter, now that you have described it, the syntax looks pretty OK to me too. 😄 Would it make sense to have both syntaxes for function descriptor? Mostly the function definitions lack default values. So, when we have this special need for default parameter: 'headings($from: 0, $to: 6)': function (from, to) {
return processIt(from, to);
}, otherwise: headings: function (from, to) {
return processIt(from, to);
}, @matryo, we may need to refine it and this will take some time to digest the whole PR. |
'src/custom_function_bridge.cpp', | ||
'src/custom_importer_bridge.cpp', | ||
'src/create_string.cpp', | ||
'src/sass_types.cpp', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CodeStyler: This is a sorted list.
Btw. I find it pretty cool that @matryo was able to pull off this implementation without asking anybody about the internals directly! I hope that the time I've put into the libsass wiki was worth it! @matryo : If you have anything to add to the Wiki, please do so!! @am11 you could handle functions without signatures as if they would be registered with |
I agree. This proves two things: 1) libsass API documentation is very reliable and 2) @matryo is an excellent C++ developer! |
proto->Set(NanNew("setKey"), FunctionTemplate::New(SetKey)->GetFunction()); | ||
proto->Set(NanNew("getValue"), FunctionTemplate::New(GetValue)->GetFunction()); | ||
proto->Set(NanNew("setValue"), FunctionTemplate::New(SetValue)->GetFunction()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it make sense to take initPrototype
and get_constructorName definitions to cpp
as well?
Same applies to CoreValue
class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely. My rationale for leaving them in the header was better readability of what is made available to the JS runtime but moving them to the cpp
makes a lot of sense as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have already made the code much readable than it was before. IMO with this, it will make the code discoverability much easy for future contributors.
My vote is for even making code/implementation files separate for each derived classes (like you did for create_string
). :)
I pushed a few commits that should cover all of the recommendations made thus far, namely:
Also made some improvements on the following points:
As far as the API doc for libsass is concerned, I concur it is really nice! Unlike a lot of API docs it does not beat around the bush and goes straight to the point, I found it very useful during this exercise. So great job! |
Excellent job there @matryo! And thanks for adding the alternative syntax . I will test it and give you further feedback. By it looks, it seems very promising as is. :) |
New types are very much in the scope of node-sass. Without them, how would you return a sass value? Some js types could be auto-boxed into Sass values but many can not. In ruby sass, these types are part of the core sass project. E.g. https://github.com/sass/sass/blob/stable/lib/sass/script/value/number.rb |
I thought these will return cstrings from our binding and LibSass will take care of the rest. Unlike ruby-sass, node-sass is only a wrapper around libsass. Besides, I do not know about the use-case when the custom functions are required to return anything but the stringly typed value, which LibSass will eventually stamp out to resultant CSS as is? |
@am11 From what I understood of the API, custom functions registered with div {
color: lighten(foo(), 30%);
} ...if foo returned a string. As I said earlier we could however let the end developer return strings and have a post-processing stage (either in |
@matryo, that makes sense. :)
I think that won't be necessary. With the proper documentation and tests cases to exemplify the usage (as you have already added both 👍 ), the implementers of node-sass API shall be able to define custom functions without much of a hassle. Since the main advantage of node-sass is performance; I am afraid pattern sniffing with regex will have a negative affect on perf ? |
As one of the heaviest users of the Ruby function api that is provided by Sass, this patch is exactly what I was expecting. returning javascript objects that are converted to |
I can only support @chriseppstein words! This is exactly as we did it in |
A few other things I was expecting to see here:
I would expect
In ruby, you can access the current selector context I would expect You can also access the current variables defined in the global scope
It is very useful to be able to invoke sass builtin functions from within a custom function. It's also nice to be able to invoke functions defined in sass. For example, the Obviously what we have is a good first step, and I wouldn't block landing this PR because these features are missing (Ruby Sass didn't have them at first either) |
@chriseppstein, (sorry for this getting stretched and GH has zero comment threading/nesting) here:
do you mean return value's type of custom function should be specified by user as @matryo's solution ( |
@chriseppstein You raised three very interesting points. At the moment it unfortunately looks like libsass' public API cannot provide what we would need to implement something like this. After looking at libsass' internals I can see that there would be a rather straight forward way to implement those features, but that would be using a private API, which is a bit questionable I guess. I will take some time this week to augment the JS binding objects so as to provide a more user friendly API. |
d6ce2d4
to
9d499f7
Compare
bf7b9a4
to
313b4d8
Compare
313b4d8
to
07a9540
Compare
🎉 It is official now!! ☀️ 🌟 ⭐ Brilliant work @matryo 👍 |
👍👍👍 awesome |
@matryo, do we need Boolean and Null exported twice: separately as types |
❤️ You guys rock my socks off. |
I second his ❤️ |
🎉 Awesome! ❤️ |
@am11 They indeed do not necessarily have to be on both and we could refactor the code to have those singleton references exported just once. It just felt easier to instantiate those singletons within |
This is a refactor of #530 (custom importers) to also support #332 (custom functions).
The mechanism to synchronize the worker thread that runs libsass and the main v8 loop has been centralized and abstracted into an object called
CallbackBridge
that has two specializations:CustomImporterBridge
andCustomFunctionBridge
. The former supports the existing functionality (#530) while the other makes it possible to register custom functions that are backed up by v8 functions and takes care of translatingSass_Value
s to something that makes sense to the JS runtime and vice versa.The function arguments are relayed to the JS callback by means of wrapping
Sass_Value*
pointers into v8 objects. Those objects mimic the API offered by libsass and as such could be a bit cumbersome to work with on the javascript side. This problem could however be largely mitigated by translating those wrapper objects into JS primitives upon entering the JS callback and back into wrapper objects upon return. The only edge cases that I can think of is that ofSASS_NUMBER
values when they carry units andSASS_COLOR
where we could parse strings and detect patterns like\d+(px|em|%)
andrgba?\(...)\
or resolve to use more JS friendly facades.This is a quick example demonstrating how compass'
headings
function may be implemented: