Giter VIP home page Giter VIP logo

Comments (38)

Igorbek avatar Igorbek commented on August 15, 2024

I also agree with the issue. It would be great to have some interface that implement custom importers.

from libsass.

Igorbek avatar Igorbek commented on August 15, 2024

I have implemented this functionality in my fork: https://github.com/Igorbek/libsass/tree/SourceProvider

from libsass.

pistolero avatar pistolero commented on August 15, 2024

Wow, that is fantastic

from libsass.

Igorbek avatar Igorbek commented on August 15, 2024

@pistolero http://habrahabr.ru/post/147567/ =)

from libsass.

pistolero avatar pistolero commented on August 15, 2024

@Igorbek nice. Do you plan to send pull request with your changes to @hcatlin for review?

from libsass.

Igorbek avatar Igorbek commented on August 15, 2024

@pistolero yep, I'll do it today after take some code cleaning.

from libsass.

jhnns avatar jhnns commented on August 15, 2024

Is this feature still planned? I could need it too 😄

from libsass.

HamptonMakes avatar HamptonMakes commented on August 15, 2024

I believe this is already solved. Please let me know if it's still an issue.

from libsass.

BPScott avatar BPScott commented on August 15, 2024

Hi @hcatlin, I've had a look through the source code and while I've found code to allow adding a vector of custom include paths, I haven't spotted the ability for tools that bind to libsass (e.g. node-sass) to provide a custom @import resolver. Could you point me to where I could find that?

The use-case I'm thinking of is that binding libraries could provide hooks into that ecosystem's package manager. For instance node-sass could provide an @import resolver that transverses npm's node_modules folder. This is would be similar to Rework providing an npm loader. In the same vein, the ruby bindings could provide a resolver to transverse the rubygems folder structure.

Thanks.

from libsass.

jhnns avatar jhnns commented on August 15, 2024

@BPScott that's exactly the use-case 👍

from libsass.

HamptonMakes avatar HamptonMakes commented on August 15, 2024

Hmm... yeah, we haven't written anything like that. I bet we could do it
with a callback that you can register.

In your mind, would you expect the @import callback to act like a "last
ditch" or "method missing" resolver? Or, have it be the first thing
called, and then if it's unaware of a package name, go to the standard Sass
import resolver?

On Mon, Oct 6, 2014 at 10:36 AM, Johannes Ewald [email protected]
wrote:

@BPScott https://github.com/BPScott that's exactly the use-case [image:
👍]

Reply to this email directly or view it on GitHub
#21 (comment).

from libsass.

BPScott avatar BPScott commented on August 15, 2024

I think it would need to be the first thing to try. This functionality feels like an override rather than a fallback coping mechanism.

I believe @import 'foo/bar; would need to look within the foo package first, then if that fails it should use the default resolver to try the relative file path ./foo/bar.scss (though it could be up to the binding library to skip trying the default resolver). Otherwise you could end up with oddness where files relative to the current file take precedence over the files from the package manager which would be undesirable.

from libsass.

HamptonMakes avatar HamptonMakes commented on August 15, 2024

Hmm… I think I’d assume it did local resolution first. But, I can see your point.

On Mon, Oct 6, 2014 at 11:38 AM, Ben Scott [email protected]
wrote:

I think it would need to be the first thing to try. This functionality feels like an override rather than a fallback coping mechanism.

I believe @import 'foo/bar; would need to look within the foo package first, then if that fails it should use the default resolver to try the relative file path ./foo/bar.scss (though it could be up to the binding library to skip trying the default resolver). Otherwise you could end up with oddness where files relative to the current file take precedence over the files from the package manager which would be undesirable.

Reply to this email directly or view it on GitHub:
#21 (comment)

from libsass.

BPScott avatar BPScott commented on August 15, 2024

A quick nose around the rework importer and the npm docs suggests that npm dodges this problem by saying that imports are only considered to be relative if they start with /, ../ or ./.

So with npm-style importing rules @import 'foo/bar'; would be considered invalid even if the relative file ./foo/bar.scss exists. So if node-sass wanted to allow npm-style importing then it would have to kick in before (and disable) the default resolver.

Other package managers may treat these paths differently so I think this needs to solved by each binding library depending on their platform's package manager.

from libsass.

thom4parisot avatar thom4parisot commented on August 15, 2024

