less 3.10.0-beta.2 → 3.10.3

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 (124) hide show
  1. package/.DS_Store +0 -0
  2. package/bin/lessc +6652 -5505
  3. package/dist/less.cjs.js +6593 -5448
  4. package/dist/less.js +10 -6
  5. package/dist/less.min.js +2 -2
  6. package/dist/less.min.js.map +1 -1
  7. package/lib/less/constants.js +13 -0
  8. package/lib/less/contexts.js +164 -0
  9. package/lib/less/data/colors.js +150 -0
  10. package/lib/less/data/index.js +4 -0
  11. package/lib/less/data/unit-conversions.js +21 -0
  12. package/lib/less/default-options.js +68 -0
  13. package/lib/less/environment/abstract-file-manager.js +127 -0
  14. package/lib/less/environment/abstract-plugin-loader.js +187 -0
  15. package/lib/less/environment/environment-api.js +25 -0
  16. package/lib/less/environment/environment.js +59 -0
  17. package/lib/less/environment/file-manager-api.js +103 -0
  18. package/lib/less/functions/boolean.js +13 -0
  19. package/lib/less/functions/color-blending.js +84 -0
  20. package/lib/less/functions/color.js +417 -0
  21. package/lib/less/functions/data-uri.js +74 -0
  22. package/lib/less/functions/default.js +25 -0
  23. package/lib/less/functions/function-caller.js +49 -0
  24. package/lib/less/functions/function-registry.js +35 -0
  25. package/lib/less/functions/index.js +33 -0
  26. package/lib/less/functions/list.js +143 -0
  27. package/lib/less/functions/math-helper.js +15 -0
  28. package/lib/less/functions/math.js +28 -0
  29. package/lib/less/functions/number.js +89 -0
  30. package/lib/less/functions/string.js +36 -0
  31. package/lib/less/functions/svg.js +87 -0
  32. package/lib/less/functions/types.js +70 -0
  33. package/lib/less/import-manager.js +169 -0
  34. package/lib/less/index.js +92 -0
  35. package/lib/less/less-error.js +140 -0
  36. package/lib/less/logger.js +34 -0
  37. package/lib/less/parse-tree.js +66 -0
  38. package/lib/less/parse.js +89 -0
  39. package/lib/less/parser/chunker.js +122 -0
  40. package/lib/less/parser/parser-input.js +390 -0
  41. package/lib/less/parser/parser.js +2418 -0
  42. package/lib/less/plugin-manager.js +168 -0
  43. package/lib/less/render.js +42 -0
  44. package/lib/less/source-map-builder.js +77 -0
  45. package/lib/less/source-map-output.js +149 -0
  46. package/lib/less/transform-tree.js +96 -0
  47. package/lib/less/tree/anonymous.js +37 -0
  48. package/lib/less/tree/assignment.js +33 -0
  49. package/lib/less/tree/atrule.js +165 -0
  50. package/lib/less/tree/attribute.js +34 -0
  51. package/lib/less/tree/call.js +105 -0
  52. package/lib/less/tree/color.js +246 -0
  53. package/lib/less/tree/combinator.js +29 -0
  54. package/lib/less/tree/comment.js +29 -0
  55. package/lib/less/tree/condition.js +43 -0
  56. package/lib/less/tree/debug-info.js +34 -0
  57. package/lib/less/tree/declaration.js +115 -0
  58. package/lib/less/tree/detached-ruleset.js +30 -0
  59. package/lib/less/tree/dimension.js +179 -0
  60. package/lib/less/tree/element.js +73 -0
  61. package/lib/less/tree/expression.js +74 -0
  62. package/lib/less/tree/extend.js +66 -0
  63. package/lib/less/tree/import.js +184 -0
  64. package/lib/less/tree/index.js +54 -0
  65. package/lib/less/tree/javascript.js +33 -0
  66. package/lib/less/tree/js-eval-node.js +58 -0
  67. package/lib/less/tree/keyword.js +21 -0
  68. package/lib/less/tree/media.js +154 -0
  69. package/lib/less/tree/mixin-call.js +215 -0
  70. package/lib/less/tree/mixin-definition.js +229 -0
  71. package/lib/less/tree/namespace-value.js +87 -0
  72. package/lib/less/tree/negative.js +26 -0
  73. package/lib/less/tree/node.js +178 -0
  74. package/lib/less/tree/operation.js +62 -0
  75. package/lib/less/tree/paren.js +22 -0
  76. package/lib/less/tree/property.js +77 -0
  77. package/lib/less/tree/quoted.js +70 -0
  78. package/lib/less/tree/ruleset.js +867 -0
  79. package/lib/less/tree/selector.js +146 -0
  80. package/lib/less/tree/unicode-descriptor.js +13 -0
  81. package/lib/less/tree/unit.js +141 -0
  82. package/lib/less/tree/url.js +65 -0
  83. package/lib/less/tree/value.js +44 -0
  84. package/lib/less/tree/variable-call.js +46 -0
  85. package/lib/less/tree/variable.js +67 -0
  86. package/lib/less/utils.js +122 -0
  87. package/lib/less/visitors/extend-visitor.js +505 -0
  88. package/lib/less/visitors/import-sequencer.js +58 -0
  89. package/lib/less/visitors/import-visitor.js +189 -0
  90. package/lib/less/visitors/index.js +15 -0
  91. package/lib/less/visitors/join-selector-visitor.js +57 -0
  92. package/lib/less/visitors/set-tree-visibility-visitor.js +45 -0
  93. package/lib/less/visitors/to-css-visitor.js +363 -0
  94. package/lib/less/visitors/visitor.js +163 -0
  95. package/lib/less-browser/add-default-options.js +49 -0
  96. package/lib/less-browser/bootstrap.js +69 -0
  97. package/lib/less-browser/browser.js +65 -0
  98. package/lib/less-browser/cache.js +43 -0
  99. package/lib/less-browser/error-reporting.js +170 -0
  100. package/lib/less-browser/file-manager.js +113 -0
  101. package/lib/less-browser/image-size.js +28 -0
  102. package/lib/less-browser/index.js +285 -0
  103. package/lib/less-browser/log-listener.js +42 -0
  104. package/lib/less-browser/plugin-loader.js +26 -0
  105. package/lib/less-browser/utils.js +24 -0
  106. package/lib/less-node/environment.js +16 -0
  107. package/lib/less-node/file-manager.js +177 -0
  108. package/lib/less-node/fs.js +10 -0
  109. package/lib/less-node/image-size.js +58 -0
  110. package/lib/less-node/index.js +27 -0
  111. package/lib/less-node/lessc-helper.js +89 -0
  112. package/lib/less-node/plugin-loader.js +53 -0
  113. package/lib/less-node/url-file-manager.js +49 -0
  114. package/lib/lessc.js +557 -0
  115. package/lib/source-map/source-map-0.1.31.js +1933 -0
  116. package/lib/source-map/source-map-footer.js +4 -0
  117. package/lib/source-map/source-map-header.js +3 -0
  118. package/package.json +1 -1
  119. package/test/.DS_Store +0 -0
  120. package/test/browser/less.min.js +11 -0
  121. package/test/browser/less.min.js.map +1 -0
  122. package/test/css/css-escapes.css +1 -0
  123. package/test/less/css-escapes.less +3 -1
  124. package/.git/index +0 -0
