less/less.js — pull request #1845 by seven-phases-max (2014-02-03 17:51:39+00:00)
Title: Initial support for variable expansion in at-rules (needs decision/discussion).

In fact I just wanted to provide a variable interpolation for the `@keyframes` name (i.e. #1264, #1640 etc.). But since `@keyframes` rule is currently implemented as a generic "directive" ruleset also handling `@supports`, `@document` and similiar at-rules, this feature automatically applies to all of those and that makes an interpolation syntax to be used/allowed there not so obvious. (
First of all I planned to make it simply `@keyframes @name {...}` but for `@supports` that would mean the following code possible: `@supports {@var: @var}` with both `@var`s expanded which is barely can be accepted.
So insipired by https://github.com/less/less.js/pull/1648#issuecomment-33879011 I decided to allow both `@var` and `@{var}` expansions possible with certain (optional) restrictions.

To give a basic idea of what is theoretically possible w/o major refactoring of current "directive" implementation, here's 5 basis interpolation variants for the same example input:
```
@a: X;
@supports @a @{a} (@a: @{a}) and (@{a}: @a) {}
```

**[1]** expand only `@var` everywhere:
`@supports X @{a} (X: @{a}) and (@{a}: X) {}`

**[2]** only `@var` when not before `:`:
`@supports X @{a} (@a: @{a}) and (@{a}: X) {}`

**[3]** only `@{var}`:
`@supports @a X (@a: X) and (X: @a) {}`

**[4]** `@{var}` and `@var` everywhere:
`@supports X X (X: X) and (X: X) {}`

**[5]** `@{var}` and `@var` when not before `:`:
`@supports X X (@a: X) and (X: X) {}`

This PR implements [5], but can be switched to any of above. Additionally the variable expansion can be enabled/disabled for either of affected at-rules, so for example we can change it to the most safe and minimalistic [1] enabled for `@keyframes` only.
The list of affected at-rules:
`@host`
`@page`
`@document`
`@supports`
`@keyframes`

My main concern with these changes is future compatibility, i.e. current "directives" implementation with no doubt will be improved/changed/refactored eventually and then it may be not so easy to maintain too complex variable interpolation (like [5]) as things go further (for instance, `@supports` will need its bubbling and other `@media` like stuff, actually most likely `@supports` and `@media` implementation is about to be combined since they are pretty much identical feature in fact).

Other important notes, known issues etc.:

**Variable reference support:** `@@var` expansion is also supported (anywhere the `@var` expansion is allowed).

**Comments & strings (issue).** The block where the vars are expanded (i.e. `@directive {...}`) is implemented just as a plain string (not really parsed), the expansion works like plain text-replace stuff thus also applies any strings and comments included, e.g.:
```
@a: X;
@supports "@a pple" /* banan @a*/ {}
```
expands to:
```
@supports "X pple" /* banan X*/ {}
```

**Escaping.** Escaping is not supported:
`@supports ~"@{a}pple" {}` -> `@supports ~"Xpple" {}`

**Concatenated vars expansion.** There's difference between expansiion of concatenated `@var`s and `@{var}`s. Also notice a difference between how `@var` is expanded inside at-rules and within standard Less code:
```
@foo: foo;
@baz: baz;
@foo-bar-: mambo;

@keyframes @{foo}-bar-@{baz} {}
@keyframes @foo-bar-@baz {}

.standard-var-expansion {
value: @foo-bar-@baz;
}
```