Less.js: modifyVars not being passed on

Created on 23 Feb 2018  ·  6Comments  ·  Source: less/less.js

I have an index.less file that decides what variables file to load in by a variable like so:

@theme-variant: "a-theme.less";

@import "./@{theme-variant}";

I have less files that load in that index file:

@import "~theme-variant-variables";

I am setting a value for the variable in my webpack.config.js file:

lessOptions.modifyVars = {
   "theme-variant": `"${v}-theme.less"`
}

If I move the contents of the index.less file into a directly imported less file everything works as I expect, I can switch the variables file chosen based on the webpack config logic, sadly if I try and centralize those two lines of less into a file that the directly imported less files import themselves it stops working. I see less loader calls less with the expected config so I'm guessing the less compiler doesn't propagate variables/options to imported less files. Is that expected?

All 6 comments

I suppose this is the same thing as in #2772 - see the middle of discussion there (e..g. modifyVars has its effect of defining the variable, but it comes after the import statement of interest is already evaluated). But also see https://github.com/less/less.js/issues/1400#issuecomment-137128461.

In short the point is: variable interpolation within import statements is a handy thing but it directly conflicts with lazy-evaluation principle. Thus if it comes to complex structuring, one would better find other ways to achieve such kind of customization (e.g. using different dirs for different theme files and then setting corresponding paths option to switch).

Thanks for the reply. The variable is updated in less files that are directly imported by JS, it's just files that are imported by other less files that don't have their variable updated. Lazy evaluation is occurring in both cases surely? It looks like the modify var logic/config isn't being applied/passed on to the less imported files. Or am I missing something?

The variable is updated in less files that are directly imported by JS, it's just files that are imported by other less files that don't have their variable updated.

Well, it's more tricky than that. Notice that for the lazy-evaluation to work the compiler has to evaluate various language entities (in the same scope) by types not by the order they appear in code, from higher level to lower, i.e. (roughly): imports -> mixins -> variables.
Now if you have @import "@{var}"; the compiler is forced to evaluate the given variable before the import (and all subsequent imports) - and this is what spoils the whole thing (in general the result of such abuse is simply undefined - it works in one cases (mostly very simple) and does not in others).

There's no way for the compiler to guarantee any consistent behaviour when two directly conflicting features are combined.

In other words, it's not that modifyVars does not "update variables" in subsequent imports but the updated variable values themselves can't have any effect on outer level imports (because these imports are already "done").


And even if the docs say:

Note that before v2.0.0, only variables which have been declared in the root or current scope were considered and that only the current file and calling files were considered when looking for a variable.

... it's not really much better after v2. It did improve/kludge-fix more combinations/use-cases but can't fix all of them. For more details see #1108 and specifically #2246.


It looks like the modify var logic/config isn't being applied/passed on to the less imported files.

No (simply because in the end the files are evaluated all together as a single large string). Unless the problem is elsewhere, to check simply add:
foo {bar: @theme-variant}
to the files of interest and see the result.

By the way. For your use-case (if the "*-theme.less" is only about variables/mixins for a specific theme), you could try something like:

  • Leave the default @import "a-theme.less"; to be expicit there (i.e. w/o any interpolation) or remove it completely.
  • With modifyVars set the importing statement itself (e.g. @import "custom-theme.less"; directly).

Note that while modifyVars pretends it's only about variables bla-bla-bla - in fact it does nothing but appending an arbitrary text to the end of the root file. I.e. in case of lessc it's realy just --modify-vars="whatever-less-code foo {bar: baz;}". I don't know though what formats webpack's less-loader can pass through, hmm... lessOptions.modifyVars = "an arbitrary code"; maybe?

That's obviously a hack but in fact it is quite predictable hack (as it abuses only modifyVars format and not the language itself like the initial combo does).

Thank you very much for your replies and ideas @seven-phases-max I'm going to investigate further during the week and have set a reminder to update/close this issue later on in the week. Thanks again.

I'll close this then as it's more like an expected (defined as "undefined/unspecified" in this case) behaviour rather than an issue that could be actually fixed somehow.
Though any improvement ideas and especially PRs are always welcome... I don't think anybody is going to get into this in a foreseeable future.

Was this page helpful?
0 / 5 - 0 ratings