@@ -0,0 +1,867 @@
1
+ import Node from './node';
2
+ import Declaration from './declaration';
3
+ import Keyword from './keyword';
4
+ import Comment from './comment';
5
+ import Paren from './paren';
6
+ import Selector from './selector';
7
+ import Element from './element';
8
+ import Anonymous from './anonymous';
9
+ import contexts from '../contexts';
10
+ import globalFunctionRegistry from '../functions/function-registry';
11
+ import defaultFunc from '../functions/default';
12
+ import getDebugInfo from './debug-info';
13
+ import * as utils from '../utils';
14
+
15
+ class Ruleset extends Node {
16
+ constructor(selectors, rules, strictImports, visibilityInfo) {
17
+ super();
18
+
19
+ this.selectors = selectors;
20
+ this.rules = rules;
21
+ this._lookups = {};
22
+ this._variables = null;
23
+ this._properties = null;
24
+ this.strictImports = strictImports;
25
+ this.copyVisibilityInfo(visibilityInfo);
26
+ this.allowRoot = true;
27
+
28
+ this.setParent(this.selectors, this);
29
+ this.setParent(this.rules, this);
30
+
31
+ }
32
+
33
+ isRulesetLike() {
34
+ return true;
35
+ }
36
+
37
+ accept(visitor) {
38
+ if (this.paths) {
39
+ this.paths = visitor.visitArray(this.paths, true);
40
+ } else if (this.selectors) {
41
+ this.selectors = visitor.visitArray(this.selectors);
42
+ }
43
+ if (this.rules && this.rules.length) {
44
+ this.rules = visitor.visitArray(this.rules);
45
+ }
46
+ }
47
+
48
+ eval(context) {
49
+ const that = this;
50
+ let selectors;
51
+ let selCnt;
52
+ let selector;
53
+ let i;
54
+ let hasVariable;
55
+ let hasOnePassingSelector = false;
56
+
57
+ if (this.selectors && (selCnt = this.selectors.length)) {
58
+ selectors = new Array(selCnt);
59
+ defaultFunc.error({
60
+ type: 'Syntax',
61
+ message: 'it is currently only allowed in parametric mixin guards,'
62
+ });
63
+
64
+ for (i = 0; i < selCnt; i++) {
65
+ selector = this.selectors[i].eval(context);
66
+ for (var j = 0; j < selector.elements.length; j++) {
67
+ if (selector.elements[j].isVariable) {
68
+ hasVariable = true;
69
+ break;
70
+ }
71
+ }
72
+ selectors[i] = selector;
73
+ if (selector.evaldCondition) {
74
+ hasOnePassingSelector = true;
75
+ }
76
+ }
77
+
78
+ if (hasVariable) {
79
+ const toParseSelectors = new Array(selCnt);
80
+ for (i = 0; i < selCnt; i++) {
81
+ selector = selectors[i];
82
+ toParseSelectors[i] = selector.toCSS(context);
83
+ }
84
+ this.parse.parseNode(
85
+ toParseSelectors.join(','),
86
+ ["selectors"],
87
+ selectors[0].getIndex(),
88
+ selectors[0].fileInfo(),
89
+ (err, result) => {
90
+ if (result) {
91
+ selectors = utils.flattenArray(result);
92
+ }
93
+ });
94
+ }
95
+
96
+ defaultFunc.reset();
97
+ } else {
98
+ hasOnePassingSelector = true;
99
+ }
100
+
101
+ let rules = this.rules ? utils.copyArray(this.rules) : null;
102
+ const ruleset = new Ruleset(selectors, rules, this.strictImports, this.visibilityInfo());
103
+ let rule;
104
+ let subRule;
105
+
106
+ ruleset.originalRuleset = this;
107
+ ruleset.root = this.root;
108
+ ruleset.firstRoot = this.firstRoot;
109
+ ruleset.allowImports = this.allowImports;
110
+
111
+ if (this.debugInfo) {
112
+ ruleset.debugInfo = this.debugInfo;
113
+ }
114
+
115
+ if (!hasOnePassingSelector) {
116
+ rules.length = 0;
117
+ }
118
+
119
+ // inherit a function registry from the frames stack when possible;
120
+ // otherwise from the global registry
121
+ ruleset.functionRegistry = (frames => {
122
+ let i = 0;
123
+ const n = frames.length;
124
+ let found;
125
+ for ( ; i !== n ; ++i ) {
126
+ found = frames[ i ].functionRegistry;
127
+ if ( found ) { return found; }
128
+ }
129
+ return globalFunctionRegistry;
130
+ })(context.frames).inherit();
131
+
132
+ // push the current ruleset to the frames stack
133
+ const ctxFrames = context.frames;
134
+ ctxFrames.unshift(ruleset);
135
+
136
+ // currrent selectors
137
+ let ctxSelectors = context.selectors;
138
+ if (!ctxSelectors) {
139
+ context.selectors = ctxSelectors = [];
140
+ }
141
+ ctxSelectors.unshift(this.selectors);
142
+
143
+ // Evaluate imports
144
+ if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) {
145
+ ruleset.evalImports(context);
146
+ }
147
+
148
+ // Store the frames around mixin definitions,
149
+ // so they can be evaluated like closures when the time comes.
150
+ const rsRules = ruleset.rules;
151
+ for (i = 0; (rule = rsRules[i]); i++) {
152
+ if (rule.evalFirst) {
153
+ rsRules[i] = rule.eval(context);
154
+ }
155
+ }
156
+
157
+ const mediaBlockCount = (context.mediaBlocks && context.mediaBlocks.length) || 0;
158
+
159
+ // Evaluate mixin calls.
160
+ for (i = 0; (rule = rsRules[i]); i++) {
161
+ if (rule.type === 'MixinCall') {
162
+ /* jshint loopfunc:true */
163
+ rules = rule.eval(context).filter(r => {
164
+ if ((r instanceof Declaration) && r.variable) {
165
+ // do not pollute the scope if the variable is
166
+ // already there. consider returning false here
167
+ // but we need a way to "return" variable from mixins
168
+ return !(ruleset.variable(r.name));
169
+ }
170
+ return true;
171
+ });
172
+ rsRules.splice(...[i, 1].concat(rules));
173
+ i += rules.length - 1;
174
+ ruleset.resetCache();
175
+ } else if (rule.type === 'VariableCall') {
176
+ /* jshint loopfunc:true */
177
+ rules = rule.eval(context).rules.filter(r => {
178
+ if ((r instanceof Declaration) && r.variable) {
179
+ // do not pollute the scope at all
180
+ return false;
181
+ }
182
+ return true;
183
+ });
184
+ rsRules.splice(...[i, 1].concat(rules));
185
+ i += rules.length - 1;
186
+ ruleset.resetCache();
187
+ }
188
+ }
189
+
190
+ // Evaluate everything else
191
+ for (i = 0; (rule = rsRules[i]); i++) {
192
+ if (!rule.evalFirst) {
193
+ rsRules[i] = rule = rule.eval ? rule.eval(context) : rule;
194
+ }
195
+ }
196
+
197
+ // Evaluate everything else
198
+ for (i = 0; (rule = rsRules[i]); i++) {
199
+ // for rulesets, check if it is a css guard and can be removed
200
+ if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) {
201
+ // check if it can be folded in (e.g. & where)
202
+ if (rule.selectors[0] && rule.selectors[0].isJustParentSelector()) {
203
+ rsRules.splice(i--, 1);
204
+
205
+ for (var j = 0; (subRule = rule.rules[j]); j++) {
206
+ if (subRule instanceof Node) {
207
+ subRule.copyVisibilityInfo(rule.visibilityInfo());
208
+ if (!(subRule instanceof Declaration) || !subRule.variable) {
209
+ rsRules.splice(++i, 0, subRule);
210
+ }
211
+ }
212
+ }
213
+ }
214
+ }
215
+ }
216
+
217
+ // Pop the stack
218
+ ctxFrames.shift();
219
+ ctxSelectors.shift();
220
+
221
+ if (context.mediaBlocks) {
222
+ for (i = mediaBlockCount; i < context.mediaBlocks.length; i++) {
223
+ context.mediaBlocks[i].bubbleSelectors(selectors);
224
+ }
225
+ }
226
+
227
+ return ruleset;
228
+ }
229
+
230
+ evalImports(context) {
231
+ const rules = this.rules;
232
+ let i;
233
+ let importRules;
234
+ if (!rules) { return; }
235
+
236
+ for (i = 0; i < rules.length; i++) {
237
+ if (rules[i].type === 'Import') {
238
+ importRules = rules[i].eval(context);
239
+ if (importRules && (importRules.length || importRules.length === 0)) {
240
+ rules.splice(...[i, 1].concat(importRules));
241
+ i += importRules.length - 1;
242
+ } else {
243
+ rules.splice(i, 1, importRules);
244
+ }
245
+ this.resetCache();
246
+ }
247
+ }
248
+ }
249
+
250
+ makeImportant() {
251
+ const result = new Ruleset(this.selectors, this.rules.map(r => {
252
+ if (r.makeImportant) {
253
+ return r.makeImportant();
254
+ } else {
255
+ return r;
256
+ }
257
+ }), this.strictImports, this.visibilityInfo());
258
+
259
+ return result;
260
+ }
261
+
262
+ matchArgs(args) {
263
+ return !args || args.length === 0;
264
+ }
265
+
266
+ // lets you call a css selector with a guard
267
+ matchCondition(args, context) {
268
+ const lastSelector = this.selectors[this.selectors.length - 1];
269
+ if (!lastSelector.evaldCondition) {
270
+ return false;
271
+ }
272
+ if (lastSelector.condition &&
273
+ !lastSelector.condition.eval(
274
+ new contexts.Eval(context,
275
+ context.frames))) {
276
+ return false;
277
+ }
278
+ return true;
279
+ }
280
+
281
+ resetCache() {
282
+ this._rulesets = null;
283
+ this._variables = null;
284
+ this._properties = null;
285
+ this._lookups = {};
286
+ }
287
+
288
+ variables() {
289
+ if (!this._variables) {
290
+ this._variables = !this.rules ? {} : this.rules.reduce((hash, r) => {
291
+ if (r instanceof Declaration && r.variable === true) {
292
+ hash[r.name] = r;
293
+ }
294
+ // when evaluating variables in an import statement, imports have not been eval'd
295
+ // so we need to go inside import statements.
296
+ // guard against root being a string (in the case of inlined less)
297
+ if (r.type === 'Import' && r.root && r.root.variables) {
298
+ const vars = r.root.variables();
299
+ for (const name in vars) {
300
+ if (vars.hasOwnProperty(name)) {
301
+ hash[name] = r.root.variable(name);
302
+ }
303
+ }
304
+ }
305
+ return hash;
306
+ }, {});
307
+ }
308
+ return this._variables;
309
+ }
310
+
311
+ properties() {
312
+ if (!this._properties) {
313
+ this._properties = !this.rules ? {} : this.rules.reduce((hash, r) => {
314
+ if (r instanceof Declaration && r.variable !== true) {
315
+ const name = (r.name.length === 1) && (r.name[0] instanceof Keyword) ?
316
+ r.name[0].value : r.name;
317
+ // Properties don't overwrite as they can merge
318
+ if (!hash[`$${name}`]) {
319
+ hash[`$${name}`] = [ r ];
320
+ }
321
+ else {
322
+ hash[`$${name}`].push(r);
323
+ }
324
+ }
325
+ return hash;
326
+ }, {});
327
+ }
328
+ return this._properties;
329
+ }
330
+
331
+ variable(name) {
332
+ const decl = this.variables()[name];
333
+ if (decl) {
334
+ return this.parseValue(decl);
335
+ }
336
+ }
337
+
338
+ property(name) {
339
+ const decl = this.properties()[name];
340
+ if (decl) {
341
+ return this.parseValue(decl);
342
+ }
343
+ }
344
+
345
+ lastDeclaration() {
346
+ for (let i = this.rules.length; i > 0; i--) {
347
+ const decl = this.rules[i - 1];
348
+ if (decl instanceof Declaration) {
349
+ return this.parseValue(decl);
350
+ }
351
+ }
352
+ }
353
+
354
+ parseValue(toParse) {
355
+ const self = this;
356
+ function transformDeclaration(decl) {
357
+ if (decl.value instanceof Anonymous && !decl.parsed) {
358
+ if (typeof decl.value.value === 'string') {
359
+ this.parse.parseNode(
360
+ decl.value.value,
361
+ ['value', 'important'],
362
+ decl.value.getIndex(),
363
+ decl.fileInfo(),
364
+ (err, result) => {
365
+ if (err) {
366
+ decl.parsed = true;
367
+ }
368
+ if (result) {
369
+ decl.value = result[0];
370
+ decl.important = result[1] || '';
371
+ decl.parsed = true;
372
+ }
373
+ });
374
+ } else {
375
+ decl.parsed = true;
376
+ }
377
+
378
+ return decl;
379
+ }
380
+ else {
381
+ return decl;
382
+ }
383
+ }
384
+ if (!Array.isArray(toParse)) {
385
+ return transformDeclaration.call(self, toParse);
386
+ }
387
+ else {
388
+ const nodes = [];
389
+ toParse.forEach(n => {
390
+ nodes.push(transformDeclaration.call(self, n));
391
+ });
392
+ return nodes;
393
+ }
394
+ }
395
+
396
+ rulesets() {
397
+ if (!this.rules) { return []; }
398
+
399
+ const filtRules = [];
400
+ const rules = this.rules;
401
+ let i;
402
+ let rule;
403
+
404
+ for (i = 0; (rule = rules[i]); i++) {
405
+ if (rule.isRuleset) {
406
+ filtRules.push(rule);
407
+ }
408
+ }
409
+
410
+ return filtRules;
411
+ }
412
+
413
+ prependRule(rule) {
414
+ const rules = this.rules;
415
+ if (rules) {
416
+ rules.unshift(rule);
417
+ } else {
418
+ this.rules = [ rule ];
419
+ }
420
+ this.setParent(rule, this);
421
+ }
422
+
423
+ find(selector, self = this, filter) {
424
+ const rules = [];
425
+ let match;
426
+ let foundMixins;
427
+ const key = selector.toCSS();
428
+
429
+ if (key in this._lookups) { return this._lookups[key]; }
430
+
431
+ this.rulesets().forEach(rule => {
432
+ if (rule !== self) {
433
+ for (let j = 0; j < rule.selectors.length; j++) {
434
+ match = selector.match(rule.selectors[j]);
435
+ if (match) {
436
+ if (selector.elements.length > match) {
437
+ if (!filter || filter(rule)) {
438
+ foundMixins = rule.find(new Selector(selector.elements.slice(match)), self, filter);
439
+ for (let i = 0; i < foundMixins.length; ++i) {
440
+ foundMixins[i].path.push(rule);
441
+ }
442
+ Array.prototype.push.apply(rules, foundMixins);
443
+ }
444
+ } else {
445
+ rules.push({ rule, path: []});
446
+ }
447
+ break;
448
+ }
449
+ }
450
+ }
451
+ });
452
+ this._lookups[key] = rules;
453
+ return rules;
454
+ }
455
+
456
+ genCSS(context, output) {
457
+ let i;
458
+ let j;
459
+ const charsetRuleNodes = [];
460
+ let ruleNodes = [];
461
+
462
+ let // Line number debugging
463
+ debugInfo;
464
+
465
+ let rule;
466
+ let path;
467
+
468
+ context.tabLevel = (context.tabLevel || 0);
469
+
470
+ if (!this.root) {
471
+ context.tabLevel++;
472
+ }
473
+
474
+ const tabRuleStr = context.compress ? '' : Array(context.tabLevel + 1).join(' ');
475
+ const tabSetStr = context.compress ? '' : Array(context.tabLevel).join(' ');
476
+ let sep;
477
+
478
+ let charsetNodeIndex = 0;
479
+ let importNodeIndex = 0;
480
+ for (i = 0; (rule = this.rules[i]); i++) {
481
+ if (rule instanceof Comment) {
482
+ if (importNodeIndex === i) {
483
+ importNodeIndex++;
484
+ }
485
+ ruleNodes.push(rule);
486
+ } else if (rule.isCharset && rule.isCharset()) {
487
+ ruleNodes.splice(charsetNodeIndex, 0, rule);
488
+ charsetNodeIndex++;
489
+ importNodeIndex++;
490
+ } else if (rule.type === 'Import') {
491
+ ruleNodes.splice(importNodeIndex, 0, rule);
492
+ importNodeIndex++;
493
+ } else {
494
+ ruleNodes.push(rule);
495
+ }
496
+ }
497
+ ruleNodes = charsetRuleNodes.concat(ruleNodes);
498
+
499
+ // If this is the root node, we don't render
500
+ // a selector, or {}.
501
+ if (!this.root) {
502
+ debugInfo = getDebugInfo(context, this, tabSetStr);
503
+
504
+ if (debugInfo) {
505
+ output.add(debugInfo);
506
+ output.add(tabSetStr);
507
+ }
508
+
509
+ const paths = this.paths;
510
+ const pathCnt = paths.length;
511
+ let pathSubCnt;
512
+
513
+ sep = context.compress ? ',' : (`,\n${tabSetStr}`);
514
+
515
+ for (i = 0; i < pathCnt; i++) {
516
+ path = paths[i];
517
+ if (!(pathSubCnt = path.length)) { continue; }
518
+ if (i > 0) { output.add(sep); }
519
+
520
+ context.firstSelector = true;
521
+ path[0].genCSS(context, output);
522
+
523
+ context.firstSelector = false;
524
+ for (j = 1; j < pathSubCnt; j++) {
525
+ path[j].genCSS(context, output);
526
+ }
527
+ }
528
+
529
+ output.add((context.compress ? '{' : ' {\n') + tabRuleStr);
530
+ }
531
+
532
+ // Compile rules and rulesets
533
+ for (i = 0; (rule = ruleNodes[i]); i++) {
534
+
535
+ if (i + 1 === ruleNodes.length) {
536
+ context.lastRule = true;
537
+ }
538
+
539
+ const currentLastRule = context.lastRule;
540
+ if (rule.isRulesetLike(rule)) {
541
+ context.lastRule = false;
542
+ }
543
+
544
+ if (rule.genCSS) {
545
+ rule.genCSS(context, output);
546
+ } else if (rule.value) {
547
+ output.add(rule.value.toString());
548
+ }
549
+
550
+ context.lastRule = currentLastRule;
551
+
552
+ if (!context.lastRule && rule.isVisible()) {
553
+ output.add(context.compress ? '' : (`\n${tabRuleStr}`));
554
+ } else {
555
+ context.lastRule = false;
556
+ }
557
+ }
558
+
559
+ if (!this.root) {
560
+ output.add((context.compress ? '}' : `\n${tabSetStr}}`));
561
+ context.tabLevel--;
562
+ }
563
+
564
+ if (!output.isEmpty() && !context.compress && this.firstRoot) {
565
+ output.add('\n');
566
+ }
567
+ }
568
+
569
+ joinSelectors(paths, context, selectors) {
570
+ for (let s = 0; s < selectors.length; s++) {
571
+ this.joinSelector(paths, context, selectors[s]);
572
+ }
573
+ }
574
+
575
+ joinSelector(paths, context, selector) {
576
+ function createParenthesis(elementsToPak, originalElement) {
577
+ let replacementParen;
578
+ let j;
579
+ if (elementsToPak.length === 0) {
580
+ replacementParen = new Paren(elementsToPak[0]);
581
+ } else {
582
+ const insideParent = new Array(elementsToPak.length);
583
+ for (j = 0; j < elementsToPak.length; j++) {
584
+ insideParent[j] = new Element(
585
+ null,
586
+ elementsToPak[j],
587
+ originalElement.isVariable,
588
+ originalElement._index,
589
+ originalElement._fileInfo
590
+ );
591
+ }
592
+ replacementParen = new Paren(new Selector(insideParent));
593
+ }
594
+ return replacementParen;
595
+ }
596
+
597
+ function createSelector(containedElement, originalElement) {
598
+ let element;
599
+ let selector;
600
+ element = new Element(null, containedElement, originalElement.isVariable, originalElement._index, originalElement._fileInfo);
601
+ selector = new Selector([element]);
602
+ return selector;
603
+ }
604
+
605
+ // joins selector path from `beginningPath` with selector path in `addPath`
606
+ // `replacedElement` contains element that is being replaced by `addPath`
607
+ // returns concatenated path
608
+ function addReplacementIntoPath(beginningPath, addPath, replacedElement, originalSelector) {
609
+ let newSelectorPath;
610
+ let lastSelector;
611
+ let newJoinedSelector;
612
+ // our new selector path
613
+ newSelectorPath = [];
614
+
615
+ // construct the joined selector - if & is the first thing this will be empty,
616
+ // if not newJoinedSelector will be the last set of elements in the selector
617
+ if (beginningPath.length > 0) {
618
+ newSelectorPath = utils.copyArray(beginningPath);
619
+ lastSelector = newSelectorPath.pop();
620
+ newJoinedSelector = originalSelector.createDerived(utils.copyArray(lastSelector.elements));
621
+ }
622
+ else {
623
+ newJoinedSelector = originalSelector.createDerived([]);
624
+ }
625
+
626
+ if (addPath.length > 0) {
627
+ // /deep/ is a CSS4 selector - (removed, so should deprecate)
628
+ // that is valid without anything in front of it
629
+ // so if the & does not have a combinator that is "" or " " then
630
+ // and there is a combinator on the parent, then grab that.
631
+ // this also allows + a { & .b { .a & { ... though not sure why you would want to do that
632
+ let combinator = replacedElement.combinator;
633
+
634
+ const parentEl = addPath[0].elements[0];
635
+ if (combinator.emptyOrWhitespace && !parentEl.combinator.emptyOrWhitespace) {
636
+ combinator = parentEl.combinator;
637
+ }
638
+ // join the elements so far with the first part of the parent
639
+ newJoinedSelector.elements.push(new Element(
640
+ combinator,
641
+ parentEl.value,
642
+ replacedElement.isVariable,
643
+ replacedElement._index,
644
+ replacedElement._fileInfo
645
+ ));
646
+ newJoinedSelector.elements = newJoinedSelector.elements.concat(addPath[0].elements.slice(1));
647
+ }
648
+
649
+ // now add the joined selector - but only if it is not empty
650
+ if (newJoinedSelector.elements.length !== 0) {
651
+ newSelectorPath.push(newJoinedSelector);
652
+ }
653
+
654
+ // put together the parent selectors after the join (e.g. the rest of the parent)
655
+ if (addPath.length > 1) {
656
+ let restOfPath = addPath.slice(1);
657
+ restOfPath = restOfPath.map(selector => selector.createDerived(selector.elements, []));
658
+ newSelectorPath = newSelectorPath.concat(restOfPath);
659
+ }
660
+ return newSelectorPath;
661
+ }
662
+
663
+ // joins selector path from `beginningPath` with every selector path in `addPaths` array
664
+ // `replacedElement` contains element that is being replaced by `addPath`
665
+ // returns array with all concatenated paths
666
+ function addAllReplacementsIntoPath( beginningPath, addPaths, replacedElement, originalSelector, result) {
667
+ let j;
668
+ for (j = 0; j < beginningPath.length; j++) {
669
+ const newSelectorPath = addReplacementIntoPath(beginningPath[j], addPaths, replacedElement, originalSelector);
670
+ result.push(newSelectorPath);
671
+ }
672
+ return result;
673
+ }
674
+
675
+ function mergeElementsOnToSelectors(elements, selectors) {
676
+ let i;
677
+ let sel;
678
+
679
+ if (elements.length === 0) {
680
+ return ;
681
+ }
682
+ if (selectors.length === 0) {
683
+ selectors.push([ new Selector(elements) ]);
684
+ return;
685
+ }
686
+
687
+ for (i = 0; (sel = selectors[i]); i++) {
688
+ // if the previous thing in sel is a parent this needs to join on to it
689
+ if (sel.length > 0) {
690
+ sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements));
691
+ }
692
+ else {
693
+ sel.push(new Selector(elements));
694
+ }
695
+ }
696
+ }
697
+
698
+ // replace all parent selectors inside `inSelector` by content of `context` array
699
+ // resulting selectors are returned inside `paths` array
700
+ // returns true if `inSelector` contained at least one parent selector
701
+ function replaceParentSelector(paths, context, inSelector) {
702
+ // The paths are [[Selector]]
703
+ // The first list is a list of comma separated selectors
704
+ // The inner list is a list of inheritance separated selectors
705
+ // e.g.
706
+ // .a, .b {
707
+ // .c {
708
+ // }
709
+ // }
710
+ // == [[.a] [.c]] [[.b] [.c]]
711
+ //
712
+ let i;
713
+
714
+ let j;
715
+ let k;
716
+ let currentElements;
717
+ let newSelectors;
718
+ let selectorsMultiplied;
719
+ let sel;
720
+ let el;
721
+ let hadParentSelector = false;
722
+ let length;
723
+ let lastSelector;
724
+ function findNestedSelector(element) {
725
+ let maybeSelector;
726
+ if (!(element.value instanceof Paren)) {
727
+ return null;
728
+ }
729
+
730
+ maybeSelector = element.value.value;
731
+ if (!(maybeSelector instanceof Selector)) {
732
+ return null;
733
+ }
734
+
735
+ return maybeSelector;
736
+ }
737
+
738
+ // the elements from the current selector so far
739
+ currentElements = [];
740
+ // the current list of new selectors to add to the path.
741
+ // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
742
+ // by the parents
743
+ newSelectors = [
744
+ []
745
+ ];
746
+
747
+ for (i = 0; (el = inSelector.elements[i]); i++) {
748
+ // non parent reference elements just get added
749
+ if (el.value !== '&') {
750
+ const nestedSelector = findNestedSelector(el);
751
+ if (nestedSelector != null) {
752
+ // merge the current list of non parent selector elements
753
+ // on to the current list of selectors to add
754
+ mergeElementsOnToSelectors(currentElements, newSelectors);
755
+
756
+ const nestedPaths = [];
757
+ let replaced;
758
+ const replacedNewSelectors = [];
759
+ replaced = replaceParentSelector(nestedPaths, context, nestedSelector);
760
+ hadParentSelector = hadParentSelector || replaced;
761
+ // the nestedPaths array should have only one member - replaceParentSelector does not multiply selectors
762
+ for (k = 0; k < nestedPaths.length; k++) {
763
+ const replacementSelector = createSelector(createParenthesis(nestedPaths[k], el), el);
764
+ addAllReplacementsIntoPath(newSelectors, [replacementSelector], el, inSelector, replacedNewSelectors);
765
+ }
766
+ newSelectors = replacedNewSelectors;
767
+ currentElements = [];
768
+ } else {
769
+ currentElements.push(el);
770
+ }
771
+
772
+ } else {
773
+ hadParentSelector = true;
774
+ // the new list of selectors to add
775
+ selectorsMultiplied = [];
776
+
777
+ // merge the current list of non parent selector elements
778
+ // on to the current list of selectors to add
779
+ mergeElementsOnToSelectors(currentElements, newSelectors);
780
+
781
+ // loop through our current selectors
782
+ for (j = 0; j < newSelectors.length; j++) {
783
+ sel = newSelectors[j];
784
+ // if we don't have any parent paths, the & might be in a mixin so that it can be used
785
+ // whether there are parents or not
786
+ if (context.length === 0) {
787
+ // the combinator used on el should now be applied to the next element instead so that
788
+ // it is not lost
789
+ if (sel.length > 0) {
790
+ sel[0].elements.push(new Element(el.combinator, '', el.isVariable, el._index, el._fileInfo));
791
+ }
792
+ selectorsMultiplied.push(sel);
793
+ }
794
+ else {
795
+ // and the parent selectors
796
+ for (k = 0; k < context.length; k++) {
797
+ // We need to put the current selectors
798
+ // then join the last selector's elements on to the parents selectors
799
+ const newSelectorPath = addReplacementIntoPath(sel, context[k], el, inSelector);
800
+ // add that to our new set of selectors
801
+ selectorsMultiplied.push(newSelectorPath);
802
+ }
803
+ }
804
+ }
805
+
806
+ // our new selectors has been multiplied, so reset the state
807
+ newSelectors = selectorsMultiplied;
808
+ currentElements = [];
809
+ }
810
+ }
811
+
812
+ // if we have any elements left over (e.g. .a& .b == .b)
813
+ // add them on to all the current selectors
814
+ mergeElementsOnToSelectors(currentElements, newSelectors);
815
+
816
+ for (i = 0; i < newSelectors.length; i++) {
817
+ length = newSelectors[i].length;
818
+ if (length > 0) {
819
+ paths.push(newSelectors[i]);
820
+ lastSelector = newSelectors[i][length - 1];
821
+ newSelectors[i][length - 1] = lastSelector.createDerived(lastSelector.elements, inSelector.extendList);
822
+ }
823
+ }
824
+
825
+ return hadParentSelector;
826
+ }
827
+
828
+ function deriveSelector(visibilityInfo, deriveFrom) {
829
+ const newSelector = deriveFrom.createDerived(deriveFrom.elements, deriveFrom.extendList, deriveFrom.evaldCondition);
830
+ newSelector.copyVisibilityInfo(visibilityInfo);
831
+ return newSelector;
832
+ }
833
+
834
+ // joinSelector code follows
835
+ let i;
836
+
837
+ let newPaths;
838
+ let hadParentSelector;
839
+
840
+ newPaths = [];
841
+ hadParentSelector = replaceParentSelector(newPaths, context, selector);
842
+
843
+ if (!hadParentSelector) {
844
+ if (context.length > 0) {
845
+ newPaths = [];
846
+ for (i = 0; i < context.length; i++) {
847
+
848
+ const concatenated = context[i].map(deriveSelector.bind(this, selector.visibilityInfo()));
849
+
850
+ concatenated.push(selector);
851
+ newPaths.push(concatenated);
852
+ }
853
+ }
854
+ else {
855
+ newPaths = [[selector]];
856
+ }
857
+ }
858
+
859
+ for (i = 0; i < newPaths.length; i++) {
860
+ paths.push(newPaths[i]);
861
+ }
862
+ }
863
+ }
864
+
865
+ Ruleset.prototype.type = 'Ruleset';
866
+ Ruleset.prototype.isRuleset = true;
867
+ export default Ruleset;