Following the Node module resolver heuristic, @import 'foo/bar'; would mean import the file bar.scss from module foo. Which means @import './node_modules/foo/bar.scss';.

In other terms Sass modules could be shipped and consumed as npm dependencies; thus making a very explicit graph of relationships between BEM blocks (for example).

from libsass.

jhnns avatar jhnns commented on August 15, 2024

@hcatlin I'd assume that the callback provides the rewritten path. Basically I want to configure somewhere (e.g. using node-sass):

    ...
    resolveImport: function (path) {
         // do some magic rewrite or throw an error if the path is illegal
         return path;
    }
    ...

from libsass.

callumlocke avatar callumlocke commented on August 15, 2024

@hcatlin, @jhnns – I want to be able to provide a callback to actually returns the SCSS code to be imported, not just to return a resolved path. So by providing this callback, I could ensure Sass never touches the disk at all.

  ...
  importer: function (name, cb) {
    // look up `name`, load it, and maybe do some custom preprocessing...
    // then call back with the SCSS
    cb(null, scssString);
  }

This would allow me to properly use libsass in a transform pipeline. Current applications of this are hacky and limited, due to the fact that libsass wants to access the disk directly when it needs to import something.

Imagine I want an extra preprocessing step before rendering my SCSS. I can have main.scss.foo, and I can set up a pipeline that does my 'foo' rendering (maybe injecting some config from environment variables) and then the resulting SCSS code is fed into node-sass. This is already possible because libsass lets me feed in the SCSS code at the start. libsass doesn't need to know that main.scss doesn't actually exist on disk. This is good.

But what if my SCSS contains @import 'partial';? Suddenly libsass wants to access the disk directly. Maybe there is no file called _partial.scss, but instead I've got _partial.scss.foo, and I want to put it through the same pipeline. There is currently no way to do this.

This is why I want to be able to provide a callback that takes care of all file reading. Because it's not just about controlling disk access or resolving paths, but also about importing from a non-disk source, such as an in-memory pipeline.

from libsass.

jhnns avatar jhnns commented on August 15, 2024

Agreed 👍

from libsass.

mgreter avatar mgreter commented on August 15, 2024

A source should always have a path/filename associated, since this is what we use to create source-map information. In regard to source-maps the whole thing becomes even more complex, as a preprocessor that alters the original data will make the mappings invalid.

So IMO a proper implementation should be able to return:

  • ['filename'] - libsass will load the file itself
  • ['filename', 'filesource'] - libsass will use the source
  • ['filename', 'filesource', 'srcmapdata'] - futureproof

Beside that case, an implementer may also want to add multiple files from one import!
So IMO it should also be possible to return a array of the above entries:

[
  ['filename'],
  ['filename', 'filesource'],
  ['filename', 'filesource', 'srcmapdata']
]

IMO this would cover all possible use cases! Or did I miss anything?

The srcmapdata could be used to incorporate that information back into the the final source-map. This is not a very easy task, but I've done it already in perl (POC). I also started to add some pieces of it into a c++ library to handle source-maps.

from libsass.

callumlocke avatar callumlocke commented on August 15, 2024

Good point, I didn't think of sourcemaps.

But I don't get why an importer would want to add multiple files from one import?

from libsass.

mgreter avatar mgreter commented on August 15, 2024

I guess someone might want to implement a globbing importer.
Or a preprocessor might load additional files (header/footer).
So just in case, I guess this way it feels more "complete".

from libsass.

mgreter avatar mgreter commented on August 15, 2024

IMO this is not implemented yet, so re-opening.
Already got it to the point where I can inject custom importers.
Now I "only" need to parse the return result and implement it as described above!

from libsass.

mgreter avatar mgreter commented on August 15, 2024

I have a working implementation in my local branch!

It should implement what I propesed earlier in this thread.
But more testing is needed to verify that is works correctly!

from libsass.

jhnns avatar jhnns commented on August 15, 2024

Awesome! I'm glad to see progress on this issue. 👍

from libsass.

jhnns avatar jhnns commented on August 15, 2024

I'm just wondering how node.js would be able to load the file asynchronously in the custom importer...
I'm not a c-programmer and don't know much about libuv. Any suggestions?

from libsass.

callumlocke avatar callumlocke commented on August 15, 2024

Same here. I'd like to help test, but I don't know C. @mgreter can you explain how I would test your branch? Presumably I would make a custom sassc build using your libsass branch, but then I don't know how I would run that executable in such a way that I can respond to its requests to load imports... or even if that makes any sense

