-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Variables in import statements. #2772
Comments
Yes, this is known problem. Imports have to be evaluated before mixins (or anything else at all) - read comments there for more details (and see why it's better to avoid such code even if the issue is resolved somehow). |
(I would probably leave this a open feature-request, but I doubt it makes sense while knowing it's barely to be ever implemented, so I leave it to you). |
I added less/less-docs#388 (trying to describe it in a user-friendly words...). |
I guess I'll attempt to explain my real problem here and maybe there is an existing solution. I have a framework of less files and a couple of themes (sets of variables) sitting on a server. There are multiple applications that need to reference this common framework, and these applications pick & choose a theme. The problem comes with the pick & choose part; I need a way to allow each application to pick a theme, but at the same time if it doesn't specify one a default theme will be used. So an example application.less would be:
If an application specifies a theme, my initial thought was:
The problem is I can't figure out the "default" part. It's not working with variables (my example in #2771) and I can't use mixins hence this issue. |
Can't you simply move theme import into the app itself? E.g.: // in either proper order:
@import "/path/to/framework-theme.less";
@import "/path/to/framework.less"; |
The problem I'm trying to solve is having a default theme so it's not required to specify a theme. This means it needs to be a variable somewhere, and apparently the 'solution' less has for default variables doesn't work across imports? |
I'll move this here. application.less
framework.less
|
No, it is just variable usage inside |
Btw., (I'm just keeping to offer workarounds) also notice that if you remove // in either order
@import "framework.less";
@theme "default"; is probably better than nothing? |
Yes, I noticed that too but it doesn't solve the problem haha. It's also really weird that it works like that. If I can't specify a default then there is no point in having external configuration in this case. You have no ideas for handling default variables mixed with import statements? |
Are the imports a "known" and limited list? If so, you could hardcode the imports, but actually "include" them via mixins guarded with variables. |
No, we often create new themes when new applications are created, but not all the time. This type of syntax is necessary.
|
What about: @import "/path/to/framework.less";
@import "/path/to/base-theme.less";
@import (optional) "@{optional-theme}" Where base-theme.less defines |
I'd rather use @theme "default";
@import "framework.less"; ) for default-themed apps. And then |
Gah I'm scratching my head can't get this to work. I'm trying to simplify it for explanation but it's not that simple. For example, each of my UI components are scoped so I can use the same theme variables across components. E.g.
Then each component (say grid.less) imports
Since my components are scoped, I either need a mixin I can call in every scope or I need a variable to dynamically reference a
|
By the way |
Hmm, I've just realized you probably try to copy Semantic-UI customization approach (or is it Semantic-UI itself?), do you?
Imports happen before anything, incl. variables (thus the compiler cannot evaluate variables before it finishes all imports and in the same time it can't apply the imports that try to use the said variables. So technically, in a "strict lazy-evaluation" context, variables should not be allowed in imports at all, and while Less provides the feature, it still is nothing but a back-door so it's important to keep in mind that using it as the core of a library customization approach is ...). That being said, while both "file-based overriding" and "variable-based overriding" work just fine on their own, using the latter to override the former is expected to open that can of worms you face. I don't think there're can be any solution (and even if you find a workaround for the particular problem you'll face more as things go further...) |
Yes I look a lot of "inspiration" from semantic-ui =) The problem with the way they have it setup is the entire library is expected to be copied into every project. I'm trying to create shared resources. |
Then you'd really to consider some alternatives (because as you can see in the ticket linked above, initially the SUI overriding approach is designed w/o even considering the base Less principles. This of course does not automatically imply the method is the worst one, but still it's something to keep in mind). |
I get the methodology and what less is trying to be. However, if you're already breaking the methodology for developer convenience by having variables inside of import strings, maybe try to make it not so hacky. It seems the actual parser process is similar to this:
Or this:
Whereas this would allow default variables across imports:
|
Cycled variables after import after variables after import after variables .. and so on evaluation does not look like something "not so hacky" for the implementation itself. |
Would something bad happen if import variables would work the same way as mixin variables? Variables used in import statement could be treated the same way as variables in mixin arguments and variables defined by import could be treated as variables returned from mixins. |
My main concern (as a user of less) is that the target implementation behaviour isn't so hacky, not the parsing method. I was guessing at why the current behavior is happening: main.less
imported.less
Today the second file imports "default.html", however if you remove the The desired behavior would be in either case, whether |
@SomMeri @seven-phases-max I feel like this is just another variation of the mixin-related "variable visibility problem". That is, a circular evaluation dependency. Variables in imports are extremely useful, but imports import variables into the main scope, which can include variables which, when evaluated, change the way the variable'd import statement is evaluated. As circular dependency chains are part of the a fundamental design of Less.js, there's not necessarily "one way" to approach the problem. However, in practice, circular evaluation of a variable in an import shouldn't take that many "loops". So @Giwayume, to your point, I would think your first description of what the parser should do (and your follow-up example) is the correct one. That is, in short, a stylesheet should call imports based on the "final" variable values in the current scope, and, if necessary, call the @seven-phases-max I don't know why we would recommend against this use case, when the rest of Less follow this same model of needing to sometimes evaluate statements more than once. |
Sorry, but no: when scenarios such as |
@matthew-dean Ofcourse you can still solve it: you have to agree on a convention that cuts open the loop of the cyclic dependency. ;-) |
@matthew-dean You mean it should call the same import statement twice when something changes in variables or rather that call imports sequentially one by one where later imports reflect previous variables? |
@SomMeri I did say that... but not necessarily. I understand why @Giwayume's example fails, but there could be a tweak that would make it work. Currently, we have to initially evaluate vars (as best we can) in order to decipher this at all (part 1):
Originally, this was only available in the root document. Later, a fix was added so that you could evaluate vars in @import statements within imports themselves (part 2). So, in this particular example given by @Giwayume. main.less
imported.less
My guess is this fails because there's only an immediate evaluation (within imported.less) of this variable. So, if we are doing a partial evaluation of root, and a partial evaluation of the import, it seems like we should be able to override It's not an easy problem, and I agree with @seven-phases-max at least in part that this problem doesn't have to exist at all if library authors weren't taking this approach. At the same time, I think it's reasonable to call the example above a bug, or, if not, unexpected behavior based on what we currently allow as far as evaluating vars in imports. As to: should we evaluate an import and then re-import it if a var changes (and then discard the original import in final evaluation)? On the one hand, that might be useful. On the other hand, it seems like a low priority. Thinking about it some more, that's probably a bit overkill and might add too much evaluation overhead for too small a gain. So, I think the only tweak here as far as evaluation isn't that circular. It just requires the parent scope to have it's vars partially evaluated (I say partially, since it wouldn't be an evaluation of other mixins / imports into the parent scope -- this should only be a list of I've been thinking of patterns where it might be possible for Less to do a kind of "continuous evaluation" rather than the more linear evaluation that happens now, but that's outside of this cope. |
@matthew-dean Then it can work the same way as mixins now - second mixin in a row sees variables returned from the first one. Since it was implemented already once, it should be easier to do the same thing again :). This works:
This works too: .import-variable() {
@variable: value;
}
.mixin(@variable) {
use: @variable;
}
#use-place {
.import-variable();
.mixin(@variable);
} both compile to: #use-place {
use: value;
} |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
It's just that this has worked fine up to version 2.7.3, at least that I can tell. Upgrading to v3 does not allow variables be used within imports across separate files. So it seems if this type of behavior was not expected to work, it certainly used to (although I am not a LESS guy, so maybe I'm missing something as to how it used to work). This is Semantic UI's fault! haha 👎 If using Semantic UI + Webpack for this, DO NOT upgrade past version 2.7.3 until either Semantic improves their LESS best practices or LESS gets to a point where they can essentially handle dynamic imports with variables across separate files. |
I get "SyntaxError: variable @url is undefined" with the following logic:
It appears imports are not as dynamic as I'd hoped. Is this expected behavior?
Funny thing is if I don't even call the mixin (only declare it), there's no errors.
Note: tested on 2.7.3
The text was updated successfully, but these errors were encountered: