less 4.9.0 → 5.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +19 -5
  2. package/bin/lessc +190 -630
  3. package/dist/less-node.cjs +399 -14570
  4. package/index.cjs +5 -2
  5. package/lib/index.js +164 -0
  6. package/lib/lessc-helper.js +52 -0
  7. package/lib/logger.js +76 -0
  8. package/lib/options.d.ts +46 -0
  9. package/lib/options.js +145 -0
  10. package/lib/types.js +20 -0
  11. package/lib/version.js +19 -0
  12. package/package.json +34 -40
  13. package/dist/less.cjs +0 -15215
  14. package/dist/less.js +0 -15215
  15. package/dist/less.min.js +0 -11
  16. package/dist/less.min.js.map +0 -1
  17. package/lib/less/constants.js +0 -13
  18. package/lib/less/contexts.js +0 -165
  19. package/lib/less/data/colors.js +0 -150
  20. package/lib/less/data/index.js +0 -4
  21. package/lib/less/data/unit-conversions.js +0 -21
  22. package/lib/less/default-options.js +0 -89
  23. package/lib/less/deprecation.js +0 -82
  24. package/lib/less/environment/abstract-file-manager.js +0 -140
  25. package/lib/less/environment/abstract-plugin-loader.js +0 -185
  26. package/lib/less/environment/environment-api.ts +0 -21
  27. package/lib/less/environment/environment.js +0 -59
  28. package/lib/less/environment/file-manager-api.ts +0 -77
  29. package/lib/less/functions/boolean.js +0 -29
  30. package/lib/less/functions/color-blending.js +0 -85
  31. package/lib/less/functions/color.js +0 -452
  32. package/lib/less/functions/data-uri.js +0 -74
  33. package/lib/less/functions/default.js +0 -26
  34. package/lib/less/functions/function-caller.js +0 -55
  35. package/lib/less/functions/function-registry.js +0 -36
  36. package/lib/less/functions/index.js +0 -35
  37. package/lib/less/functions/list.js +0 -158
  38. package/lib/less/functions/math-helper.js +0 -22
  39. package/lib/less/functions/math.js +0 -30
  40. package/lib/less/functions/number.js +0 -95
  41. package/lib/less/functions/string.js +0 -39
  42. package/lib/less/functions/style.js +0 -29
  43. package/lib/less/functions/svg.js +0 -87
  44. package/lib/less/functions/types.js +0 -70
  45. package/lib/less/import-manager.js +0 -183
  46. package/lib/less/index.js +0 -95
  47. package/lib/less/less-error.js +0 -166
  48. package/lib/less/logger.js +0 -34
  49. package/lib/less/parse-tree.js +0 -124
  50. package/lib/less/parse.js +0 -87
  51. package/lib/less/parser/lookup-pattern.js +0 -26
  52. package/lib/less/parser/parser-input.js +0 -400
  53. package/lib/less/parser/parser.js +0 -2998
  54. package/lib/less/plugin-manager.js +0 -168
  55. package/lib/less/render.js +0 -41
  56. package/lib/less/source-map-builder.js +0 -82
  57. package/lib/less/source-map-output.js +0 -151
  58. package/lib/less/transform-tree.js +0 -102
  59. package/lib/less/tree/anonymous.js +0 -61
  60. package/lib/less/tree/assignment.js +0 -48
  61. package/lib/less/tree/atrule-syntax.js +0 -8
  62. package/lib/less/tree/atrule.js +0 -340
  63. package/lib/less/tree/attribute.js +0 -63
  64. package/lib/less/tree/call.js +0 -132
  65. package/lib/less/tree/color.js +0 -285
  66. package/lib/less/tree/combinator.js +0 -38
  67. package/lib/less/tree/comment.js +0 -48
  68. package/lib/less/tree/condition.js +0 -65
  69. package/lib/less/tree/container.js +0 -104
  70. package/lib/less/tree/debug-info.js +0 -80
  71. package/lib/less/tree/declaration.js +0 -139
  72. package/lib/less/tree/detached-ruleset.js +0 -45
  73. package/lib/less/tree/dimension.js +0 -215
  74. package/lib/less/tree/element.js +0 -93
  75. package/lib/less/tree/expression.js +0 -100
  76. package/lib/less/tree/extend.js +0 -88
  77. package/lib/less/tree/import.js +0 -299
  78. package/lib/less/tree/index.js +0 -56
  79. package/lib/less/tree/interpolated-variable.js +0 -106
  80. package/lib/less/tree/javascript.js +0 -45
  81. package/lib/less/tree/js-eval-node.js +0 -80
  82. package/lib/less/tree/keyword.js +0 -28
  83. package/lib/less/tree/media.js +0 -90
  84. package/lib/less/tree/merge-rules.js +0 -47
  85. package/lib/less/tree/mixin-call.js +0 -283
  86. package/lib/less/tree/mixin-definition.js +0 -319
  87. package/lib/less/tree/namespace-value.js +0 -95
  88. package/lib/less/tree/negative.js +0 -37
  89. package/lib/less/tree/nested-at-rule.js +0 -239
  90. package/lib/less/tree/node.js +0 -310
  91. package/lib/less/tree/operation.js +0 -81
  92. package/lib/less/tree/paren.js +0 -41
  93. package/lib/less/tree/property.js +0 -96
  94. package/lib/less/tree/query-in-parens.js +0 -64
  95. package/lib/less/tree/quoted.js +0 -131
  96. package/lib/less/tree/ruleset.js +0 -1068
  97. package/lib/less/tree/selector.js +0 -201
  98. package/lib/less/tree/unicode-descriptor.js +0 -14
  99. package/lib/less/tree/unit.js +0 -170
  100. package/lib/less/tree/url.js +0 -83
  101. package/lib/less/tree/value.js +0 -60
  102. package/lib/less/tree/variable-call.js +0 -60
  103. package/lib/less/tree/variable.js +0 -85
  104. package/lib/less/utils.js +0 -126
  105. package/lib/less/visitors/extend-visitor.js +0 -505
  106. package/lib/less/visitors/import-sequencer.js +0 -56
  107. package/lib/less/visitors/import-visitor.js +0 -203
  108. package/lib/less/visitors/index.js +0 -15
  109. package/lib/less/visitors/join-selector-visitor.js +0 -65
  110. package/lib/less/visitors/set-tree-visibility-visitor.js +0 -57
  111. package/lib/less/visitors/to-css-visitor.js +0 -332
  112. package/lib/less/visitors/visitor.js +0 -165
  113. package/lib/less-browser/add-default-options.js +0 -53
  114. package/lib/less-browser/bootstrap.js +0 -67
  115. package/lib/less-browser/browser.js +0 -65
  116. package/lib/less-browser/cache.js +0 -43
  117. package/lib/less-browser/error-reporting.js +0 -170
  118. package/lib/less-browser/file-manager.js +0 -112
  119. package/lib/less-browser/image-size.js +0 -28
  120. package/lib/less-browser/index.js +0 -284
  121. package/lib/less-browser/log-listener.js +0 -42
  122. package/lib/less-browser/plugin-loader.js +0 -24
  123. package/lib/less-browser/utils.js +0 -30
  124. package/lib/less-node/environment.js +0 -43
  125. package/lib/less-node/file-manager.js +0 -156
  126. package/lib/less-node/fs.js +0 -14
  127. package/lib/less-node/image-size.js +0 -78
  128. package/lib/less-node/index.js +0 -31
  129. package/lib/less-node/lessc-helper.js +0 -95
  130. package/lib/less-node/plugin-loader.js +0 -62
  131. package/lib/less-node/url-file-manager.js +0 -60
