less 4.9.1 → 5.0.0-alpha.10

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 (134) hide show
  1. package/README.md +23 -5
  2. package/V5-STATUS.md +71 -0
  3. package/bin/lessc +302 -626
  4. package/dist/less-browser-dev.js +2550 -0
  5. package/dist/less-node.cjs +480 -14561
  6. package/index.cjs +5 -2
  7. package/lib/browser-dev.js +86 -0
  8. package/lib/index.js +164 -0
  9. package/lib/lessc-helper.js +64 -0
  10. package/lib/logger.js +76 -0
  11. package/lib/options.d.ts +82 -0
  12. package/lib/options.js +227 -0
  13. package/lib/types.js +20 -0
  14. package/lib/version.js +19 -0
  15. package/package.json +36 -40
  16. package/dist/less.cjs +0 -15218
  17. package/dist/less.js +0 -15218
  18. package/dist/less.min.js +0 -11
  19. package/dist/less.min.js.map +0 -1
  20. package/lib/less/constants.js +0 -13
  21. package/lib/less/contexts.js +0 -165
  22. package/lib/less/data/colors.js +0 -150
  23. package/lib/less/data/index.js +0 -4
  24. package/lib/less/data/unit-conversions.js +0 -21
  25. package/lib/less/default-options.js +0 -89
  26. package/lib/less/deprecation.js +0 -82
  27. package/lib/less/environment/abstract-file-manager.js +0 -140
  28. package/lib/less/environment/abstract-plugin-loader.js +0 -185
  29. package/lib/less/environment/environment-api.ts +0 -21
  30. package/lib/less/environment/environment.js +0 -59
  31. package/lib/less/environment/file-manager-api.ts +0 -77
  32. package/lib/less/functions/boolean.js +0 -29
  33. package/lib/less/functions/color-blending.js +0 -85
  34. package/lib/less/functions/color.js +0 -452
  35. package/lib/less/functions/data-uri.js +0 -74
  36. package/lib/less/functions/default.js +0 -26
  37. package/lib/less/functions/function-caller.js +0 -55
  38. package/lib/less/functions/function-registry.js +0 -36
  39. package/lib/less/functions/index.js +0 -35
  40. package/lib/less/functions/list.js +0 -158
  41. package/lib/less/functions/math-helper.js +0 -22
  42. package/lib/less/functions/math.js +0 -30
  43. package/lib/less/functions/number.js +0 -95
  44. package/lib/less/functions/string.js +0 -39
  45. package/lib/less/functions/style.js +0 -29
  46. package/lib/less/functions/svg.js +0 -87
  47. package/lib/less/functions/types.js +0 -70
  48. package/lib/less/import-manager.js +0 -183
  49. package/lib/less/index.js +0 -95
  50. package/lib/less/less-error.js +0 -166
  51. package/lib/less/logger.js +0 -34
  52. package/lib/less/parse-tree.js +0 -124
  53. package/lib/less/parse.js +0 -87
  54. package/lib/less/parser/lookup-pattern.js +0 -26
  55. package/lib/less/parser/parser-input.js +0 -400
  56. package/lib/less/parser/parser.js +0 -2998
  57. package/lib/less/plugin-manager.js +0 -168
  58. package/lib/less/render.js +0 -41
  59. package/lib/less/source-map-builder.js +0 -82
  60. package/lib/less/source-map-output.js +0 -151
  61. package/lib/less/transform-tree.js +0 -102
  62. package/lib/less/tree/anonymous.js +0 -61
  63. package/lib/less/tree/assignment.js +0 -48
  64. package/lib/less/tree/atrule-syntax.js +0 -8
  65. package/lib/less/tree/atrule.js +0 -340
  66. package/lib/less/tree/attribute.js +0 -63
  67. package/lib/less/tree/call.js +0 -132
  68. package/lib/less/tree/color.js +0 -285
  69. package/lib/less/tree/combinator.js +0 -38
  70. package/lib/less/tree/comment.js +0 -48
  71. package/lib/less/tree/condition.js +0 -65
  72. package/lib/less/tree/container.js +0 -104
  73. package/lib/less/tree/debug-info.js +0 -80
  74. package/lib/less/tree/declaration.js +0 -142
  75. package/lib/less/tree/detached-ruleset.js +0 -45
  76. package/lib/less/tree/dimension.js +0 -215
  77. package/lib/less/tree/element.js +0 -93
  78. package/lib/less/tree/expression.js +0 -100
  79. package/lib/less/tree/extend.js +0 -88
  80. package/lib/less/tree/import.js +0 -299
  81. package/lib/less/tree/index.js +0 -56
  82. package/lib/less/tree/interpolated-variable.js +0 -106
  83. package/lib/less/tree/javascript.js +0 -45
  84. package/lib/less/tree/js-eval-node.js +0 -80
  85. package/lib/less/tree/keyword.js +0 -28
  86. package/lib/less/tree/media.js +0 -90
  87. package/lib/less/tree/merge-rules.js +0 -47
  88. package/lib/less/tree/mixin-call.js +0 -283
  89. package/lib/less/tree/mixin-definition.js +0 -319
  90. package/lib/less/tree/namespace-value.js +0 -95
  91. package/lib/less/tree/negative.js +0 -37
  92. package/lib/less/tree/nested-at-rule.js +0 -239
  93. package/lib/less/tree/node.js +0 -310
  94. package/lib/less/tree/operation.js +0 -81
  95. package/lib/less/tree/paren.js +0 -41
  96. package/lib/less/tree/property.js +0 -96
  97. package/lib/less/tree/query-in-parens.js +0 -64
  98. package/lib/less/tree/quoted.js +0 -131
  99. package/lib/less/tree/ruleset.js +0 -1068
  100. package/lib/less/tree/selector.js +0 -201
  101. package/lib/less/tree/unicode-descriptor.js +0 -14
  102. package/lib/less/tree/unit.js +0 -170
  103. package/lib/less/tree/url.js +0 -83
  104. package/lib/less/tree/value.js +0 -60
  105. package/lib/less/tree/variable-call.js +0 -60
  106. package/lib/less/tree/variable.js +0 -85
  107. package/lib/less/utils.js +0 -126
  108. package/lib/less/visitors/extend-visitor.js +0 -505
  109. package/lib/less/visitors/import-sequencer.js +0 -56
  110. package/lib/less/visitors/import-visitor.js +0 -203
  111. package/lib/less/visitors/index.js +0 -15
  112. package/lib/less/visitors/join-selector-visitor.js +0 -65
  113. package/lib/less/visitors/set-tree-visibility-visitor.js +0 -57
  114. package/lib/less/visitors/to-css-visitor.js +0 -332
  115. package/lib/less/visitors/visitor.js +0 -165
  116. package/lib/less-browser/add-default-options.js +0 -53
  117. package/lib/less-browser/bootstrap.js +0 -67
  118. package/lib/less-browser/browser.js +0 -65
  119. package/lib/less-browser/cache.js +0 -43
  120. package/lib/less-browser/error-reporting.js +0 -170
  121. package/lib/less-browser/file-manager.js +0 -112
  122. package/lib/less-browser/image-size.js +0 -28
  123. package/lib/less-browser/index.js +0 -284
  124. package/lib/less-browser/log-listener.js +0 -42
  125. package/lib/less-browser/plugin-loader.js +0 -24
  126. package/lib/less-browser/utils.js +0 -30
  127. package/lib/less-node/environment.js +0 -43
  128. package/lib/less-node/file-manager.js +0 -156
  129. package/lib/less-node/fs.js +0 -14
  130. package/lib/less-node/image-size.js +0 -78
  131. package/lib/less-node/index.js +0 -31
  132. package/lib/less-node/lessc-helper.js +0 -95
  133. package/lib/less-node/plugin-loader.js +0 -62
  134. 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;