dartsass-rails
dartsass-rails copied to clipboard
Migration from `sassc-rails` workarounds
It would help enormously if we had better documentation on the incompatibilities with sassc-rails
and known workarounds for them. I'm willing to create a README.md
or docs/migrating.md
PR, but I'd need guidance (and I can't promise when I'll get around to creating it).
Following are my initial thoughts start towards what might be needed in a docs/migrating.md
, and the workarounds that I used. 🙂 You can maybe consider it a "rough draft". I would appreciate advice on my workarounds: Can they be simplified or improved? Are they factually inaccurate (I did test them all, but there might be something abnormal in my testing env)? Which ones deserve to be included in a documentation PR? What am I missing?
Glob imports
Imports need to be explicitly enumerated. A simple pattern is to convert all @import "foo/**/*";
glob imports into @import "foo";
and add a new foo/_index.scss
file which contains explicit imports for all of the globbed files. If foo/_index.scss
already exists, @import "foo/glob";
and foo/_glob.scss
could be used instead.
This can be tedious and error-prone to do by hand, so it would be nice to add a find|awk
bash one-liner to the documentation. Even nicer would be a dartsass:migrate-globs
task.
Require directives
Although sass-rails
and sassc-rails
both process the sprockets require
directives in .scss
files, dartsass-rails
does not. I found several different workarounds for this. The first two should work with sprockets
or propshaft
, but the others require sprockets
.
Convert to @import
Fortunately, dartsass-rails
adds all gem asset paths as load paths for dartsass
. So, if the gem is only using basic css or scss, we can generally replace //= require "foo"
with @import "foo";
Unfortunately, when the gems themselves rely on sprockets features, this won't work. For example: jquery-ui-rails
uses sprockets require
directives. And font-awesome-rails
uses erb to process a .css.erb
file. Other gems may have other incompatibilities.
Convert to node packages
In many cases, a node package exists for the library in question. In that case, the simplest approach may be to convert from the bundled gems to the node package. The @import
statement may need to use the full path to the stylesheet, relative to node_modules
. E.g: //= require "jquery-ui"
might be converted to @import 'jquery-ui/dist/themes/base/jquery-ui.min';
. Alternatively, the path to the node package's dist
directory could be added to config.assets.paths
.
However, in some cases, the node packages require non-trivial changes to the application code, or the gems are significantly simpler to use, or the gems come with additional functionality such as image assets and helper modules. In those cases, the next two workarounds will probably work.
Use a sprockets css entrypoint to require dartsass build output
Add a prefix or suffix to the entrypoint with the problematic require
directives. Add this new file to the config.dartsass.builds
hash. Remove the sprockets requires from the top, to avoid confusion (optional, they are comments so they're simply ignored).
Create a new .css
file with the same base name as the original entrypoint (only changing the extension from .scss
to .css
). Add this file to either config.assets.precompile
or manifest.js
. Copy all of the original require
directives into this file (nothing else). Add a require
directive for the renamed .scss
file at the bottom of this css file--or if require self
was used, replace that. Once dartsass has run and written to app/assets/builds
, the new sprockets entrypoint should be able to work just like before.
Use new entrypoints and new stylesheet_link_tags
Convert the = require
that work into @import
. The problematic imports can mostly be discovered by simply converting them all and then commenting out the ones that don't work until dartsass:build
runs without errors.
- For each problematic import:
- add the built name to
config.assets.precompile
. Sprockets precompiles these entrypoints using the asset paths and manifests of all loaded rails engines (gems). - Above every
stylesheet_link_tag
for the existing entrypoint, add astylesheet_link_tag
for each of these new entrypoints.
- add the built name to
- CSS ordering is significant and this moves the sprockets processed stylesheets to first. If that breaks the styles, create more entrypoints in either dartsass or sprockets as needed.
Examples
The examples are based on the following hypothetical broken scss:
// app/assets/stylesheets/admin.scss
/*
*= require "bootstrap"
*= require "bootstrap-responsive"
*= require "font-awesome"
*= require "jquery-ui"
*= require "something-else-etc-etc"
*/
.my.awesome.scss.styles {}
.etc .etc .etc {}
# config/initializers/assets.rb
config.dartsass.builds = { "admin.scss" => "admin.css" }
<%# app/views/layouts/admin.html.erb %>
<%= stylesheet_link_tag "admin", media: "all" %>
Use a sprockets css entrypoint to require dartsass build output
The example could be converted to something like the following:
// app/assets/stylesheets/admin.css
/*
*= require "bootstrap"
*= require "bootstrap-responsive"
*= require "font-awesome"
*= require "jquery-ui"
*= require "something-else-etc-etc"
*= require "admin-scss"
*/
// EOF
// app/assets/stylesheets/admin-scss.scss
.my.awesome.scss.styles {}
.etc .etc .etc {}
# config/initializers/assets.rb
config.assets.precompile += %w[admin.scss]
config.dartsass.builds = { "admin-scss.scss" => "admin-scss.css" }
The erb would be unchanged.
Use new entrypoints and new stylesheet_link_tags
The example would be converted to something like the following:
// app/assets/stylesheets/base-layout.scss
@import "bootstrap";
@import "bootstrap-responsive";
// app/assets/stylesheets/admin.scss
@import "something-else-etc-etc";
.my.awesome.scss.styles {}
.etc .etc .etc {}
# config/initializers/assets.rb
config.assets.precompile += %w[
font-awesome.css
jquery-ui.css
]
config.dartsass.builds = {
"base-layout" => "base-layout.css"
"admin-scss.scss" => "admin-scss.css"
}
<%# app/views/layouts/admin.html.erb %>
<%= stylesheet_link_tag "base-layout", media: "all" %>
<%= stylesheet_link_tag "font-awesome", media: "all" %>
<%= stylesheet_link_tag "jquery-ui", media: "all" %>
<%= stylesheet_link_tag "admin", media: "all" %>