@@ -1,319 +0,0 @@
1
- // @ts-check
2
- /** @import { EvalContext, TreeVisitor, VisibilityInfo } from './node.js' */
3
- /** @import { FunctionRegistry } from './nested-at-rule.js' */
4
- /** @import { MixinArg } from './mixin-call.js' */
5
- import Node from './node.js';
6
- import Selector from './selector.js';
7
- import Element from './element.js';
8
- import Ruleset from './ruleset.js';
9
- import Declaration from './declaration.js';
10
- import DetachedRuleset from './detached-ruleset.js';
11
- import Expression from './expression.js';
12
- import contexts from '../contexts.js';
13
- import * as utils from '../utils.js';
14
-
15
- /**
16
- * @typedef {object} MixinParam
17
- * @property {string} [name]
18
- * @property {Node} [value]
19
- * @property {boolean} [variadic]
20
- */
21
-
22
- /**
23
- * @typedef {Ruleset & {
24
- * functionRegistry?: FunctionRegistry,
25
- * originalRuleset?: Node
26
- * }} RulesetWithRegistry
27
- */
28
-
29
- class Definition extends Ruleset {
30
- get type() { return 'MixinDefinition'; }
31
-
32
- /**
33
- * @param {string | undefined} name
34
- * @param {MixinParam[]} params
35
- * @param {Node[]} rules
36
- * @param {Node | null} [condition]
37
- * @param {boolean} [variadic]
38
- * @param {Node[] | null} [frames]
39
- * @param {VisibilityInfo} [visibilityInfo]
40
- */
41
- constructor(name, params, rules, condition, variadic, frames, visibilityInfo) {
42
- super(null, null);
43
- /** @type {string} */
44
- this.name = name || 'anonymous mixin';
45
- this.selectors = [new Selector([new Element(null, name, false, this._index, this._fileInfo)])];
46
- /** @type {MixinParam[]} */
47
- this.params = params;
48
- /** @type {Node | null | undefined} */
49
- this.condition = condition;
50
- /** @type {boolean | undefined} */
51
- this.variadic = variadic;
52
- /** @type {number} */
53
- this.arity = params.length;
54
- this.rules = rules;
55
- this._lookups = {};
56
- /** @type {string[]} */
57
- const optionalParameters = [];
58
- /** @type {number} */
59
- this.required = params.reduce(function (/** @type {number} */ count, /** @type {MixinParam} */ p) {
60
- if (!p.name || (p.name && !p.value)) {
61
- return count + 1;
62
- }
63
- else {
64
- optionalParameters.push(p.name);
65
- return count;
66
- }
67
- }, 0);
68
- /** @type {string[]} */
69
- this.optionalParameters = optionalParameters;
70
- /** @type {Node[] | null | undefined} */
71
- this.frames = frames;
72
- this.copyVisibilityInfo(visibilityInfo);
73
- this.allowRoot = true;
74
- /** @type {boolean} */
75
- this.evalFirst = true;
76
- }
77
-
78
- /** @param {TreeVisitor} visitor */
79
- accept(visitor) {
80
- if (this.params && this.params.length) {
81
- this.params = /** @type {MixinParam[]} */ (/** @type {unknown} */ (visitor.visitArray(/** @type {Node[]} */ (/** @type {unknown} */ (this.params)))));
82
- }
83
- this.rules = visitor.visitArray(this.rules);
84
- if (this.condition) {
85
- this.condition = visitor.visit(this.condition);
86
- }
87
- }
88
-
89
- /**
90
- * @param {EvalContext} context
91
- * @param {EvalContext} mixinEnv
92
- * @param {MixinArg[] | null} args
93
- * @param {Node[]} evaldArguments
94
- * @returns {Ruleset}
95
- */
96
- evalParams(context, mixinEnv, args, evaldArguments) {
97
- /* jshint boss:true */
98
- const frame = new Ruleset(null, null);
99
-
100
- /** @type {Node[] | undefined} */
101
- let varargs;
102
- /** @type {MixinArg | undefined} */
103
- let arg;
104
- const params = /** @type {MixinParam[]} */ (utils.copyArray(this.params));
105
- /** @type {number} */
106
- let i;
107
- /** @type {number} */
108
- let j;
109
- /** @type {Node | undefined} */
110
- let val;
111
- /** @type {string | undefined} */
112
- let name;
113
- /** @type {boolean} */
114
- let isNamedFound;
115
- /** @type {number} */
116
- let argIndex;
117
- let argsLength = 0;
118
-
119
- if (mixinEnv.frames && mixinEnv.frames[0] && /** @type {RulesetWithRegistry} */ (mixinEnv.frames[0]).functionRegistry) {
120
- /** @type {RulesetWithRegistry} */ (frame).functionRegistry = /** @type {FunctionRegistry} */ (/** @type {RulesetWithRegistry} */ (mixinEnv.frames[0]).functionRegistry).inherit();
121
- }
122
- mixinEnv = new contexts.Eval(mixinEnv, /** @type {Node[]} */ ([frame]).concat(/** @type {Node[]} */ (mixinEnv.frames)));
123
-
124
- if (args) {
125
- args = /** @type {MixinArg[]} */ (utils.copyArray(args));
126
- argsLength = args.length;
127
-
128
- for (i = 0; i < argsLength; i++) {
129
- arg = args[i];
130
- if (name = (arg && arg.name)) {
131
- isNamedFound = false;
132
- for (j = 0; j < params.length; j++) {
133
- if (!evaldArguments[j] && name === params[j].name) {
134
- evaldArguments[j] = arg.value.eval(context);
135
- frame.prependRule(new Declaration(name, arg.value.eval(context)));
136
- isNamedFound = true;
137
- break;
138
- }
139
- }
140
- if (isNamedFound) {
141
- args.splice(i, 1);
142
- i--;
143
- continue;
144
- } else {
145
- throw { type: 'Runtime', message: `Named argument for ${this.name} ${args[i].name} not found` };
146
- }
147
- }
148
- }
149
- }
150
- argIndex = 0;
151
- for (i = 0; i < params.length; i++) {
152
- if (evaldArguments[i]) { continue; }
153
-
154
- arg = args && args[argIndex];
155
-
156
- if (name = params[i].name) {
157
- if (params[i].variadic) {
158
- varargs = [];
159
- for (j = argIndex; j < argsLength; j++) {
160
- varargs.push(/** @type {MixinArg[]} */ (args)[j].value.eval(context));
161
- }
162
- frame.prependRule(new Declaration(name, new Expression(varargs).eval(context)));
163
- } else {
164
- val = arg && arg.value;
165
- if (val) {
166
- // This was a mixin call, pass in a detached ruleset of it's eval'd rules
167
- if (Array.isArray(val)) {
168
- val = /** @type {Node} */ (/** @type {unknown} */ (new DetachedRuleset(new Ruleset(null, /** @type {Node[]} */ (val)))));
169
- }
170
- else {
171
- val = val.eval(context);
172
- }
173
- } else if (params[i].value) {
174
- val = /** @type {Node} */ (params[i].value).eval(mixinEnv);
175
- frame.resetCache();
176
- } else {
177
- throw { type: 'Runtime', message: `wrong number of arguments for ${this.name} (${argsLength} for ${this.arity})` };
178
- }
179
-
180
- frame.prependRule(new Declaration(name, val));
181
- evaldArguments[i] = val;
182
- }
183
- }
184
-
185
- if (params[i].variadic && args) {
186
- for (j = argIndex; j < argsLength; j++) {
187
- evaldArguments[j] = args[j].value.eval(context);
188
- }
189
- }
190
- argIndex++;
191
- }
192
-
193
- return frame;
194
- }
195
-
196
- /** @returns {Ruleset} */
197
- makeImportant() {
198
- const rules = !this.rules ? this.rules : this.rules.map(function (/** @type {Node & { makeImportant?: (important?: boolean) => Node }} */ r) {
199
- if (r.makeImportant) {
200
- return r.makeImportant(true);
201
- } else {
202
- return r;
203
- }
204
- });
205
- const result = new Definition(this.name, this.params, rules, this.condition, this.variadic, this.frames);
206
- return /** @type {Ruleset} */ (/** @type {unknown} */ (result));
207
- }
208
-
209
- /**
210
- * @param {EvalContext} context
211
- * @returns {Definition}
212
- */
213
- eval(context) {
214
- return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || utils.copyArray(context.frames));
215
- }
216
-
217
- /**
218
- * @param {EvalContext} context
219
- * @param {MixinArg[]} args
220
- * @param {string | undefined} important
221
- * @returns {Ruleset}
222
- */
223
- evalCall(context, args, important) {
224
- /** @type {Node[]} */
225
- const _arguments = [];
226
- const mixinFrames = this.frames ? /** @type {Node[]} */ (this.frames).concat(/** @type {Node[]} */ (context.frames)) : /** @type {Node[]} */ (context.frames);
227
- const frame = this.evalParams(context, new contexts.Eval(context, mixinFrames), args, _arguments);
228
- /** @type {Node[]} */
229
- let rules;
230
- /** @type {Ruleset} */
231
- let ruleset;
232
-
233
- frame.prependRule(new Declaration('@arguments', new Expression(_arguments).eval(context)));
234
-
235
- rules = utils.copyArray(this.rules);
236
-
237
- ruleset = new Ruleset(null, rules);
238
- /** @type {RulesetWithRegistry} */ (ruleset).originalRuleset = this;
239
- ruleset = /** @type {Ruleset} */ (ruleset.eval(new contexts.Eval(context, /** @type {Node[]} */ ([this, frame]).concat(mixinFrames))));
240
- if (important) {
241
- ruleset = /** @type {Ruleset} */ (ruleset.makeImportant());
242
- }
243
- return ruleset;
244
- }
245
-
246
- /**
247
- * @param {MixinArg[] | null} args
248
- * @param {EvalContext} context
249
- * @returns {boolean}
250
- */
251
- matchCondition(args, context) {
252
- if (this.condition && !this.condition.eval(
253
- new contexts.Eval(context,
254
- /** @type {Node[]} */ ([this.evalParams(context, /* the parameter variables */
255
- new contexts.Eval(context, this.frames ? /** @type {Node[]} */ (this.frames).concat(/** @type {Node[]} */ (context.frames)) : context.frames), args, [])])
256
- .concat(/** @type {Node[]} */ (this.frames || [])) // the parent namespace/mixin frames
257
- .concat(/** @type {Node[]} */ (context.frames))))) { // the current environment frames
258
- return false;
259
- }
260
- return true;
261
- }
262
-
263
- /**
264
- * @param {MixinArg[] | null} args
265
- * @param {EvalContext} [context]
266
- * @returns {boolean}
267
- */
268
- matchArgs(args, context) {
269
- const allArgsCnt = (args && args.length) || 0;
270
- const evaldArguments = new Array(this.params.length);
271
- const positionalArgs = [];
272
- let positionalIndex = 0;
273
-
274
- for (let i = 0; i < allArgsCnt; i++) {
275
- const arg = /** @type {MixinArg[]} */ (args)[i];
276
- if (!arg.name) {
277
- positionalArgs.push(arg);
278
- continue;
279
- }
280
-
281
- const paramIndex = this.params.findIndex((param, index) => param.name === arg.name && !evaldArguments[index]);
282
- if (paramIndex < 0) {
283
- return false;
284
- }
285
- evaldArguments[paramIndex] = arg;
286
- }
287
-
288
- for (let i = 0; i < this.params.length; i++) {
289
- if (evaldArguments[i]) {
290
- continue;
291
- }
292
- if (this.params[i].variadic) {
293
- positionalIndex = positionalArgs.length;
294
- continue;
295
- }
296
- if (positionalIndex < positionalArgs.length) {
297
- evaldArguments[i] = positionalArgs[positionalIndex++];
298
- } else if (!this.params[i].name || !this.params[i].value) {
299
- return false;
300
- }
301
- }
302
-
303
- if (positionalIndex < positionalArgs.length) {
304
- return false;
305
- }
306
-
307
- // check patterns
308
- for (let i = 0; i < this.arity; i++) {
309
- if (!this.params[i].name && !this.params[i].variadic) {
310
- if (evaldArguments[i].value.eval(context).toCSS(/** @type {EvalContext} */ ({})) != /** @type {Node} */ (this.params[i].value).eval(context).toCSS(/** @type {EvalContext} */ ({}))) {
311
- return false;
312
- }
313
- }
314
- }
315
- return true;
316
- }
317
- }
318
-
319
- export default Definition;
@@ -1,95 +0,0 @@
1
- // @ts-check
2
- /** @import { EvalContext, FileInfo } from './node.js' */
3
- import Node from './node.js';
4
- import Variable from './variable.js';
5
- import Ruleset from './ruleset.js';
6
- import Selector from './selector.js';
7
-
8
- class NamespaceValue extends Node {
9
- get type() { return 'NamespaceValue'; }
10
-
11
- /**
12
- * @param {Node} ruleCall
13
- * @param {string[]} lookups
14
- * @param {number} index
15
- * @param {FileInfo} fileInfo
16
- */
17
- constructor(ruleCall, lookups, index, fileInfo) {
18
- super();
19
- this.value = ruleCall;
20
- this.lookups = lookups;
21
- this._index = index;
22
- this._fileInfo = fileInfo;
23
- }
24
-
25
- /**
26
- * @param {EvalContext} context
27
- * @returns {Node}
28
- */
29
- eval(context) {
30
- let i, name;
31
- /** @type {Ruleset | Node | Node[]} */
32
- let rules = this.value.eval(context);
33
-
34
- for (i = 0; i < this.lookups.length; i++) {
35
- name = this.lookups[i];
36
-
37
- if (Array.isArray(rules)) {
38
- rules = new Ruleset([new Selector()], rules);
39
- }
40
-
41
- const rs = /** @type {Ruleset} */ (rules);
42
-
43
- if (name === '') {
44
- rules = rs.lastDeclaration();
45
- }
46
- else if (name.charAt(0) === '@') {
47
- if (name.charAt(1) === '@') {
48
- name = `@${new Variable(name.slice(1)).eval(context).value}`;
49
- }
50
- if (rs.variables) {
51
- rules = rs.variable(name);
52
- }
53
-
54
- if (!rules) {
55
- throw { type: 'Name',
56
- message: `variable ${name} not found`,
57
- filename: this.fileInfo().filename,
58
- index: this.getIndex() };
59
- }
60
- }
61
- else {
62
- if (name.substring(0, 2) === '$@') {
63
- name = `$${new Variable(name.slice(1)).eval(context).value}`;
64
- }
65
- else {
66
- name = name.charAt(0) === '$' ? name : `$${name}`;
67
- }
68
- if (rs.properties) {
69
- rules = rs.property(name);
70
- }
71
-
72
- if (!rules) {
73
- throw { type: 'Name',
74
- message: `property "${name.slice(1)}" not found`,
75
- filename: this.fileInfo().filename,
76
- index: this.getIndex() };
77
- }
78
- const rulesArr = /** @type {Node[]} */ (rules);
79
- rules = rulesArr[rulesArr.length - 1];
80
- }
81
-
82
- const current = /** @type {Node} */ (rules);
83
- if (current.value) {
84
- rules = /** @type {Node} */ (current.eval(context).value);
85
- }
86
- const currentNode = /** @type {Node & { ruleset?: Node }} */ (rules);
87
- if (currentNode.ruleset) {
88
- rules = currentNode.ruleset.eval(context);
89
- }
90
- }
91
- return /** @type {Node} */ (rules);
92
- }
93
- }
94
-
95
- export default NamespaceValue;
@@ -1,37 +0,0 @@
1
- // @ts-check
2
- /** @import { EvalContext, CSSOutput } from './node.js' */
3
- import Node from './node.js';
4
- import Operation from './operation.js';
5
- import Dimension from './dimension.js';
6
-
7
- class Negative extends Node {
8
- get type() { return 'Negative'; }
9
-
10
- /** @param {Node} node */
11
- constructor(node) {
12
- super();
13
- this.value = node;
14
- }
15
-
16
- /**
17
- * @param {EvalContext} context
18
- * @param {CSSOutput} output
19
- */
20
- genCSS(context, output) {
21
- output.add('-');
22
- /** @type {Node} */ (this.value).genCSS(context, output);
23
- }
24
-
25
- /**
26
- * @param {EvalContext} context
27
- * @returns {Node}
28
- */
29
- eval(context) {
30
- if (context.isMathOn('*')) {
31
- return (new Operation('*', [new Dimension(-1), /** @type {Node} */ (this.value)], false)).eval(context);
32
- }
33
- return new Negative(/** @type {Node} */ (this.value).eval(context));
34
- }
35
- }
36
-
37
- export default Negative;
@@ -1,239 +0,0 @@
1
- // @ts-check
2
- /** @import { EvalContext, CSSOutput, TreeVisitor, FileInfo, VisibilityInfo } from './node.js' */
3
- import Ruleset from './ruleset.js';
4
- import Value from './value.js';
5
- import Selector from './selector.js';
6
- import Anonymous from './anonymous.js';
7
- import Expression from './expression.js';
8
- import * as utils from '../utils.js';
9
- import Node from './node.js';
10
-
11
- /**
12
- * @typedef {object} FunctionRegistry
13
- * @property {(name: string, func: Function) => void} add
14
- * @property {(functions: Object) => void} addMultiple
15
- * @property {(name: string) => Function} get
16
- * @property {() => Object} getLocalFunctions
17
- * @property {() => FunctionRegistry} inherit
18
- * @property {(base: FunctionRegistry) => FunctionRegistry} create
19
- */
20
-
21
- /**
22
- * @typedef {Node & {
23
- * features: Value,
24
- * rules: Ruleset[],
25
- * type: string,
26
- * functionRegistry?: FunctionRegistry,
27
- * multiMedia?: boolean,
28
- * debugInfo?: { lineNumber: number, fileName: string },
29
- * allowRoot?: boolean,
30
- * _evaluated?: boolean,
31
- * evalFunction: () => void,
32
- * evalTop: (context: EvalContext) => Node | Ruleset,
33
- * evalNested: (context: EvalContext) => Node | Ruleset,
34
- * permute: (arr: Node[][]) => Node[][],
35
- * bubbleSelectors: (selectors: Selector[] | undefined) => void,
36
- * outputRuleset: (context: EvalContext, output: CSSOutput, rules: Node[]) => void
37
- * }} NestableAtRuleThis
38
- */
39
-
40
- // A media query may only carry a media type at its front, before any
41
- // conditions (https://drafts.csswg.org/mediaqueries-5/#typedef-media-query-list).
42
- // Operators that join media-query parts, so a fragment leading with one is not
43
- // a media type. Any other bare identifier is treated as a type, since unknown
44
- // media types are valid non-matches in CSS, not syntax errors.
45
- const MEDIA_QUERY_OPERATORS = ['and', 'or'];
46
-
47
- /**
48
- * Whether a flattened media-query fragment leads with a media type, e.g.
49
- * `screen`, `only screen`, `print and (color)` or an unknown type like `foo`.
50
- * @param {Node & { value?: * }} fragment
51
- */
52
- function startsWithMediaType(fragment) {
53
- let head;
54
- if (fragment.type === 'Keyword' || fragment.type === 'Anonymous') {
55
- head = fragment.value;
56
- } else if (fragment.type === 'Expression' && Array.isArray(fragment.value)) {
57
- const parts = fragment.value.filter(p => p && p.value !== undefined);
58
- let idx = 0;
59
- const first = parts[idx] && String(parts[idx].value).toLowerCase();
60
- if (first === 'not' || first === 'only') { idx++; }
61
- head = parts[idx] && parts[idx].value;
62
- }
63
- if (typeof head !== 'string' || head === '') {
64
- return false;
65
- }
66
- // Inspect only the first token. A media type is a bare identifier; a feature
67
- // condition begins with '(' - e.g. an escaped ~"(max-width: 1px)" is an
68
- // Anonymous whose whole value is "(max-width: 1px)", which is not a media type.
69
- const firstToken = head.trim().split(/[\s(]/)[0];
70
- return firstToken !== ''
71
- && MEDIA_QUERY_OPERATORS.indexOf(firstToken.toLowerCase()) < 0;
72
- }
73
-
74
- const NestableAtRulePrototype = {
75
-
76
- isRulesetLike() {
77
- return true;
78
- },
79
-
80
- /** @param {TreeVisitor} visitor */
81
- accept(visitor) {
82
- /** @type {NestableAtRuleThis} */
83
- const self = /** @type {NestableAtRuleThis} */ (/** @type {unknown} */ (this));
84
- if (self.features) {
85
- self.features = /** @type {Value} */ (visitor.visit(self.features));
86
- }
87
- if (self.rules) {
88
- self.rules = /** @type {Ruleset[]} */ (visitor.visitArray(self.rules));
89
- }
90
- },
91
-
92
- evalFunction: function () {
93
- /** @type {NestableAtRuleThis} */
94
- const self = /** @type {NestableAtRuleThis} */ (/** @type {unknown} */ (this));
95
- if (!self.features || !Array.isArray(self.features.value) || self.features.value.length < 1) {
96
- return;
97
- }
98
-
99
- const exprValues = /** @type {Node[]} */ (self.features.value);
100
- /** @type {Node | undefined} */
101
- let expr;
102
- /** @type {Node | undefined} */
103
- let paren;
104
-
105
- for (let index = 0; index < exprValues.length; ++index) {
106
- expr = exprValues[index];
107
-
108
- if ((expr.type === 'Keyword' || expr.type === 'Variable')
109
- && index + 1 < exprValues.length
110
- && (/** @type {Node & { noSpacing?: boolean }} */ (expr).noSpacing || /** @type {Node & { noSpacing?: boolean }} */ (expr).noSpacing == null)) {
111
- paren = exprValues[index + 1];
112
-
113
- if (paren.type === 'Paren' && /** @type {Node & { noSpacing?: boolean }} */ (paren).noSpacing) {
114
- exprValues[index]= new Expression([expr, paren]);
115
- exprValues.splice(index + 1, 1);
116
- /** @type {Node & { noSpacing?: boolean }} */ (exprValues[index]).noSpacing = true;
117
- }
118
- }
119
- }
120
- },
121
-
122
- /** @param {EvalContext} context */
123
- evalTop(context) {
124
- /** @type {NestableAtRuleThis} */
125
- const self = /** @type {NestableAtRuleThis} */ (/** @type {unknown} */ (this));
126
- self.evalFunction();
127
-
128
- /** @type {Node | Ruleset} */
129
- let result = self;
130
-
131
- // Render all dependent Media blocks.
132
- if (context.mediaBlocks.length > 1) {
133
- const selectors = (new Selector([], null, null, self.getIndex(), self.fileInfo())).createEmptySelectors();
134
- result = new Ruleset(selectors, context.mediaBlocks);
135
- /** @type {Ruleset & { multiMedia?: boolean }} */ (result).multiMedia = true;
136
- result.copyVisibilityInfo(self.visibilityInfo());
137
- self.setParent(result, self);
138
- }
139
-
140
- delete context.mediaBlocks;
141
- delete context.mediaPath;
142
-
143
- return result;
144
- },
145
-
146
- /** @param {EvalContext} context */
147
- evalNested(context) {
148
- /** @type {NestableAtRuleThis} */
149
- const self = /** @type {NestableAtRuleThis} */ (/** @type {unknown} */ (this));
150
- self.evalFunction();
151
-
152
- let i;
153
- /** @type {Node | Node[]} */
154
- let value;
155
- const path = context.mediaPath.concat([self]);
156
-
157
- // Extract the media-query conditions separated with `,` (OR).
158
- for (i = 0; i < path.length; i++) {
159
- if (path[i].type !== self.type) {
160
- const blockIndex = context.mediaBlocks.indexOf(self);
161
- if (blockIndex > -1) {
162
- context.mediaBlocks.splice(blockIndex, 1);
163
- }
164
- return self;
165
- }
166
-
167
- value = /** @type {NestableAtRuleThis} */ (path[i]).features instanceof Value ?
168
- /** @type {Node[]} */ (/** @type {NestableAtRuleThis} */ (path[i]).features.value) : /** @type {NestableAtRuleThis} */ (path[i]).features;
169
- path[i] = /** @type {Node} */ (/** @type {unknown} */ (Array.isArray(value) ? value : [value]));
170
- }
171
-
172
- // Trace all permutations to generate the resulting media-query.
173
- //
174
- // (a, b and c) with nested (d, e) ->
175
- // a and d
176
- // a and e
177
- // b and c and d
178
- // b and c and e
179
- self.features = new Value(self.permute(/** @type {Node[][]} */ (/** @type {unknown} */ (path))).map(
180
- /** @param {Node | Node[]} path */
181
- path => {
182
- path = /** @type {Node[]} */ (path).map(
183
- /** @param {Node & { toCSS?: Function }} fragment */
184
- fragment => fragment.toCSS ? fragment : new Anonymous(/** @type {string} */ (/** @type {unknown} */ (fragment))));
185
-
186
- // A media type nested inside conditions must move ahead of them
187
- // so the flattened query stays valid (issue #3694, #3764).
188
- const types = /** @type {Node[]} */ (path).filter(startsWithMediaType);
189
- if (types.length && types.length < /** @type {Node[]} */ (path).length) {
190
- path = types.concat(/** @type {Node[]} */ (path).filter(f => !startsWithMediaType(f)));
191
- }
192
-
193
- for (i = /** @type {Node[]} */ (path).length - 1; i > 0; i--) {
194
- /** @type {Node[]} */ (path).splice(i, 0, new Anonymous('and'));
195
- }
196
-
197
- return new Expression(/** @type {Node[]} */ (path));
198
- }));
199
- self.setParent(self.features, self);
200
-
201
- // Fake a tree-node that doesn't output anything.
202
- return new Ruleset([], []);
203
- },
204
-
205
- /**
206
- * @param {Node[][]} arr
207
- * @returns {Node[][]}
208
- */
209
- permute(arr) {
210
- if (arr.length === 0) {
211
- return [];
212
- } else if (arr.length === 1) {
213
- return /** @type {Node[][]} */ (/** @type {unknown} */ (arr[0]));
214
- } else {
215
- /** @type {Node[][]} */
216
- const result = [];
217
- const rest = this.permute(arr.slice(1));
218
- for (let i = 0; i < rest.length; i++) {
219
- for (let j = 0; j < arr[0].length; j++) {
220
- result.push([arr[0][j]].concat(rest[i]));
221
- }
222
- }
223
- return result;
224
- }
225
- },
226
-
227
- /** @param {Selector[] | undefined} selectors */
228
- bubbleSelectors(selectors) {
229
- /** @type {NestableAtRuleThis} */
230
- const self = /** @type {NestableAtRuleThis} */ (/** @type {unknown} */ (this));
231
- if (!selectors) {
232
- return;
233
- }
234
- self.rules = [new Ruleset(utils.copyArray(selectors), [self.rules[0]])];
235
- self.setParent(self.rules, self);
236
- }
237
- };
238
-
239
- export default NestableAtRulePrototype;