sass / sass

Sass makes CSS fun!
https://sass-lang.com
MIT License
15.06k stars 2.15k forks source link

Packaging of SassScript functions outside of Ruby environment #803

Closed Snugug closed 6 years ago

Snugug commented 11 years ago

Currently, if I wanted to create some Sass functionality that required dipping into Ruby outside of a Ruby project (such as for use with a stand alone or PHP project), the only way I can do that is through a Compass extension (thus tying my code to Compass, which I love, but not everyone uses). Is there a way for me to package SassScript functions into files that I can import/can be used by the vanilla Sass parser without requiring Compass? If not, may I request the ability to do so? Something like the following:

# my-ruby-awesome.rb
module Sass::Script::Functions
  def reverse(string)
    assert_type string, :String
    Sass::Script::String.new(string.value.reverse)
  end
  declare :reverse, :args => [:string]
end
@import "my-ruby-awesome";

$string: "foobar";

#foo {
  content: reverse($string);
}

Having this as an option would allow us to package and others to consume some of the more advanced pieces of Sass functionality out there easier to do.

chriseppstein commented 11 years ago

I like the idea of declaring a host-language dependency from a sass file. I'm not sure that @import is the right place for such a thing. What's more, such a construct would imply that the functionality is only available during the compilation of the sass files into which this import called which is doable, but tricky.

Snugug commented 11 years ago

Maybe an @require syntax?

chriseppstein commented 11 years ago

I'm less hung up on the name of the directive than understanding how this would work and what it implies for the ecosystem. E.g. Would ruby files be allowed to be placed in any sass load path location? Ruby files are inherently insecure so if we allow people to distribute ruby code, more easily, maybe we should sandbox them? How would such a feature integrate with other sass implementations?

nex3 commented 11 years ago

I'm also uncomfortable with the security implications here. We'd have to be very confident in whatever sandboxing solution we use to ever do this.

chriseppstein commented 11 years ago

Ruby has $SAFE levels. But still, there's things like compass sprites which need filesystem access.

To be clear the scenario I'm thinking about is one where we allow ruby files to be loaded by an importer, then we evaluate that ruby code in process. If there was an importer that loaded files from the internet, this could quickly become an attack vector for services that offer on-the-fly sass compilation. The idea is a powerful one, but it needs to be well thought out.

robwierzbowski commented 11 years ago

+1 +1. This would bring one of the most useful features of Compass into Sass, and let people experiment with possible language extensions in real world situations.

Breaking the reliance on Compass for any extension that is vanilla Sass except for one or two custom Ruby functions would be fantastic.

chriseppstein commented 11 years ago

TBH, I'm not sure that I want to increase the exposure of ruby as part of "Standard Issue Sass".

robwierzbowski commented 11 years ago

Is Compass's method of securing custom SassScript portable to Sass?

I think what we're looking for is a way to extend the language as we would in Compass, but without being dependent on Compass itself. Most of the @Team-Sass extensions could be vanilla Sass, and be more useful to the community as a whole, if there was a way to add custom SassScript functions at this level.

nex3 commented 11 years ago

Compass's security model relies on users configuring which Ruby-based extensions they want outside of their Sass files. That's a very different proposition than allowing Sass files themselves to import Ruby.

I think there's room for a non-Compass-based Sass package manager of some sort that handles this sort of thing. I don't think it would need to be part of core Sass.

Snugug commented 11 years ago

The issue with it not being part of core Sass is that it defeats the purpose of wanting a package manager agnostic way of packaging Ruby functionality for use with Sass and in fact make the reasoning behind this issue even worse (a more fragmented extension community). Maybe the answer is as "simple" as moving Compass's extension architecture into Sass? I'm not sure. I would argue, though, that a user explicitly saying that they were importing a Ruby-specific file in Sass is tantamount to what they do in Compass currently; either way it's an explicit want on their part.

nex3 commented 11 years ago

The issue with it not being part of core Sass is that it defeats the purpose of wanting a package manager agnostic way of packaging Ruby functionality for use with Sass and in fact make the reasoning behind this issue even worse (a more fragmented extension community). Maybe the answer is as "simple" as moving Compass's extension architecture into Sass? I'm not sure.

Why does it need to be package-manager-agnostic? If people standardize on a single package manager, why isn't that good enough? Core Sass is a language implementation, and I don't want to make it more than that. Package management is complicated and tricky, and that's not complication I want to add to the main Sass repo.

I would argue, though, that a user explicitly saying that they were importing a Ruby-specific file in Sass is tantamount to what they do in Compass currently; either way it's an explicit want on their part.

