grass icon indicating copy to clipboard operation
grass copied to clipboard

Why Grass & Rust framework integration

Open MartinKavik opened this issue 4 years ago • 23 comments

Hi @connorskees,

I'm a core member of the frontend framework Seed. I'm looking for a SASS library that I can integrate into Seed projects and yours looks like a good fit. Could you answer these questions please:

  • What's the difference between Grass and other pure Rust SASS libs like rsass?
  • Do you plan to implement all SASS specs?
  • When do you think we will be able to use Grass to compile, for instance, Bootstrap (4)? (Just rough guess (weeks, months..))

Thank you very much!

P.S. You are very welcome to join our chat and forum (links on the Seed website), we can help at least with testing your library with WASM framework.

MartinKavik avatar Jan 29 '20 17:01 MartinKavik

Hello @MartinKavik! Thank you for your interest in this project.

For right now, I cannot recommend this library over other implementations. grass is only about a month old and does not really compare to a more mature Rust implementation like rsass, which was started in 2016. I believe rsass is the only other actively maintained implementation written purely in Rust.

I am planning on implementing the entire Sass spec. I am currently focused on just achieving the bare minimum of functionality, especially the Sass features that I use the most -- the indented syntax for example is a very low priority.

I've taken a look at the features used by Bootstrap 4 and I think about 2 months is a decent though rough estimate. I think it mostly depends on how much time I have after school to work on it.

I am interested in knowing your perspective: what kind of API are you looking for from this library? I currently expose a single struct with methods to write CSS to a buffer. Would you want the internals to be exposed as well (e.g. a Unit struct to represent CSS units) or is the primary use case just compiling to CSS?

connorskees avatar Jan 29 '20 23:01 connorskees

Bootstrap 4 and I think about 2 months is a decent though rough estimate

  • Very nice!

what kind of API are you looking for from this library?

  • Context:

    • We have two quickstarts (aka project templates for Seed) - the first one uses only Rust, but it's too simple and many important features are missing; The second one uses Webpack and PostCSS to compile Tailwind and generate Rust file with typed CSS classes and there are some JS scripts e.g. for prerendering.

    • I want to write a new Rust-only quickstart as a superior alternative - basically much simpler Rust Webpack. And I'm starting to write Rust library for Bootstrap next week.

  • So I want to use your library mainly as a part of the build pipeline - for this case I don't need to use any internals (I think).

  • However if you would be able to expose API for CSS parsing (or extract it into another crate) - it would be amazing to use it as an alternative to PostCSS - I want to generate typed CSS classes, see example in my PostCSS plugin - basic_input_data.css => rust_generator.basic.expected_output

MartinKavik avatar Jan 30 '20 09:01 MartinKavik

Hi @MartinKavik! After just a month of work, grass is now passing 1,217 out of 5,093 sass spec tests on a default run.

The major features remaining (listed in roughly the same order I intend to triage them) are:

@if @else
@while
refactor value parsing of lists
maps
@each
unit arithmetic
special case certain functions (min, max, calc, element, expression, progid, url)
variadic arguments
builtin selector functions
builtin list functions
builtin string functions
@extend
indented syntax
dedicated global scope
implement a special parser for plain css
@content
@use and module system
@forward

grass is still a decent ways away from being able to compile Bootstrap. Bootstrap 4 relies on @extend in 14 places, so at the very least we cannot compile Bootstrap without implementing @extend. I think 2 months might have been a bit optimistic; however, by the end of March there should still be a respectable number of passing tests :)

connorskees avatar Feb 29 '20 21:02 connorskees

Hi, I follow your progress, good job! I'm working on a production app, where we use Bootstrap, let me know once BS is compilable, I can test it immediately 🙂


Do you plan to support both SCSS and SASS? (I use only SCSS, but I'm curious).


I am interested in knowing your perspective: what kind of API are you looking for from this library?

I was thinking about it again. I want to use it in Rust code - cargo-make scripts or bundle.rs or from new Rust dev server (probably Actix).

The primary use-case would be just pick a file (style.scss) and save the output (to style.scss). I can read and save the file by myself, but I assume grass would need to know the file path so it can read imported files (e.g. @import "./bootstrap/bootstrap"). So something like grass::compile(my_file: Path) -> Result<String (aka SCSS), Error) should be enough for me.

