Dotless MVC bundling issue when importing same less file twice - css

Im trying to accomplish following structure using dotless:
styles/variables.less - contains, well, all variables like below
#color:green;
styles/component1.less - some random component specific style which imports variables.less
#import "variables";
body {
background:#color;
}
styles/component2.less - some more styles which also imports the global variables.less file
#import "variables";
a {
color:#color;
}
BundleConfig.cs - declaring the bundle like below. Im using this bundling addition: https://gist.github.com/benfoster/3924025
bundles.Add(new Bundle("~/styles/css", new LessTransform()).Include("~/styles/component1.less", "~/styles/component2.less"));
Everything works fine when Debug is set to true
But When Debug is set to false
Only the first file passed in Include method of bundle resolves #import "variables". The rest just fail.
Below is the output of declaring "~/styles/component1.less" first
bundles.Add(new Bundle("~/styles/css", new LessTransform()).Include("~/styles/component1.less", "~/styles/component2.less"));
Output when "~/styles/component2.less" is declared first
bundles.Add(new Bundle("~/styles/css", new LessTransform()).Include("~/styles/component2.less", "~/styles/component1.less"));
Strangely - it works if i import different files in component1 and component2
For instance, if i rename "varibales" to "variables.less" in either file just to make those imports look a bit different. It works. Like below
styles/component1.less
#import "variables.less"; // added extension here
body {
background:#color;
}
Any thoughts? Ive been fidling with this for days..
Edit
Reasons for using this structure:
To send seperate less files in debug mode, as it makes its easier to debug. Line number comments aren't very helpful
To concatenate and minify all the less files when served in production.
Adding #import "variables" on top of every file is ugly.
So, tried declaring variables.less as part of .Include("variables.less", file-dependant-on-variables.less, ...)
Which apparently doesnt work because of some scoping issues mentioned here: Dotless - Can't reference less variable in separate file with MVC Bundling
There is a fix for that, concatenating contents of every single less file and use Less to parse that concatinated file instead. Example here, https://groups.google.com/forum/?fromgroups#!topic/dotless/j-8OP1dNjUY
But in that case, i dont seem to be able to get minified version of the parsed file.

According to the docos:
In v1.3.0 - v1.3.3 #import imports a file multiple times and you can
override this behaviour with #import-once.
In v1.4.0 #import-once has been removed and #import imports once by
default. This means that with the following
The second import of variables is ignored and the variable #color:green; is only defined in the scope of the first component; being undefined in the scope of the second component it would probably end up with the rule or even the entire less file ignored (I'm not that intimately familiar with the default behavior, you could just add in extra properties and rules and see what happens). You could confirm this by inspecting preprocessor logs or otherwise allowing its errors to be traced in the console.
Importing variables at a "higher" level into shared scope like #Hans suggested (+1) should fix this. A tentative alternative could be to downgrade the preprocessor to v1.3.0-v1.3.3 so that #import fires multiple times. Since my preference when dealing with css pre-processors revolves almost exclusively around their variable and mixin functionality I myself could possibly see this as an acceptable option.

I might be missing something here since you didn't state why you are trying to achieve this structure, but you could easily avoid the problem AND generate a smaller resulting bundle by rearranging your file structure. Create a 4th less file with the following content:
#import "variables.less";
#import "component1.less";
#import "component2.less";
And just throw this file into the bundler. The bundle will end up smaller because variables.less is included only once instead of twice, and this definetly works with Dotless.

#o.v. is absolutely right. Dotless generates the next error during parsing the second *.less file in the bundle:
variable #color is undefined on line 4 in file
'..\styles\component2.less':
[3]: a { [4]: color:#color;
----------^
If you look at dotless sources you will find CheckIgnoreImport method in dotless.Core.Importers.Importer class which is called for each imported file:
/// <summary>
/// returns true if the import should be ignored because it is a duplicate and import-once was used
/// </summary>
/// <param name="import"></param>
/// <returns></returns>
protected bool CheckIgnoreImport(Import import, string path)
{
if (_rawImports.Contains(path, StringComparer.InvariantCultureIgnoreCase))
{
return import.IsOnce;
}
_rawImports.Add(path);
return false;
}
Currently import.IsOnce value is always true (see dotless.Core.Parser.Parsers class, line 1080). And you don't have opportunity to change this behavior outside the dotless library.

Related

how to can I prevent css code duplication with sass

I am working on a project as front-end developer which has focused on client side performance.
I am using sass(scss), my approach is to convert every section to specific template with specific sass file, so I can import that template whenever I needed. sometimes some templates has divided to several component. in conclusion we have:
template -> every section : import in page
component -> some template part : include with sass mixin if needed
so if you use mixin, codes can be duplicate, as I said before I need 1 css file for every page, but I have solved the duplication of mixins, my approach is:
every mixin has a variable which say, is mixin has included yet or not. if it has included so do not include again. (mixin has #if $mixin_var==false { mixin codes }). so we have these:
template code:
#include component_1;
$component_1: true; // now included, this variable has declared in file _varibale.scss in top of codes
#include ...;
mixin code:
#mixin component_1 {
#if $component_1==false {
// mixin code here ...
}
}
so if I include component_1 again in other sass file, the $component_1 is true, so mixin condition prevents it.
by this method, code duplication has solved yet.
but now I want to convert every template to single css file and finally include in page. so I cannot use variable and mixin anymore and components code duplicate again and again.
is there any approach to solve this? maybe any npm package or some other sass approach...
any help appreciated.

