Dotless MVC bundling issue when importing same less file twice

3k views Asked by At

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

Works 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"));


When component1.less is declared first

Output when "~/styles/component2.less" is declared first

bundles.Add(new Bundle("~/styles/css", new LessTransform()).Include("~/styles/component2.less", "~/styles/component1.less"));

When componenet2.less is declared first

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;
}

Works

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.

3

There are 3 answers

2
Oleg On BEST ANSWER

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.

1
Hans Roerdinkholder On

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.

2
Shad On

@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.