Sass Switcheroo
Sass Switcheroo is a work around to the fact that you cannot dynamically control your imports from within a sass file.
This Sass add-on gives you the ability to define import rules. You can:
- Rewrite import statements
- Specify a file should be imported before or after another sass file.
What is this useful for?
- Making a project work across different versions of libraries
- Managing Sass version differences
Installation
Add this line to your application's Gemfile:
gem 'sass-switcheroo'
And then execute:
$ bundle
Or install it yourself as:
$ gem install sass-switcheroo
Usage
The easiest way to make switcheroo work is in a compass configuration file:
importer = Sass::Switcheroo.rules(sass_path) do
# custom rules go here. These are just examples.
replace("**/setup*", %r{(.*)/setup} => 'sass3.4/\1/setup')
before("**/variables*", %r{.*} => 'sass3.4/variable_defaults')
after("**/variables*", %r{.*} => 'sass3.4/variable_adapaters')
end
add_import_path importer
Declaring import rules
Sass::Switcheroo.rules
- This method takes two arguments. A root
directory within which to find sass imports and and an optional value
for glob options which defaults to File::FNM_EXTGLOB |
File::FNM_PATHNAME
. Docs on ruby globs and the glob options can be found
here.
Import rewriting (The replace
directive)
The first argument is a ruby glob. Import statements that match this glob will rewritten. Note that the import statements are made relative to the import root for the purpose of rewriting, even if they are specified relative to the sass file.
The second argument is a hash of transformations where the keys are iteratively substituted by the values. The keys can be strings or regular expressions. If the regular expression has captures, those can be accessed with the escapes \1 through \9 (This uses the ruby String#sub method) so it's not a global replace. Note that, because of ruby's syntax the hash does not require curly braces when the third argument is omitted.
This directive accepts the :glob_options
option, which will override
the glob option value that was specified globally for the rules.
Replacements can interact with each other. That is, once a replacement is matched, the resulting import will be checked against the remaining replacements.
replace
Examples:
# Given these rules:
importer = Sass::Switcheroo.rules(sass_path) do
replace("**/test-a*", "/test-a" => "/test-b")
replace("**/theme-1/**/*", "/theme-1" => "/theme-2")
replace("**/*-config", %r{^(.*)/([^/]+)-config} => 'config/\1/\2')
end
The following import rewrites would occur:
@import "my-app/test-a-sidebar" => my-app/test-b-sidebar
@import "my-app/theme-1/colors" => my-app/theme-2/colors
@import "typography/font-config" => config/typography/font
@import "tests/theme-1/test-a-config" => tests/theme-1/test-b-config => tests/theme-2/test-b-config => config/tests/theme-1/test-b
Co-imports (The before
and after
directives)
Once rewriting is performed the actual sass file that is being imported is matched for co-imports. Co-imports are import statements that are added either to the top or the bottom of the imported file's contents. Multiple co-imports can end up being injected and files imported as a co-import can themselves have their own co-imports.
Like replace
, the first argument of before
and after
is a glob
pattern. Unlike replace
the glob pattern is matched against the actual
sass file (relative to root directory of the imported file). This means
that the filename will include extension and for partials, the leading
underscore.
The second argument to before
and after
can either be a string or a
hash. When the second argument is a string, this is the import that
should be injected when importing files matching the glob. If the second
argument is a hash, the keys much be regular expressions that can
capture values from the matched file to construct imports. The captures
from the regular expression are referenced in the values of the hash
using the \1
..\9
references. If several key/value pairs are in the
hash, they will all be checked if they match the file. If none of the
regular expression keys match, then no import will be created. Note
that, because of ruby's syntax the hash does not require curly braces
when the third argument is omitted.
Like replace
, these directives allow the :glob_options
option to be
passed if you need to override the glob options specified by the rules
block.
If you want to conditionally peform a co-import if the co-import file
exists, pass :if_exists => true
as an option to the before
or
after
directives.
Caveat: Because co-imports mangle the source of the sass file and inject
@import
directives, the column numbers of the first line (in an scss
file) as reported by sourcemaps and in error messages will be incorrect.
In the indented syntax, the line numbers of sourcemaps and errors will
be incorrectly reported.
before
and after
Examples:
# Given these definitions:
importer = Sass::Switcheroo.rules(sass_path) do
before("config/setup", "config/sass-34-defaults")
after("config/setup", "config/sass-34-config-adapter")
after("app/**/*", %r{/_?([^/]+)\.s[ac]ss$} => 'config/app/\1', :if_exists => true)
end
The following co-imports occur:
@import "config/setup"
is equivalent to@import "config/sass-34-defaults"; @import "config/setup"; @import "config/sass-34-config-adapter"
@import "app/foo"
is equivalent to@import "@import "app/foo"; @import "config/app/foo"
if the import ofconfig/app/foo
exists.
Miscellaneous Notes
- Since
\
is an escape in a double quoted string("..."
), it is usually better to use a single quoted string ('...'
) which treats\
as a literal backslash. - Since
/
is a regular expression delimiter it is easier to define regular expressions involving/
using ruby's%r{...}
syntax instead so that/
doesn't have any particular meaning and doesn't need to be escaped. - It is possible to create co-import loops so be careful to avoid that unless you have import-once enabled.
Contributing
- Fork it ( http://github.com/
/sass-switcheroo/fork ) - Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request