from libsass.

mgreter avatar mgreter commented on August 15, 2024

To implement it someone definitely has to know C, there is no way around.
But I can give you a hint from my initial test how such a function would look like:
Note that source will be owned and freed by sass, so make a copy if necessary!

struct Sass_Import** sass_importer(const char* url, void* cookie)
{
  struct Sass_Import** includes = sass_make_import_list(1);
  includes[0] = sass_make_import_entry("path", strdup("source"), 0);
  return includes;
}

This C function is then registered with the sass context:

sass_option_set_importer(sass_options, sass_make_importer(sass_importer, *importer_sv));

Note that importer_sv can be a pointer to anything (you get it back on the call).
I use it to store the perl function reference that should be invoked by sass_importer.

By the way, the most recent version you find in #635

from libsass.

mgreter avatar mgreter commented on August 15, 2024

Since commit mgreter@df5b7c3 this should be possible!
There is a wiki page with some API Documentation.

from libsass.

BPScott avatar BPScott commented on August 15, 2024

@mgreter: that is fantastic. Thank you for your work and the time you put into this <3

from libsass.

thom4parisot avatar thom4parisot commented on August 15, 2024

\o/

from libsass.

jhnns avatar jhnns commented on August 15, 2024

Awesome, thx! 👍

from libsass.

callumlocke avatar callumlocke commented on August 15, 2024

Thank you! 👍

from libsass.

chriseppstein avatar chriseppstein commented on August 15, 2024

The Sass Importer abstraction is a well defined API. It doesn't require the sass file to be on disk. It allows you to load Sass files from a database or over HTTP. It allows you to construct a sass file on the fly (Eg. compass sprites)

The resolution order is always relative to the current Sass file and then falling back to searching the load path from the beginning.

Is this how this works? I'm having a hard time understanding this from the code and wiki.

from libsass.

chriseppstein avatar chriseppstein commented on August 15, 2024

https://github.com/sass/sass/blob/stable/lib/sass/importers/base.rb is the Ruby API.

from libsass.

mgreter avatar mgreter commented on August 15, 2024

Unfortunately most information is scattered around in various issues. I'll try to explain the basics.

You get called for every import statement libsass sees (and get passed the url). If you return NULL, libsass should treat it as if the custom importer would not have existed. Alternatively you return a import_list back to libsass, which can have zero or more import_entry items. Each of this entry can have a file_name, a source_content and a source_map_content.

  1. If only the file_name is given, libsass will try to load it as any normal import statement (so I guess it will also use the same lookup methods as libsass normally does, like resolving partials etc.).
  2. If the content is also given, we will skip the loading from disk and use that data instead.
  3. The source_map is already there, as I'm planning to implement to map the resulting source map back (I already do this in another project and it works pretty well, but code needs to be ported from perl to C++, which I plan to do in a sperate project for now).
struct Sass_Import** sass_importer(const char* url, void* cookie)
{
  struct Sass_Import** incs = sass_make_import_list(2);
  incs[0] = sass_make_import_entry("virtual.scss", strdup(source), 0);
  incs[1] = sass_make_import_entry("include.scss", 0, 0);
  return incs;
}

I hope this clears it up a little? But full ACK that the documentation could still need a lot of more examples and more detailed information, but I hope it's still better than what we had before ;)

from libsass.

chriseppstein avatar chriseppstein commented on August 15, 2024

I don't see how this scheme handles relative import resolution.

from libsass.

mgreter avatar mgreter commented on August 15, 2024

I don't think I understand the concerns correctly? If you pass back a relative path to libsass, it should take care of that, as it should still see that import within the context of the current file (or how is it different from normal relative import resolution libsass already does?). But maybe you mean that the implementor would have to know what the current file beeing processed is, so it can actually make the imports relative from there? Not sure if this is already possible ...

from libsass.

chriseppstein avatar chriseppstein commented on August 15, 2024

So if a file imports "foo/bar/baz" and then that file imports "asdf" then the file that should be imported is "foo/bar/asdf" if such a file exists otherwise a file "asdf" on the import path should be looked for. With importers the concept of a file is abstract, so there's a relative resolution that is passed the import statement as well as the current filename. filenames and import statements are not necessarily filesystem paths, they can be any string that the importer understands.

from libsass.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.