Other use-cases would need to work with internals:

  • I want to filter out unused CSS classes in my app to drastically reduce output file size.
  • And generate Rust struct, where fields represent CSS classes and field comments represent CSS properties & values associated to given CSS classes.

So I assume an API similar to PostCSS's one would be needed for similar cases. It would be also the reason to prefer grass over other libraries. And because SCSS is super-set of CSS, grass would become de facto Rust PostCSS...

I don't know grass architecture - would it be possible to write such API (now or in the future)? Thanks!

MartinKavik avatar Mar 01 '20 12:03 MartinKavik

The sass specification currently contains about 27 tests dedicated to testing the indented syntax. I am planning on implementing it; however, it is a very low priority. I don't think it will be that difficult to implement, though -- grass currently differs from the dart implementation in that it includes a tokenization step, which should make the work required to parse the indented syntax as easy as making StyleSheet generic over the lexer.

I think what you're describing as far as an API similar to PostCSS would be incredibly easy to implement. The SASS is already parsed from an AST into a vector of styles before printing, so it would be straightforward to just operate on that linear form before printing. As far as I can tell, it would only be a matter of exposing a struct and writing some methods to emulate their API. In fact, I think most of http://api.postcss.org/index.html is already implemented internally. For example, it should already be possible to write a clone of autoprefixer (though I would not do something like this until this library is more stable).

I think it might be fun to implement a lot of the existing PostCSS plugins natively, though of course that is ancillary to the initial goal of this library and will not be something I focus on for a while.

connorskees avatar Mar 01 '20 21:03 connorskees

Cool!

be a matter of exposing a struct and writing some methods to emulate their API

It would be nice to have access to that struct in the near future. It can be very "raw" and ugly with big note "It will change!" - I would use it internally in my Seeder project so I'll be able to test it and maybe I'll discover some patterns for future official public API.

MartinKavik avatar Mar 02 '20 11:03 MartinKavik