There's a world of difference between a user's Ruby-based configuration file importing a Ruby library and a Sass file importing a Ruby library. If Sass files can import Ruby, something that was previously safe -- running sass on user input -- suddenly has the potential to take over your computer.

Snugug commented 11 years ago

@nex3 We tried to standardize on Compass, but what we've run in to is lots of people just don't use Compass, or are moving to Ruby-less Sass compiling systems like Grunt through libsass/sassc. Having a language level way of including 3rd party code would provide a standard language level way of providing advanced functionality.

As for the concern between a Ruby based config file and a Sass file, then maybe the answer is to have an extensions.rb that Sass can read that effectively does the same thing as Compass's require in config.rb.

robwierzbowski commented 11 years ago

Why does it need to be package-manager-agnostic?

Having package manager agnostic extensions is the best of all worlds solution. Compass isn't an ideal way to expose this functionality because it comes with so much extra (although I love some parts of Compass) [edit] and for the reasons Snugug points out above [/edit]. Creating a separate slim extension to import SassScript is an option, but fragments the user base even more — now we have Compass extensions, SassScript Importer extensions, and vanilla, usable everywhere Sass.

If Sass files can import Ruby, something that was previously safe -- running sass on user input -- suddenly has the potential to take over your computer.

That's reasonable. What about a command line option to import a SassScript file?

sass --watch ... --script extension.rb extension-two.rb

That's an explicit choice by the user.

nex3 commented 11 years ago

@Snugug wrote: We tried to standardize on Compass, but what we've run in to is lots of people just don't use Compass, or are moving to Ruby-less Sass compiling systems like Grunt through libsass/sassc.

My understanding is that most people don't use Compass because they perceive it as "heavyweight" (as Rob mentioned). Having a package manager whose sole purpose is managing packages is a different proposition. People don't like requiring Compass to get access to a single compatibility mixin, but with a separate package manager it feels much more like just requiring the compatibility package.

Having a language level way of including 3rd party code would provide a standard language level way of providing advanced functionality.

I don't think it's even possible to do package management at the language level. I'm happy to read proposals, but I've got no idea how you'd (cleanly) support versioning and dependency management within the context of the language. I think it requires a tool with a broader notion of what a "package" is and how they interrelate.

@robwierzbowski wrote: Creating a separate slim extension to import SassScript is an option, but fragments the user base even more — now we have Compass extensions, SassScript Importer extensions, and vanilla, usable everywhere Sass.

I can't speak for @chriseppstein, but I suspect he'd be supportive of migrating Compass and its extensions to a separate package-management system. I'd be supportive of including some support for such a system in the sass command-line tool. If we coordinate our efforts and if the package manager can effectively address the use cases it's trying to, I don't think fragmentation is something we need to worry about.

That's reasonable. What about a command line option to import a SassScript file?

sass --watch ... --script extension.rb extension-two.rb

That's an explicit choice by the user.

The --require flag has done this since Sass 3.0.13.

One more thing I want to mention: as various Sass ports are maturing, people are getting more concerned -- and rightly so -- about the compatibility of Ruby-based extensions. At the same time, many of the things that Ruby extensions do, such as file IO, aren't likely to ever be natively supported in core Sass. Having a notion of packages and a package manager that's separate from the Ruby-based Sass presents one possibility for handling this. In the same way that Rubygems supports natively-compiled packages for multiple platforms, a Sass package manager could support a single package with implementations in several different languages -- Ruby, PHP, C, and one day JavaScript.

robwierzbowski commented 11 years ago

Thanks for giving point by point input on this, Nathan. I appreciate the thoroughness.

I'm personally unconcerned about package management — Bower handles everything I need for Sass/Compass extensions.

I wasn't aware of require; I'll have to play around with it. If it works the way I'm guessing I think adding a Sass extension directory structure with a default require directory (like Compass' lib) would cover @Snugug's original request.

chriseppstein commented 11 years ago

Compass is "heavyweight" because the problems it tries to solve are hard to solve and would probably be harder or impossible to solve independently.

  1. Tools for compiling your project's stylesheets if you don't have a better, more integrated way.
  2. Provide the glue between Sass and application frameworks to give a more integrated compilation experience (or make it possible to build and distribute such a glue for a particular framework)
  3. Provide a single way to package and distribute sass stylesheets so that they will work in any application context.
  4. Provide core mixins and functions that would be reproduced in likely incompatible ways if left to the ecosystem at large.
  5. Provide a single way to install sass packages into application.
  6. Provide best practice tools for managing stylesheets over the course of a project's lifecycle (E.g. image-url())
  7. Some sugar (mostly sprites) to increase the install base to make compass worth installing even without all the other benefits so that we can bootstrap the ecosystem.
  8. In the future, compass should work with any feature-compatible Sass implementation.