How to use less mixins in meteor with #import and not get multiple definitions

in my current meteor app I have split the less declarations in one file per Controller (iron-router). I have a common file - where I have defined some mixins - which is imported in each less file. My problem is that the classes are imported multiple times in each route.
The file structure is:
mixins.import.less (new names, reference http://docs.meteor.com/#less)
.grid-container {
// something
}
postList.less
#import (once) url('/client/views/mixins.import.less');
postDetail.less
#import (once) url('/client/views/mixins.import.less');
Then in the Chrome inspector I found duplicated everything I have written in mixins.import.less. Is it possible to avoid this double import?
Assuming you want the mixin code at least once in your compiled css (perhaps not, some just want them as mixins, not classes in the css code), then make sure you set it to bring in the "mixins.import.less" file all by itself. Then for all your dependent files using it, do this:
"postList.less", "postDetail.less", etc.
#import (reference) url('/client/views/mixins.import.less');
The (reference) option has been available since LESS 1.5, and will only bring in the code for reference purposes to be used in the LESS file, but will not itself output any css.
Meteor bundles css and js/html resources all together as a single css and a single js file in production.
In development, they are individually served, but still at the same time, during initial page load (first ever request to server)
For less files, a css file is created for each (during development). Since you are importing, what Meteor basically does is create each corresponding css file that each contain the import individually.
And when they are served to the client all together (take a look at the head section of the generated html), you end up with that many copies of the imported style declarations.
Therefore, due to this bundling behaviour of Meteor, you can just keep one copy of your less mixins in a less file, and not import at all, since they are going to be served to the client in CSS form anyway.
Also, it is possible to trick Meteor into bypassing as described in the unofficial meteor faq:
... you can change the extension of the less files to be imported from .less to .lessimport and then change your #import file.less to #import file.lessimport. This will prevent the less compiler from automatically trying to compile all your import files independently, yet still let you use them ...

Meteor bootstrap less mixins not working across files

I am using bootstrap with Meteor, and importing the bootstrap.less files, which is installed in the public folder, through an import command in main.less:
#import "public/bower_components/bootstrap/less/bootstrap.less";
Below it, I can start using the bootstrap mixins such as .clearfix() and text-hide() and they compile fine.
However, when I want to abstract my own less code into a separate file apply.less and import that file back into main.less, which now looks like this:
#import "public/bower_components/bootstrap/less/bootstrap.less";
#import "apply.less";
I now gets an error
=> Errors prevented startup:
While building the application:
client/less/apply.less:10:2: Less compiler error: .clearfix is undefined
This is really strange. Is this an issue with Meteor?
Another thing I found out - if I put this empty mixin definition
.clearfix(){}
at the top of my apply.less file, things will compile fine again.
Has anyone come across this issue before and figured out a workaround?
Rename your second file as apply.lessimport and import it as:
#import "apply.lessimport";
Basically, the less package looks for every file in the directory tree with a ".less" extension and compiles it to CSS individually, regardless of whether the file is being imported by another file.
When it finds a file with a ".lessimport" extension, it adds it to the list of watched files, but does not actually compile or do anything with it.

Page Specific Sass files and Duplication

I am really struggling to structure the Sass files in a Rails 3.1 project to avoid massive duplication ...
I have the following structure, with application.css.scss being imported as part of my main layout:
application.css.scss
- [*= require] main.css.scss
- [#import] 'variables';
- [#import]'bootstrap_overrides';
- [#import]'bootstrap';
- [#import]'base_elements';
- [#import]'superstructure';
- [#import]'modules';
So far so good. All these files are combined by sprockets into a single document. However I then want to modularise my Sass further, with page-specific files or files shared across regions of my site.
So on my GalleryResource#show page I need to use additional Sass files:
resource.scss
gallery_resource.scss
badges.scss
And maybe a css file from a lib:
gallery_lib.scss
These files need to reference a number of the files already imported in application.css. They need to make use of variables defined in variables.css.scss and of mixins defined in bootstrap. So I am forced to reimport these files in each file that uses them, causing massive duplication. I could write a manifest file for each and every page, but this is a maintenance nightmare and still results in duplication and two css files; application.css and page_specific.css.
So what is the solution? Do I need to dispence with application.css and move its imports into each page-specific file? So using the above example I would end up with a single manifest file that looks like this:
gallery_resource_manifest.css.scss
- [*= require] gallery_lib.css
- [*= require] gallery_resource.css.scss
- [#import] 'variables';
- [#import]'bootstrap_overrides';
- [#import]'bootstrap';
- [#import]'base_elements';
- [#import]'superstructure';
- [#import]'modules';
- [#import]'resource';
- [#import]'gallery_resource';
- [#import]'gallery';
- [#import]'badges';
I also attempted to do the same thing you are mentioning when organizing styles. Rails almost pushes you in this direction by constantly generating controller based stylesheets for you. In the end I've opted for adding everything to application.sass. The main advantage to this method is that you only make one request on page load and you don't end up having to re-import code that the user requested on the last page.
My personal opinion would be that if you abstract your CSS well you only need to use one stylesheet as you don't have a lot of view specific code to bring into the stylesheet in the first place. Either way, the alternative (randomly loading up modules based on page needs while you may have done the same on the previous page) doesn't sound like it's solving that problem anyway. Plus you are making more requests for completely new files.
CSS can be kind of tricky to organize. Here is an example of how I would typically organize my styles and also my stylesheets directory. This method works for a single namespace (application) or it can be broken out to work for multiple namespaces if need be (i.e. admin, application).
application.sass
// vendor ----------------------------------------------------------------------
// compass
#import "compass/reset"
#import "compass/css3/box-shadow"
#import "compass/css3/border-radius"
#import "compass/css3/box-sizing"
#import "compass/css3/opacity"
#import "compass/layout/sticky-footer"
#import "compass/utilities"
// grid
#import "susy"
// application -----------------------------------------------------------------
// base
// for mixins, variables and helpers to aid in building
// the application. no styling of actual elements should
// go in base.
#import "base/colors"
#import "base/fonts"
#import "base/mixins"
#import "base/grid"
// layout
// generic site styles. this includes site chrome styles
// such as the header, footer, flash messages, nav, as well
// as common patterns used around the site like forms, type,
// links, etc. the loose idea is that you could take these
// styles and create a similar "layout" but it wouldn't
// include contextual patterns like books, originals, etc.
#import "layout/buttons"
#import "layout/content"
#import "layout/header"
#import "layout/flash"
#import "layout/forms"
#import "layout/footer"
#import "layout/links"
#import "layout/tooltips"
#import "layout/typography"
// modules
// elements used on multiple pages that are loosely tied
// to our domain and data structure. examples are dependent
// on the needs of your site. as a general guideline, all
// modules should be used on multiple pages. elements used
// on a single page should be included in views.
#import "modules/articles"
#import "modules/buttons"
#import "modules/forms"
#import "modules/links"
#import "modules/pagination"
#import "modules/users"
#import "modules/stats"
#import "modules/some_other_domain_specific_styling_unique_to_your_site"
// views
// elements used on a single page. these styles are
// often needed to help put finishing touches on a
// page where modules didn't quite line up perfectly,
// or where a page has completely unique elements.
// these styles could often be abstracted to modules
// but lack a reason to do so. keeping them here helps
// explain their singular usage.
#import "views/welcome/index"
#import "views/settings/all"
#import "views/articles/show"
#import "views/users/show"
Organizing your styles in this manner helps to create a single responsibility for each file (which it looks like you are trying to do as well). I've found that this makes your CSS a lot more maintainable. Also, if you need to add media queries or target a specific browser with a given module it gives you a place to do so that is contextually relevant while still keeping your code legible and separate.
Note, the example above would be used if you only had one namespace. If you had an admin section (or something else) you could stick all of these directories/files inside of an "application" directory and create a similar structure for "admin". This would leave you with the following structure in app/stylesheets.
- admin
- application
- admin.sass
- application.sass
You could even create a structure to share items between the two, but that may or may not be necessary. The point is that you have a lot of flexibility and organization for whatever you may need to do. This same structure can also be used for JS if you were so inclined. This circumvents the issue of reloading modules and making new requests.
FWIW, I tried to find a solution to bringing in only what I need per page (your question) but in the end I just ended up passing more bits on the whole while simultaneously increasing my number of requests. YMMV.
Hope this helps to get you on your way!
I solved this by taking a cue from rspec, and creating a _sass_helper.sass partial, which contains all my non-direct-style imports (variables, mixins, placeholders), which is then included at the top of each file. The non-code bit is important, because if _sass_helper.sass contained any styles, they would be written into each file.
This effectively allows me to get the same "Sass environment" for each of my files, without any code duplication.
I have my tree organized like so:
includes/
_variables.sass
_mixins.sass
_extends.sass
posts/
_post_partial_1.sass
_post_partial_2.sass
application.sass
posts.sass
_sass_helper.sass
Then, something like posts.sass would look like:
#import 'sass_helper'
#import 'posts/post_partial_1'
#import 'posts/post_partial_2'
.some.globally.shared.post
styles: here
It works great. The post partials don't need the sass helper, so I end up with one master file per controller, with partials for individual features, and the same environment everywhere.

Import regular CSS file in SCSS file?

Is there anyway to import a regular CSS file with Sass's #import command? While I'm not using all of the SCSS syntax from sass, I do still enjoy it's combining/compressing features, and would like to be able to use it without renaming all of my files to *.scss
After having the same issue, I got confused with all the answers here and the comments over the repository of sass in github.
I just want to point out that as December 2014, this issue has been resolved. It is now possible to import css files directly into your sass file. The following PR in github solves the issue.
The syntax is the same as now - #import "your/path/to/the/file", without an extension after the file name. This will import your file directly. If you append *.css at the end, it will translate into the css rule #import url(...).
In case you are using some of the "fancy" new module bundlers such as webpack, you will probably need to use use ~ in the beginning of the path. So, if you want to import the following path node_modules/bootstrap/src/core.scss you would write something like #import "~bootstrap/src/core".
NOTE:
It appears this isn't working for everybody. If your interpreter is based on libsass it should be working fine (checkout this). I've tested using #import on node-sass and it's working fine. Unfortunately this works and doesn't work on some ruby instances.
This was implemented and merged starting from version 3.2 (pull #754 merged on 2 Jan 2015 for libsass, issues originaly were defined here: sass#193 #556, libsass#318).
To cut the long story short, the syntax in next:
to import (include) the raw CSS-file the syntax is **without `.css`** extension at the end (results in actual read of partial `s[ac]ss|css` and include of it inline to SCSS/SASS):
#import "path/to/file";
to import the CSS-file in a traditional way syntax goes in traditional way, **with `.css` extension** at the end (results to `#import url("path/to/file.css");` in your compiled CSS):
#import "path/to/file.css";
And it is damn good: this syntax is elegant and laconic, plus backward compatible! It works excellently with libsass and node-sass.
__
To avoid further speculations in comments, writing this explicitly: Ruby based Sass still has this feature unimplemented after 7 years of discussions. By the time of writing this answer, it's promised that in 4.0 there will be a simple way to accomplish this, probably with the help of #use. It seems there will be an implementation very soon, the new "planned" "Proposal Accepted" tag was assigned for the issue #556 and the new #use feature.
UPD: on 26 October 2020 lib-sass was deprecated, therefore issue #556 was immediately closed.
__
answer might be updated, as soon as something changes.
Looks like this is unimplemented, as of the time of this writing:
https://github.com/sass/sass/issues/193
For libsass (C/C++ implementation), import works for *.css the same way as for *.scss files - just omit the extension:
#import "path/to/file";
This will import path/to/file.css.
See this answer for further details.
See this answer for Ruby implementation (sass gem)
You must prepend an underscore to the css file to be included, and switch its extension to scss (ex: _yourfile.scss). Then you just have to call it this way:
#import "yourfile";
And it will include the contents of the file, instead of using the CSS standard #import directive.
Good news everyone, Chris Eppstein created a compass plugin with inline css import functionality:
https://github.com/chriseppstein/sass-css-importer
Now, importing a CSS file is as easy as:
#import "CSS:library/some_css_file"
If you have a .css file which you don't wish to modify, neither change its extension to .scss (e.g. this file is from a forked project you don't maintain), you can always create a symlink and then import it into your .scss.
Creates a symlink:
ln -s path/to/css/file.css path/to/sass/files/_file.scss
Imports symlink file into a target .scss:
#import "path/to/sass/files/file";
Your target output .css file is going to hold contents from imported symlink .scss file, not a CSS import rule (mentioned by #yaz with highest comment votes). And you don't have duplicated files with different extensions, what means any update made inside initial .css file immediately gets imported into your target output.
Symbolic link (also symlink or soft link) is a special type of file
that contains a reference to another file in the form of an absolute
or relative path and that affects pathname resolution.
– http://en.wikipedia.org/wiki/Symbolic_link
You can use a third-party importer to customise #import semantics.
node-sass-import-once, which works with node-sass (for Node.js) can inline import CSS files.
Example of direct usage:
var sass = require('node-sass');,
importOnce = require('node-sass-import-once');
sass.render({
file: "input.scss",
importer: importOnce,
importOnce: {
css: true,
}
});
Example grunt-sass config:
var importOnce = require("node-sass-import-once");
grunt.loadNpmTasks("grunt-sass");
grunt.initConfig({
sass: {
options: {
sourceMap: true,
importer: importOnce
},
dev: {
files: {
"dist/style.css": "scss/**/*.scss"
}
}
});
Note that node-sass-import-once cannot currently import Sass partials without an explicit leading underscore. For example with the file partials/_partial.scss:
#import partials/_partial.scss succeeds
#import * partials/partial.scss fails
In general, be aware that a custom importer could change any import semantics. Read the docs before you start using it.
If I am correct css is compatible with scss so you can change the extension of a css to scss and it should continue to work. Once you change the extension you can import it and it will be included in the file.
If you don't do that sass will use the css #import which is something you don't want.
I figured out an elegant, Rails-like way to do it. First, rename your .scss file to .scss.erb, then use syntax like this (example for highlight_js-rails4 gem CSS asset):
#import "<%= asset_path("highlight_js/github") %>";
Why you can't host the file directly via SCSS:
Doing an #import in SCSS works fine for CSS files as long as you explicitly use the full path one way or another. In development mode, rails s serves assets without compiling them, so a path like this works...
#import "highlight_js/github.css";
...because the hosted path is literally /assets/highlight_js/github.css. If you right-click on the page and "view source", then click on the link for the stylesheet with the above #import, you'll see a line in there that looks like:
#import url(highlight_js/github.css);
The SCSS engine translates "highlight_js/github.css" to url(highlight_js/github.css). This will work swimmingly until you decide to try running it in production where assets are precompiled have a hash injected into the file name. The SCSS file will still resolve to a static /assets/highlight_js/github.css that was not precompiled and doesn't exist in production.
How this solution works:
Firstly, by moving the .scss file to .scss.erb, we have effectively turned the SCSS into a template for Rails. Now, whenever we use <%= ... %> template tags, the Rails template processor will replace these snippets with the output of the code (just like any other template).
Stating asset_path("highlight_js/github") in the .scss.erb file does two things:
Triggers the rake assets:precompile task to precompile the appropriate CSS file.
Generates a URL that appropriately reflects the asset regardless of the Rails environment.
This also means that the SCSS engine isn't even parsing the CSS file; it's just hosting a link to it! So there's no hokey monkey patches or gross workarounds. We're serving a CSS asset via SCSS as intended, and using a URL to said CSS asset as Rails intended. Sweet!
To import a regular CSS file into Sass:
Official Sass Documentation: Import CSS into Sass
Simple workaround:
All, or nearly all css file can be also interpreted as if it would be scss. It also enables to import them inside a block. Rename the css to scss, and import it so.
In my actual configuration I do the following:
First I copy the .css file into a temporary one, this time with .scss extension. Grunt example config:
copy: {
dev: {
files: [
{
src: "node_modules/some_module/some_precompiled.css",
dest: "target/resources/some_module_styles.scss"
}
]
}
}
Then you can import the .scss file from your parent scss (in my example, it is even imported into a block):
my-selector {
#import "target/resources/some_module_styles.scss";
...other rules...
}
Note: this could be dangerous, because it will effectively result that the css will be parsed multiple times. Check your original css for that it contains any scss-interpretable artifact (it is improbable, but if it happen, the result will be hard to debug and dangerous).
to Import css file in to scss simply use the this:
#import "src/your_file_path";
without using extension .css at the end
It is now possible using:
#import 'CSS:directory/filename.css';
I can confirm this works:
class CSSImporter < Sass::Importers::Filesystem
def extensions
super.merge('css' => :scss)
end
end
view_context = ActionView::Base.new
css = Sass::Engine.new(
template,
syntax: :scss,
cache: false,
load_paths: Rails.application.assets.paths,
read_cache: false,
filesystem_importer: CSSImporter # Relevant option,
sprockets: {
context: view_context,
environment: Rails.application.assets
}
).render
Credit to Chriss Epstein:
https://github.com/sass/sass/issues/193
Simple.
#import "path/to/file.css";

Resources