rails / sass-rails

Ruby on Rails stylesheet engine for Sass

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Sass @include load path priority regression between v3.2.6 and v5.0.6

garethrees opened this issue · comments

I'm upgrading an application from Rails 3.2 to Rails 4.0 and have run in to an issue around sass @include load path ordering.

In our app we have some "core" styles defined for layout and basic font sizes etc (in app/assets), and we then apply a "theme" for different installs of the app (which gets installed in to lib/theme.

Here's a simplified sass-only example tree and some notes about what each part does.

stylesheets/
├── core             # Main layout styles
│   ├── all.scss     # Main stylsheet containing `@include 'custom';`
│   └── custom.scss
└── theme            # Theme overrides
    └── custom.scss  # Should take priority over core/custom.scss

I've written a simple test case to prove this works in sass 3.4.21 – the version shared by both the Rails 3.2 branch and the Rails 4.0 branch of our main app.

Its pretty simple – you just put the theme directory higher in the load path:

# ./test.rb
paths = %w(theme core).map do |dir|
  File.expand_path(File.dirname(__FILE__) + "/stylesheets/#{ dir }")
end

On to Rails…

Again I've made a sample test case that demonstrates the problem.

Here's a snapshot of the tree with some notes

├── app
│   ├── assets
│   │   └── stylesheets
│   │       ├── all.scss            # 3. Main sass file for importing view-specific styles, including custom
│   │       ├── application.css     # 1. Main manifest; requires main.scss
│   │       ├── custom.scss
│   │       └── main.scss           # 2. Just contains `@import "all";`
│   ├── initializers
│   │   ├── theme_loader.rb         # Just requires lib/theme/theme.rb
└── lib
    └── theme
        ├── app
        │   └── assets
        │       └── stylesheets
        │           └── custom.scss # Should override app/…/custom.scss
        └── theme.rb                # Prepends lib/theme/app/assets/stylesheets to sprockets load path

The only notable bit is in lib/theme/theme.rb we prepend to the assets load path:

theme_asset_path =
  File.expand_path(File.dirname(__FILE__) + '/app/assets/stylesheets')

Rails.application.config.assets.paths.unshift theme_asset_path

In Rails 3.2 this produces the desired behaviour. It does not in Rails 4.0+.

The override behaviour works correctly with images, javascript and non-scss-included stylesheets.

I'm pretty sure the issue is with Sass::Rails::SassImporter#find_relative, as that only ever looks relative to the base directory of the file containing the @include. Sass::Rails::Importer#find_relative seems to consider the load path, but I'm not sure whether this is a regression or if the behaviour we're using was a bug in the first place.

I've had a go at creating a test against working (v3.2.6) and non-working (v5.0.6) versions of sass-rails:

  • v3.2.6 – seems to pass my addition to test "sass imports work correctly", but I can't get the full suite to pass.
  • v5.0.6 – fails, but again can't get a reliable environment working to really be able to demonstrate this.

I'm happy to dig in to this, but it would be great if someone could have a look at my examples and let me know if its something that I should expect to work, or whether e.g. I should be doing something different to get the desired behaviour.

This ended up being the same issue as spree/spree#3415 (comment)

commented

@garethrees I ran into a similar issue with the same intention (having a default scss file and a way to customize it).
The fix for spree seems to have been to specify full paths... however my files live in the toplevel of app/assets/stylesheets and the customization dir just like yours. So how did you fix this in the end?

mysociety/alaveteli@6b70927 was the commit that fixed the problem. We needed to make the paths relative to the stylesheets directory, rather than relative to the directory of the file using the import.

commented

@garethrees Thanks a lot. Figured it out now. It only works with absolute paths that cannot be found as relative paths... First i had this in my app/assets/stylesheets/application.scss:

@import 'custom/head'

It would still pick up app/assets/stylesheets/custom/head.scss - the path would be interpreted as a relative one.
Only when i moved that import into app/assets/stylesheets/custom/import.scss it would pick the one from the prioritized asset_path.

I think i will work around this by having application.scss in the customization paths and import the defaults there. That way all relative paths will default to the customized ones as well.

commented

created a new issue for the underlying problem... #406