I think that items 2,3,4, and 5 above are inseparable. Without these things you do not achieve a vibrant ecosystem -- it becomes fragmented and we get silo's of sass users with different dependencies largely isolated by language/app framework. There's no reason Sass should end up in that state and I started compass with the vision that we would not.

I think most people think that compass is mostly items 4 and 7 and they think 2,3 and 5 are just cruft that is in the way and different from what they are used to. But I want there to be a single, simple way for every sass developer to give their code away and get it into the hands of every other sass developer out on the internet.

That said, if there is a better way to accomplish the goals I've set out here I want to know what that is. I want to make Sass as widely available and the ecosystem as vibrant as it can be. If destroying compass and rebuilding new, better replacement(s) is the way to accomplish that, then I would do that, but evolution is usually better than revolution.

Every language community eventually settles upon a standard library and package manager because doing so is an essential role in community building. If we don't have one, our fledgeling sass community will break down.

So I think we should double-down on compass. I know I am biased. We should identify the pain points and improve compass to the point where people don't avoid it. We should make it easy for people who would not otherwise know how to publish their Sass code and distribute it to every possible application framework and software installation tools. We should make it easy for people to find Sass modules built on compass so that they see the point. We should improve the compass docs so that the features of compass grows with their understanding of their needs instead of appearing to be daunting and "too much for me".

I made compass with the primary goal of getting Sass into the hands of more people and I mean it when I say this remains my goal to this day. Compass is a means to an end. I think we all share a vision for that end and so I think we should either rally around that means or make a very clear decision about what other means gets us to that end as quickly as possible.

scottkellum commented 11 years ago

@chriseppstein I agree on rallying around Compass and @robwierzbowski’s work has provided a means to use Compass in tandem with Bower. I was a hold out for a while on Compass and I think Compass needs to be branded better as a meta framework instead of a collection of CSS3 mixins. @nex3, maybe the Sass site can push harder for Compass as it is not mentioned anywhere ont he Sass home page? A lot of people probably use Sass yet have no idea what Compass is.

While I’m happy to see a project like libsass take on the very real issues of performance it is also what worries me the most. There are things that need to be written in Ruby in order to remain efficient like exponents and string parsing. I see no way to efficiently support libsass when we have to go to a deeper language to handle more complex things. I get it, we are going way outside the box with these features and maybe the solution is that we need to work harder to stay within the sandbox of Sass.

cimmanon commented 11 years ago

Gradients were what finally pushed me to use Compass. Creating a simple gradient mixin is easy, but combining it with multiple backgrounds is not. Extensions are what finally cemented my need for Compass (copying/maintaining my toolbox of CSS stuff got old). The ability to find Compass extensions is sorely needed.

In my mind, Compass has 2 aspects: Compass the extension (the css3 mixins, helper functions, etc.) and Compass the extension manager. It makes sense to me that the extension manager portion should be a part of Sass. The need for Compass the extension won't go away.

chriseppstein commented 11 years ago

@scottkellum It's been a marketing challenge to convince someone to install a "meta-framework". Compass's initial branding was very much along those lines but no one knows what a "meta-framework" is or why they need one. Instead, we've taken the approach of making Compass compelling in-and-of itself but to "subversively" provide all the things we need as a community to get to a vibrant ecosystem. Honestly, it would be easier for most people to think of Compass as "Sass for your project" and Sass as "Just the barebones Sass Compiler".

I think libsass is a Good Thing™ but I agree that it also has a fragmenting force on the community and it's existence necessarily forces us to consider broadening the language's core capabilities to minimize the need for native extensions.

@cimmanon While, I agree that compass could be, in essence, 2 or even 3 distinct projects. What would be the value in separating them? It's a lot of work, it makes it harder to get started with sass. I'm not completely opposed, but given the effort, I think a compelling argument is needed.

cimmanon commented 11 years ago

@chriseppstein What's the value in keeping them together? Just the work involved? Portions of Compass have become standard features of Sass in the past, haven't they? Extensions just seem like they should be a natural feature of Sass.

chriseppstein commented 11 years ago

