4

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.

Community
  • 1
  • 1
Varinder
  • 2,634
  • 1
  • 17
  • 20

3 Answers3

5

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.

Oleg
  • 24,465
  • 8
  • 61
  • 91
  • Hi o.v. Thanks for digging it up. I'll go with downgrading to dotLess v1.3.1, @import issue is solved! One more thing, adding import statement on top of every file feels a bit ugly. Is there any other way to approach it? I've edited my question explaining what im trying to do. Thanks – Varinder Jan 08 '14 at 10:13
  • @Varinder: I don't think it's ugly, it feels pretty traditional actually. For instance your partial views all have to import dependencies, don't they? On an unrelated note, you might want to follow [source maps integration ticket](https://github.com/less/less.js/issues/1050), those [are extremely powerful](https://developers.google.com/chrome-developer-tools/docs/css-preprocessors) for debugging – Oleg Jan 10 '14 at 02:00
4

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.

Hans Roerdinkholder
  • 3,000
  • 1
  • 20
  • 30
  • 1
    Although good for release, it is tricky to debug ( all stylesheets added into 1 ). I tried setting up https://github.com/dotless/dotless/issues/186 but as stated there "it outputs the line number of the mixin definition and not the line number it was called from" - which is not very helpful. So yea, im not confident about that approach. – Varinder Jan 06 '14 at 23:37
3

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

Community
  • 1
  • 1
Shad
  • 4,423
  • 3
  • 36
  • 37
  • Thanks for the answer Shad. One question, How do you output error messages generated by less? as this import issue arises only when debug ( in web.config ) is set to false. – Varinder Jan 08 '14 at 10:20
  • 1
    All error messages are tracked by logger. `LessTransform` class, that you mentioned in the question uses `LessEngine` with `AspNetTraceLogger`. I'd just replaced it with `AspResponseLogger` and error message has appeared in the response: `var logger = new AspResponseLogger(LogLevel.Debug, new CssResponse(new Http(), false));` (instead line 52 in https://gist.github.com/benfoster/3924025) – Shad Jan 08 '14 at 10:35