-
Notifications
You must be signed in to change notification settings - Fork 464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve custom importer to pass parent import context #691
Conversation
One example would be a custom importer that loads the content from the web. If this includes another import, it should be loaded relative to the parent file. To do this, the custom importer needs to know the context of the parent import. Which is what this commit will fix!
I'm actually OK with merging this. The feature should work and I would like to get the API changes for the importer in ASAP. I initially thought about unifying all the needed info directly into Basically that would get rid of this: sources.push_back(contents);
included_files.push_back(abs_path);
queue.push_back(Sass_Queued(load_path, abs_path, contents));
source_map.source_index.push_back(sources.size() - 1);
include_links.push_back(resolve_relative_path(abs_path, source_map_file, cwd)); But this can be postponed to a later point! |
Improve custom importer to pass parent import context
Yep, updating the docs is on my todo list. There was a small change in that API: 34a5752 typedef struct Sass_Import** (*Sass_C_Import_Fn) (const char* url, const char* prev, void* cookie); You probably only need to add |
Great! So basically can I interpret it llike this: // JS
// set options:
options.file = blah;
...
options.prefix = "some-url-prefix-string";
options.importer = function(file, prefix, done) {
// do something with file and prefix and when you
// are done producing result, call done(result)
}; Then in binding code, pass prefix as second parameter to I am afraid my example might not be accurate, but does it make sense from node-sass implementation viewpoint? |
I wouldn't call the variable prefix, since it contains also the filename of the previous import. Lets say we have this local scss file "import.scss":
The file on the server has this content:
Somce the second import is relative, it should be relative to its parent (which is on a remote server in this case). We added this string to make this information accessible to implementors. With second thoughts it might have been better and easier to just have a function to query the import stack. IMO this feature is pretty important for custom importers, so I would like to ship it with the first release that includes the new API. Your opinions @am11, @xzyfer ? |
Oh that's actually the previous URL! But what would happen if partial.scss (in your example) also imports I think it would probably make sense to make it Thoughts? IMO it makes sense to wait for this feature before the release. |
Good point, I guess the sample is still incomplete :)
Next import looks like this:
It may should be noted that you must include It's not as sophisticated as the original ruby sass importer; but it's a start. |
typedef struct Sass_Import** (*Sass_C_Import_Fn) (
const char* parsed_url, // libsass: what I got
const char* interpreted_url, // libsass: my interpretation of what I got
void* cookie // libsass: some cookie you want to eat later, I don't care about
); 😃 |
I think beyond current URL and its interpretation by libsass, the tertiary implementer/consumer can maintain the list of imports (perhaps in a static memory) it has received by libsass via importer function -- if the use-case is extra-ordinary. |
I don't think I can follow where you want to go with that. Can you come up with a use case or example!? I'm thinking about reverting the additional callback parameter and use functions to query the stack:
I actually like this a lot better! Sorry for the detour @am11, but I really think I should have done it that way in the first place. Oh well, rome wasn't built in a day either ;) |
Haha! That is true. Actually I am assuming that libsass without custom importer, does the following:
Now suppose Momentarily, if it was a depth-first, then the previous to So in my opinion, libsass should keep that context (resolving path with correct cwd based on the recursion step or tree or whatever) and send us the paths before and after the interpretation, along with cookie. I think this will suffice most of the use cases (the final implementer can then cache the URLs, store them in cache and make use of them depending on their use-case). Does this make sense? |
Separately, I think (not sure if it is beyond the scope at this point), for downloading content in case of URL, libsass can probably make use of http://casablanca.codeplex.com/. |
(or even better libcurl: https://github.com/bagder/curl) |
I don't think libsass will support this anytime soon (probably never). And this is quite reasonable, since we want to concentrate on the sass part and not how to "interface" with the outside world. We like to have our tiny and well defined API and let the outside world mess up things :) Unfortunately again I have to abandon my previous idea with the additional API functions. So for now I will leave it as it is. IMHO you can do the correct stuff now with "nested" import calls in any way your context suits you. But I agree that this makes it impossible to access any "grand-parent" imports. Not sure if this really is a common use case!? For now we let the implementors do all the hard work, we only provide the raw information. I guess it's better than nothing! |
Thanks for clarification. Lets go with the current implementation. :) I updated node-sass code with Consider the following setup: C:\temp\alpha\index.scss: @import 'foo.scss';
@import 'bar.scss'; C:\temp\alpha\foo.scss: @import 'bar2.scss';
body {
color: red;
} C:\temp\alpha\bar.scss: p {
color: grey;
} C:\temp\alpha\bar2.scss: span {
z-index: 4;
} Following is the JS code (lets call it require("./").render ({
file: "c:/temp/alpha/index.scss",
outFile: "c:/temp/alpha/index.css",
sourceMap: "c:/temp/alpha/index.css.map",
success: function(css, map){
console.warn(css); console.warn(map);
},
error: function(err, status){
console.error(status + ": " + err);
},
importer: function(file, prev, done) {
console.log("File: " + file);
console.log("Previous: " + prev);
done({
contents: "div {color: yellow;}"
});
}
}); (in the importer, I am printing the first two parameters as is to stdout) Output for log messages:
Bug 1:
Next, the generated source-map looks like this: {
"version": 3,
"file": "index.css",
"sources": [
"index.scss",
"../../../C:/temp/alpha/foo.scss",
"../../../C:/temp/alpha/bar.scss"
],
"sourcesContent": [],
"mappings": "AEAA,AAAK,AAAO,AAAZ,AAAK,AAAO",
"names": []
} Bug2: The If I remove importer from {
"version": 3,
"file": "index.css",
"sources": [
"index.scss",
"foo.scss",
"bar.scss",
"bar2.scss"
],
"sourcesContent": [],
"mappings": "AGAA,AACA,AAAS,AFAT,AACA,AAAO,ACFP,AACA,AAAO",
"names": []
} Can we nail them before the release? :) |
Thanks for checking it out. Will add your example to my other perl integration tests. If you are right this needs some more work. Will have time in some hours ... |
I tested your sample with
Are you sure you return the right thing from the importer? Looks like you don't! |
OK, I just pushed a commit to master which probably fixes at least some parts of the problem you have. I noticed you do not return a filename and this caused some problems on linux (which I wasn't aware off). |
Hi. I updated to latest libsass, but the sourcemap issue still exists. It looks like in case of custom importers, the paths in source-map's require("./").render ({
file: "c:\\temp\\alpha\\index.scss",
outFile: "c:\\temp\\alpha\\index.css",
sourceMap: "c:\\temp\\alpha\\index.css.map",
success: function(css, map){
console.warn(css); console.warn(map);
},
error: function(err, status){
console.error(status + ": " + err);
}
}); vs require("./").render ({
file: "c:\\temp\\alpha\\index.scss",
outFile: "c:\\temp\\alpha\\index.css",
sourceMap: "c:\\temp\\alpha\\index.css.map",
success: function(css, map){
console.warn(css); console.warn(map);
},
error: function(err, status){
console.error(status + ": " + err);
},
importer: function(file, prev, done) {
console.log("Previous: " + prev);
done({
contents: "div {color: yellow;}"
});
}
}); BTW, node.js runtime does not mind Linux-style slash in Windows. :) |
Needs some more work and more tests to make sure
it does not introduce any memory leaks!
One example would be a custom importer that loads the
content from the web. If this includes another import,
it should be loaded relative to the parent file. To do
this, the custom importer needs to know the context of
the parent import. Which is what this commit will fix!