I've just found out that sass-rs doesn't support functions (according to its README and my problems with map.get). Are there some general problems with supporting functions? (Or does grass already handle functions / it's in your todo list?)

MartinKavik avatar Mar 03 '20 15:03 MartinKavik

What kind of function are you referring to?

So far functions have been implemented in

  • @function
  • builtin functions

The module system has not yet been implemented, so I think map.get will not work for a while, but the architecture for builtin functions is very robust and map-get should be available as soon as maps are properly parsed. I've mostly held off on implementing the module system because I don't like writing unit tests for imports -- if it is important I could prioritize it further.

Currently there is no way for library users to write functions in rust and access them in SASS, but that has already been implemented internally. I think it might be just a matter of exposing a few structs and methods. Is that something you would be looking for?

connorskees avatar Mar 04 '20 02:03 connorskees

What kind of function are you referring to?

Probably built-in ones according to your description 🙂 I was just surprised that functions like map.get don't work with the currently most mature SASS library for Rust. So I thought I'll let you know so you can use it for "marketing purposes" and another reason for me to looking forward to grass.

Is that something you would be looking for?

It sounds cool, but I don't know where I would like to use it now.

MartinKavik avatar Mar 04 '20 09:03 MartinKavik

Hi @MartinKavik! After another month of work, grass is now passing 1,719 out of 5,093 sass spec tests. Notable improvements include @each, @while, @if/@else if/@else, maps and their associated functions, !global, @content, unit arithmetic/conversions, builtin string and list functions, a complete rewrite of value parsing, and a complete rewrite of the tokenization step.

My focuses for the month of April will be

variadic arguments (this is blocking some builtin functions)
feature complete string parsing/escaping/interpolation
order of operations
refactoring selector parsing and implementing their associated functions
special casing of min, max, calc, element, expression, progid:..., url
@at-root
@extend

connorskees avatar Apr 02 '20 06:04 connorskees

Hi @MartinKavik! I have an API question.

I've spent the last few days integrating error handling with codemap, and I am very close to something I would consider an MVP.

I was wondering, as a consumer of the library, how would you like errors to be exposed? Right now, errors are tightly coupled with codemap and only accessible by converting them into a string. Looking at sass-rs, it seems they return Result<String, String>.

Would you want structured errors (e.g. exposing line, column, file, message/reason, and span separately as fields of a struct) or would you be fine with being given a string similar to the dart sass error messages,

Only 1 argument allowed, but 2 were passed.
   ╷
34 │   color: random(foo, 2)
   │          ^^^^^^^^^^^^^^
   ╵
  stdin 34:10  root stylesheet on line 34 at column 10

connorskees avatar Apr 12 '20 04:04 connorskees

I would like to display errors:

  • On the browser overlay (something like Webpack's devServer.overlay).
  • In the terminal.

So String should be good enough, but it would be nice to add into that string also path and position to the error: e.g. /my_project/style.sass:34:10.

  • It's the most important thing for the user.
  • Some IDEs (e.g. VSCode) would allow to navigate user by clicking on this path & position to the source code (it's very nice feature that saves time ; I can't find docs for the required format but it shouldn't be hard to test it).

MartinKavik avatar Apr 12 '20 11:04 MartinKavik

As an update to the progress so far, in April we have

  • fully implemented string parsing, interpolation, quoting, and escaping
  • implemented variadic arguments
  • implemented special functions calc, element, expression, progid:..., url
  • implemented order of operations and refactored completely how operations are handled
  • implemented @at-root
  • implemented error messages nearly identically to dart-sass. grass differs, however, in that paths are emitted in a way that vscode can link you to the exact file, line number, and column
  • experimentally compiled the library to wasm and released it to npm
  • completely rewritten control flow at-rules
  • implemented @import to an extent (some issues remain with css imports and there are not nearly as many tests as I would like)
  • made lots of various bugfixes for edge cases (the commit log is probably better to look at if you're interested in these)

To speak more to the error handling, for this input

a {
  color: random(foo);
}

the error below is generated, which in vscode will allow you to alt-click and have your cursor immediately taken to the correct line and column

Error: $limit: foo is not a number.
  |
2 |   color: random(foo);
  |                 ^^^^
  |
./bar.scss:2:17

Error spans and many error messages are largely untested (and, as you can see from the above span containing the closing paren, slightly off sometimes), but I think for right now they are good enough.

We are very close to being able to compile bootstrap. I think the last true blocker is implementing @extend, which is my top priority at the moment. I am in the process of completely rewriting selector parsing, and after that I will be implementing the builtin selector functions and then @extend.

I will probably implement the indented syntax to an extent sometime next week as it is bothering me. I am interested to see how my "novel" idea of emitting { and } tokens instead of indent and dedent will work in practice. I am willing to accept some deviation from the spec here for the sake of allowing code reuse.

grass has caught up to rsass and I believe by the end of May grass will be at a point where you could reasonably use it for projects.

connorskees avatar May 03 '20 03:05 connorskees

@connorskees Are we aiming to be incremental later on such that we need salsa instead of building using our own graph system?

pickfire avatar Jul 26 '20 06:07 pickfire

@pickfire Certainly I've considered salsa (and am experimenting with it in another project of mine), but I think for Sass in particular it might not be a good fit. The import model of Sass combined with @extend affecting output that comes both before and after make it difficult to incrementally compile Sass.

Switching to a query-based architecture would undoubtedly make parsing more complex and cause the codebase to deviate further from that of dart-sass's. The gain presumably would be a bit faster incremental file watching.

I think the best solution in this case would simply be to further optimize the performance of the compiler itself.

I'm not sure what exactly you mean by "our own graph system." Is this in reference to the way that we track changes in files we are watching?

connorskees avatar Jul 26 '20 06:07 connorskees

Yes, that's the one used in dart-sass, I saw that they have ImportCache. Besides, I wonder if switching to async would give benefit for us for parallel compilation.

pickfire avatar Jul 26 '20 07:07 pickfire

@MartinKavik With regard to your earlier comments (in March!) about map.get, grass now has full support for the module system.

@use "sass:map";

a {
  color: map.get((a: b, c: d), a);
}

libsass is currently in the early stages of being deprecated. I will be interested to see what the outcome of that process means for grass.

connorskees avatar Aug 07 '20 07:08 connorskees

libsass is currently in the early stages of being deprecated. I will be interested to see what the outcome of that process means for grass.

Heh, we could add c-bindings to be drop-in replacement for libsass and dart-sass. But I rather see that we have support for graph caching first to prevent re-parsing the same file, but I will look into compressed output before that first.

pickfire avatar Aug 07 '20 07:08 pickfire

@pickfire The next steps are to rewrite output and support merging media queries such that we may add bootstrap, bulma, materialize et al. to the CI. After that we will want to add support for @forward and the indented syntax (which has unfortunate quirks that require special parsing beyond simply emitting indent and dedent tokens). Once those features are supported, we will want to clean up the remaining small features (e.g. (1 / 0) will still panic) and do much more fuzzing.

One of the end goals of this library is to provide a C API. We already have Node compatibility through WASM, though this is a bit rough at the moment and there is not CLI.

connorskees avatar Aug 07 '20 08:08 connorskees

Hey there @connorskees, just wanted to say that the work here is definitely outstanding! Excellent work. The error reporting is quite good as well. Thanks for all the work on this.

thedodd avatar Aug 20 '20 03:08 thedodd

Grass is only about a month old and does not really compare to a more mature Rust implementation like rsass

I'm still interested why you went on creating a new lib instead of contributing to rsass. They could be a lot of reasons, but I'd like to know your motivations, at least back then.

batisteo avatar Dec 10 '20 15:12 batisteo

rsass have different aims, grass aims not to use any parser libraries but rsass uses nom.

pickfire avatar Dec 10 '20 17:12 pickfire

@batisteo this project largely began as a way for me to learn more about Sass. As pickfire said, this project aims to have as few dependencies as possible (and, also, I do not like parser combinators).

connorskees avatar Dec 10 '20 21:12 connorskees

Currently there is no way for library users to write functions in rust and access them in SASS, but that has already been implemented internally. I think it might be just a matter of exposing a few structs and methods. Is that something you would be looking for?

Did this ever happen? If not, are there plans for it to happen? I'm currently building a project using Yew, and being able to use this library for building a sass-based component library would be very useful, but the library appears to need support for this feature to be able to build it.

dacid44 avatar Feb 18 '23 00:02 dacid44

@dacid44 Currently I don't have any plans to expose such functionality through the main grass crate, as I want it to focus on the common case of just needing to compile Sass. This also reduces the number of breaking changes we have to release.

Now that the public API and the internals of this crate are separated into 2 projects (grass and grass_compiler), I could consider exposing a lower-level API that lets you implement built-in functions in rust, manipulate the AST, etc. Such internals would likely have breaking changes often, but that should be fine.

Do you know what sort of functionality you'd want to achieve using builtin functions?

connorskees avatar Feb 18 '23 01:02 connorskees

@connorskees I'm trying to build the blueprint.js sass code for a Yew project. Particularly, there is one function called svg-icon that inlines an SVG file into the CSS (using a URL encoding) and replaces its path colors with a given color. I've tried to do this as a pre-processing step, but anywhere that it's needed to be more dynamic, such as inside a function that's defined in sass, with a parameter, it seems like the only option would be to have the sass compiler itself know about it.

Edit: specifically this function https://github.com/palantir/blueprint/blob/v4/packages/core/scripts/sass-custom-functions.js which in turn refers to this https://www.npmjs.com/package/sass-inline-svg. I think I can reimplement at least the functionality of the function that I need (in fact, I already have, when I tried to run it as a preprocessing step).

dacid44 avatar Feb 18 '23 16:02 dacid44

@dacid44 I've published a new version of grass_compiler to crates.io that exposes more internals and which should make it possible to implement custom functions in rust. Would you be able to try this out and see if it solves your problem?

You can add the custom functions using the Options struct, and you can declare the functions using the Builtin struct.

The current API just exposes what should be the minimum necessary to implement custom functions. If you do experiment with this, please let me know if you run into anything that is impossible to do with the current API.

connorskees avatar Feb 18 '23 22:02 connorskees

@connorskees Sorry I didn't get back here quickly, just saw this email and realized I should probably let you know - it looks like it would likely solve the issues I was having, however, because of unrelated issues with Tauri/WebkitGtk running Yew, we've decided to instead switch to Iced for our GUI solution. Thank you for doing this though! Sorry I wasn't able to try it out on this project, though I'll probably come back to this solution for a future project if/when the Webkit WASM interpreter is fixed.

dacid44 avatar Mar 21 '23 15:03 dacid44

No worries, thanks for letting me know. This is a feature I've wanted to experiment with for a while, so it wasn't wasted work.

connorskees avatar Mar 21 '23 17:03 connorskees

Closing this issue out as grass is the only rust library capable of compiling major libraries (bootstrap, bulma, etc.) and is currently the fastest at doing so, being faster than libsass and dart-sass

connorskees avatar Mar 21 '23 17:03 connorskees