@cimmanon It's true, many features of compass have found their way into Sass. That said, the boundaries of Sass's features are very clear right now: The language and basic tools for compiling Sass files. Package management has traditionally been a language concern only to the point where the necessary hooks and environmental capabilities are provided. From there, it is usually a separate project: Perl has CPAN, Ruby has gem, Node has NPM, Java has maven, Python has pip. As far as I know, only Go has built package management into the language. Maybe this is a case of everyone is doing it wrong. but why move package management into Sass core and leave the std library of compass out? The only argument I really see is that they do not have essentially linked release schedules and that users may want to install new compilation and dev tools without changing their compilation dependencies. But so far, I do not hear that pain from our users.

robwierzbowski commented 11 years ago

There's lots of issues being discussed here; hopefully sectioning this post will avoid any conflation.

Sass Packages

Package management has traditionally been a language concern only to the point where the necessary hooks and environmental capabilities are provided. From there, it is usually a separate project

Exactly. Until proven beyond any doubt, package management is a project concern. Defining packages can be a language concern — it's Sass's job to standardize what an extension is, and to make it easy to consume in a project.

Compass has vetted a highly successful package format. We know it works, we know people are building great tools with it and other people are easily incorporating those tools into their projects. For many tools the ability to add custom Ruby functions is an essential part of that package — for complex processing, to add new features, or for performance reasons.

Most of the developers in this issue would like to see Compass's package definition (and Ruby exposure) added to vanilla Sass. The benefits include:

Package Management

This is a more complex issue for sure. There seem to be two contexts for Sass package consumption: an integrated Ruby/Rails project and a front-end project that uses Sass as a tool. I don't have any experience using Compass in a Rails project, but this is what I can add from my perspective:

[edit] When I wrote this I wasn't super familiar with how Compass works in a Rails project. After learning more I think the distinction is mostly moot. RubyGems and Bower both would manage Sass packages equally well. Compass is only needed for bundling templates/boilerplate.

Bower for Sass-package-as-component projects

In a Sass-package-as-component project, Sass extensions are just front-end components, the same as any JavaScript, CSS, image, font, or CoffeeScript component. @Bower manages all of these packages lightly, has a friendly user interface, and is a fixture in the JavaScript tooling community that's being embraced by the larger front-end community at a rapid pace. It also works with Sass and Compass extensions right now.

I think there's a good case for Sass to have an official scheme or recommendation for package distribution by Bower, even if it's not the only package manager. Bower is aggressively single task — from what I know it can't replace everything Compass does in a Ruby/Rails project, but if I'm using Bower on a project I'm going to use it to manage all of my front-end components, including Sass.

Compass as a library

Why move package management into Sass core and leave the std library of compass out?

There's still a clear line between language and library. Compass is a huge part of the Sass community, enough that in many developer's minds the distinction between the two is blurred. But competitors and alternatives exist, and I think it's important that Compass stands on its merits as a framework/library without hindering those that want to try new solutions to the same problems.

Take Bourbon by @thoughtbot, an alternate Sass library, or @AI's Autoprefixer, which I've been testing and love. Projects like Autoprefixer make me think the future of Sass could be just programatic CSS construction, tools, and style mixins. I personally don't ever want to use another CSS3 compatibility mixin again.

CSS preprocessing is still in its adolescence. I don't think we can say any library-level problems are definitively solved, but we can use language extensions tested in libraries to strengthen Sass itself (if that foggy distinction makes any sense).

robwierzbowski commented 11 years ago

Some more thoughts after mulling this over for a month:

Components/packages vs. templates/boilerplate

Components are managed by package managers. They have an api or interface with which to to use them, but can't be edited, revised, or altered in any way downstream. Most Sass tools and Compass extensions are components.

Templates are files that downstream users use as a starting point for a project. If a file is meant to be edited, added to, or otherwise moved outside a package manager's ability to update, it ceases to be a component and becomes a template. Compass's templates/patterns are template code.

This doesn't address the above discussion directly, but I think it's important to define as we talk about PMs.

Compass isn't a package manager

Compass does not manage packages. It defines the package, enables a developer to consume a package with Ruby extensions, and bundles templates. But there are no compass install, compass up, or compass register commands.

RubyGems manages Ruby packages and is the current most popular PM for Sass/Compass. Bower manages front-end components and is a working alternative. So the question becomes: Are Sass/Compass components Ruby packages or front-end packages?

I classify my Sass packages as front-end components, and use Bower to manage them. I think there's a good argument that it's the package consumer's choice, based on how their project is architected.

Although I definitely hear Chris's reasoning about standardizing on a single package manager, I think it's possible to passively support both. In order to do that well the definition of a Sass package has to be defined by Sass itself.

nex3 commented 6 years ago

As Ruby Sass is no longer the primary implementation of Sass, there are no plans for this.