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,189 @@
1
+ import contexts from '../contexts';
2
+ import Visitor from './visitor';
3
+ import ImportSequencer from './import-sequencer';
4
+ import * as utils from '../utils';
5
+
6
+ const ImportVisitor = function(importer, finish) {
7
+
8
+ this._visitor = new Visitor(this);
9
+ this._importer = importer;
10
+ this._finish = finish;
11
+ this.context = new contexts.Eval();
12
+ this.importCount = 0;
13
+ this.onceFileDetectionMap = {};
14
+ this.recursionDetector = {};
15
+ this._sequencer = new ImportSequencer(this._onSequencerEmpty.bind(this));
16
+ };
17
+
18
+ ImportVisitor.prototype = {
19
+ isReplacing: false,
20
+ run: function (root) {
21
+ try {
22
+ // process the contents
23
+ this._visitor.visit(root);
24
+ }
25
+ catch (e) {
26
+ this.error = e;
27
+ }
28
+
29
+ this.isFinished = true;
30
+ this._sequencer.tryRun();
31
+ },
32
+ _onSequencerEmpty: function() {
33
+ if (!this.isFinished) {
34
+ return;
35
+ }
36
+ this._finish(this.error);
37
+ },
38
+ visitImport: function (importNode, visitArgs) {
39
+ const inlineCSS = importNode.options.inline;
40
+
41
+ if (!importNode.css || inlineCSS) {
42
+
43
+ const context = new contexts.Eval(this.context, utils.copyArray(this.context.frames));
44
+ const importParent = context.frames[0];
45
+
46
+ this.importCount++;
47
+ if (importNode.isVariableImport()) {
48
+ this._sequencer.addVariableImport(this.processImportNode.bind(this, importNode, context, importParent));
49
+ } else {
50
+ this.processImportNode(importNode, context, importParent);
51
+ }
52
+ }
53
+ visitArgs.visitDeeper = false;
54
+ },
55
+ processImportNode: function(importNode, context, importParent) {
56
+ let evaldImportNode;
57
+ const inlineCSS = importNode.options.inline;
58
+
59
+ try {
60
+ evaldImportNode = importNode.evalForImport(context);
61
+ } catch (e) {
62
+ if (!e.filename) { e.index = importNode.getIndex(); e.filename = importNode.fileInfo().filename; }
63
+ // attempt to eval properly and treat as css
64
+ importNode.css = true;
65
+ // if that fails, this error will be thrown
66
+ importNode.error = e;
67
+ }
68
+
69
+ if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) {
70
+ if (evaldImportNode.options.multiple) {
71
+ context.importMultiple = true;
72
+ }
73
+
74
+ // try appending if we haven't determined if it is css or not
75
+ const tryAppendLessExtension = evaldImportNode.css === undefined;
76
+
77
+ for (let i = 0; i < importParent.rules.length; i++) {
78
+ if (importParent.rules[i] === importNode) {
79
+ importParent.rules[i] = evaldImportNode;
80
+ break;
81
+ }
82
+ }
83
+
84
+ const onImported = this.onImported.bind(this, evaldImportNode, context);
85
+ const sequencedOnImported = this._sequencer.addImport(onImported);
86
+
87
+ this._importer.push(evaldImportNode.getPath(), tryAppendLessExtension, evaldImportNode.fileInfo(),
88
+ evaldImportNode.options, sequencedOnImported);
89
+ } else {
90
+ this.importCount--;
91
+ if (this.isFinished) {
92
+ this._sequencer.tryRun();
93
+ }
94
+ }
95
+ },
96
+ onImported: function (importNode, context, e, root, importedAtRoot, fullPath) {
97
+ if (e) {
98
+ if (!e.filename) {
99
+ e.index = importNode.getIndex(); e.filename = importNode.fileInfo().filename;
100
+ }
101
+ this.error = e;
102
+ }
103
+
104
+ const importVisitor = this;
105
+ const inlineCSS = importNode.options.inline;
106
+ const isPlugin = importNode.options.isPlugin;
107
+ const isOptional = importNode.options.optional;
108
+ const duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector;
109
+
110
+ if (!context.importMultiple) {
111
+ if (duplicateImport) {
112
+ importNode.skip = true;
113
+ } else {
114
+ importNode.skip = () => {
115
+ if (fullPath in importVisitor.onceFileDetectionMap) {
116
+ return true;
117
+ }
118
+ importVisitor.onceFileDetectionMap[fullPath] = true;
119
+ return false;
120
+ };
121
+ }
122
+ }
123
+
124
+ if (!fullPath && isOptional) {
125
+ importNode.skip = true;
126
+ }
127
+
128
+ if (root) {
129
+ importNode.root = root;
130
+ importNode.importedFilename = fullPath;
131
+
132
+ if (!inlineCSS && !isPlugin && (context.importMultiple || !duplicateImport)) {
133
+ importVisitor.recursionDetector[fullPath] = true;
134
+
135
+ const oldContext = this.context;
136
+ this.context = context;
137
+ try {
138
+ this._visitor.visit(root);
139
+ } catch (e) {
140
+ this.error = e;
141
+ }
142
+ this.context = oldContext;
143
+ }
144
+ }
145
+
146
+ importVisitor.importCount--;
147
+
148
+ if (importVisitor.isFinished) {
149
+ importVisitor._sequencer.tryRun();
150
+ }
151
+ },
152
+ visitDeclaration: function (declNode, visitArgs) {
153
+ if (declNode.value.type === 'DetachedRuleset') {
154
+ this.context.frames.unshift(declNode);
155
+ } else {
156
+ visitArgs.visitDeeper = false;
157
+ }
158
+ },
159
+ visitDeclarationOut: function(declNode) {
160
+ if (declNode.value.type === 'DetachedRuleset') {
161
+ this.context.frames.shift();
162
+ }
163
+ },
164
+ visitAtRule: function (atRuleNode, visitArgs) {
165
+ this.context.frames.unshift(atRuleNode);
166
+ },
167
+ visitAtRuleOut: function (atRuleNode) {
168
+ this.context.frames.shift();
169
+ },
170
+ visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
171
+ this.context.frames.unshift(mixinDefinitionNode);
172
+ },
173
+ visitMixinDefinitionOut: function (mixinDefinitionNode) {
174
+ this.context.frames.shift();
175
+ },
176
+ visitRuleset: function (rulesetNode, visitArgs) {
177
+ this.context.frames.unshift(rulesetNode);
178
+ },
179
+ visitRulesetOut: function (rulesetNode) {
180
+ this.context.frames.shift();
181
+ },
182
+ visitMedia: function (mediaNode, visitArgs) {
183
+ this.context.frames.unshift(mediaNode.rules[0]);
184
+ },
185
+ visitMediaOut: function (mediaNode) {
186
+ this.context.frames.shift();
187
+ }
188
+ };
189
+ export default ImportVisitor;
@@ -0,0 +1,15 @@
1
+ import Visitor from './visitor';
2
+ import ImportVisitor from './import-visitor';
3
+ import MarkVisibleSelectorsVisitor from './set-tree-visibility-visitor';
4
+ import ExtendVisitor from './extend-visitor';
5
+ import JoinSelectorVisitor from './join-selector-visitor';
6
+ import ToCSSVisitor from './to-css-visitor';
7
+
8
+ export default {
9
+ Visitor,
10
+ ImportVisitor,
11
+ MarkVisibleSelectorsVisitor,
12
+ ExtendVisitor,
13
+ JoinSelectorVisitor,
14
+ ToCSSVisitor
15
+ };
@@ -0,0 +1,57 @@
1
+ import Visitor from './visitor';
2
+
3
+ class JoinSelectorVisitor {
4
+ constructor() {
5
+ this.contexts = [[]];
6
+ this._visitor = new Visitor(this);
7
+ }
8
+
9
+ run(root) {
10
+ return this._visitor.visit(root);
11
+ }
12
+
13
+ visitDeclaration(declNode, visitArgs) {
14
+ visitArgs.visitDeeper = false;
15
+ }
16
+
17
+ visitMixinDefinition(mixinDefinitionNode, visitArgs) {
18
+ visitArgs.visitDeeper = false;
19
+ }
20
+
21
+ visitRuleset(rulesetNode, visitArgs) {
22
+ const context = this.contexts[this.contexts.length - 1];
23
+ const paths = [];
24
+ let selectors;
25
+
26
+ this.contexts.push(paths);
27
+
28
+ if (!rulesetNode.root) {
29
+ selectors = rulesetNode.selectors;
30
+ if (selectors) {
31
+ selectors = selectors.filter(selector => selector.getIsOutput());
32
+ rulesetNode.selectors = selectors.length ? selectors : (selectors = null);
33
+ if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); }
34
+ }
35
+ if (!selectors) { rulesetNode.rules = null; }
36
+ rulesetNode.paths = paths;
37
+ }
38
+ }
39
+
40
+ visitRulesetOut(rulesetNode) {
41
+ this.contexts.length = this.contexts.length - 1;
42
+ }
43
+
44
+ visitMedia(mediaNode, visitArgs) {
45
+ const context = this.contexts[this.contexts.length - 1];
46
+ mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia);
47
+ }
48
+
49
+ visitAtRule(atRuleNode, visitArgs) {
50
+ const context = this.contexts[this.contexts.length - 1];
51
+ if (atRuleNode.rules && atRuleNode.rules.length) {
52
+ atRuleNode.rules[0].root = (atRuleNode.isRooted || context.length === 0 || null);
53
+ }
54
+ }
55
+ }
56
+
57
+ export default JoinSelectorVisitor;
@@ -0,0 +1,45 @@
1
+ class SetTreeVisibilityVisitor {
2
+ constructor(visible) {
3
+ this.visible = visible;
4
+ }
5
+
6
+ run(root) {
7
+ this.visit(root);
8
+ }
9
+
10
+ visitArray(nodes) {
11
+ if (!nodes) {
12
+ return nodes;
13
+ }
14
+
15
+ const cnt = nodes.length;
16
+ let i;
17
+ for (i = 0; i < cnt; i++) {
18
+ this.visit(nodes[i]);
19
+ }
20
+ return nodes;
21
+ }
22
+
23
+ visit(node) {
24
+ if (!node) {
25
+ return node;
26
+ }
27
+ if (node.constructor === Array) {
28
+ return this.visitArray(node);
29
+ }
30
+
31
+ if (!node.blocksVisibility || node.blocksVisibility()) {
32
+ return node;
33
+ }
34
+ if (this.visible) {
35
+ node.ensureVisibility();
36
+ } else {
37
+ node.ensureInvisibility();
38
+ }
39
+
40
+ node.accept(this);
41
+ return node;
42
+ }
43
+ }
44
+
45
+ export default SetTreeVisibilityVisitor;
@@ -0,0 +1,363 @@
1
+ import tree from '../tree';
2
+ import Visitor from './visitor';
3
+
4
+ class CSSVisitorUtils {
5
+ constructor(context) {
6
+ this._visitor = new Visitor(this);
7
+ this._context = context;
8
+ }
9
+
10
+ containsSilentNonBlockedChild(bodyRules) {
11
+ let rule;
12
+ if (!bodyRules) {
13
+ return false;
14
+ }
15
+ for (let r = 0; r < bodyRules.length; r++) {
16
+ rule = bodyRules[r];
17
+ if (rule.isSilent && rule.isSilent(this._context) && !rule.blocksVisibility()) {
18
+ // the atrule contains something that was referenced (likely by extend)
19
+ // therefore it needs to be shown in output too
20
+ return true;
21
+ }
22
+ }
23
+ return false;
24
+ }
25
+
26
+ keepOnlyVisibleChilds(owner) {
27
+ if (owner && owner.rules) {
28
+ owner.rules = owner.rules.filter(thing => thing.isVisible());
29
+ }
30
+ }
31
+
32
+ isEmpty(owner) {
33
+ return (owner && owner.rules)
34
+ ? (owner.rules.length === 0) : true;
35
+ }
36
+
37
+ hasVisibleSelector(rulesetNode) {
38
+ return (rulesetNode && rulesetNode.paths)
39
+ ? (rulesetNode.paths.length > 0) : false;
40
+ }
41
+
42
+ resolveVisibility(node, originalRules) {
43
+ if (!node.blocksVisibility()) {
44
+ if (this.isEmpty(node) && !this.containsSilentNonBlockedChild(originalRules)) {
45
+ return ;
46
+ }
47
+
48
+ return node;
49
+ }
50
+
51
+ const compiledRulesBody = node.rules[0];
52
+ this.keepOnlyVisibleChilds(compiledRulesBody);
53
+
54
+ if (this.isEmpty(compiledRulesBody)) {
55
+ return ;
56
+ }
57
+
58
+ node.ensureVisibility();
59
+ node.removeVisibilityBlock();
60
+
61
+ return node;
62
+ }
63
+
64
+ isVisibleRuleset(rulesetNode) {
65
+ if (rulesetNode.firstRoot) {
66
+ return true;
67
+ }
68
+
69
+ if (this.isEmpty(rulesetNode)) {
70
+ return false;
71
+ }
72
+
73
+ if (!rulesetNode.root && !this.hasVisibleSelector(rulesetNode)) {
74
+ return false;
75
+ }
76
+
77
+ return true;
78
+ }
79
+ }
80
+
81
+ const ToCSSVisitor = function(context) {
82
+ this._visitor = new Visitor(this);
83
+ this._context = context;
84
+ this.utils = new CSSVisitorUtils(context);
85
+ };
86
+
87
+ ToCSSVisitor.prototype = {
88
+ isReplacing: true,
89
+ run: function (root) {
90
+ return this._visitor.visit(root);
91
+ },
92
+
93
+ visitDeclaration: function (declNode, visitArgs) {
94
+ if (declNode.blocksVisibility() || declNode.variable) {
95
+ return;
96
+ }
97
+ return declNode;
98
+ },
99
+
100
+ visitMixinDefinition: function (mixinNode, visitArgs) {
101
+ // mixin definitions do not get eval'd - this means they keep state
102
+ // so we have to clear that state here so it isn't used if toCSS is called twice
103
+ mixinNode.frames = [];
104
+ },
105
+
106
+ visitExtend: function (extendNode, visitArgs) {
107
+ },
108
+
109
+ visitComment: function (commentNode, visitArgs) {
110
+ if (commentNode.blocksVisibility() || commentNode.isSilent(this._context)) {
111
+ return;
112
+ }
113
+ return commentNode;
114
+ },
115
+
116
+ visitMedia: function(mediaNode, visitArgs) {
117
+ const originalRules = mediaNode.rules[0].rules;
118
+ mediaNode.accept(this._visitor);
119
+ visitArgs.visitDeeper = false;
120
+
121
+ return this.utils.resolveVisibility(mediaNode, originalRules);
122
+ },
123
+
124
+ visitImport: function (importNode, visitArgs) {
125
+ if (importNode.blocksVisibility()) {
126
+ return ;
127
+ }
128
+ return importNode;
129
+ },
130
+
131
+ visitAtRule: function(atRuleNode, visitArgs) {
132
+ if (atRuleNode.rules && atRuleNode.rules.length) {
133
+ return this.visitAtRuleWithBody(atRuleNode, visitArgs);
134
+ } else {
135
+ return this.visitAtRuleWithoutBody(atRuleNode, visitArgs);
136
+ }
137
+ },
138
+
139
+ visitAnonymous: function(anonymousNode, visitArgs) {
140
+ if (!anonymousNode.blocksVisibility()) {
141
+ anonymousNode.accept(this._visitor);
142
+ return anonymousNode;
143
+ }
144
+ },
145
+
146
+ visitAtRuleWithBody: function(atRuleNode, visitArgs) {
147
+ // if there is only one nested ruleset and that one has no path, then it is
148
+ // just fake ruleset
149
+ function hasFakeRuleset(atRuleNode) {
150
+ const bodyRules = atRuleNode.rules;
151
+ return bodyRules.length === 1 && (!bodyRules[0].paths || bodyRules[0].paths.length === 0);
152
+ }
153
+ function getBodyRules(atRuleNode) {
154
+ const nodeRules = atRuleNode.rules;
155
+ if (hasFakeRuleset(atRuleNode)) {
156
+ return nodeRules[0].rules;
157
+ }
158
+
159
+ return nodeRules;
160
+ }
161
+ // it is still true that it is only one ruleset in array
162
+ // this is last such moment
163
+ // process childs
164
+ const originalRules = getBodyRules(atRuleNode);
165
+ atRuleNode.accept(this._visitor);
166
+ visitArgs.visitDeeper = false;
167
+
168
+ if (!this.utils.isEmpty(atRuleNode)) {
169
+ this._mergeRules(atRuleNode.rules[0].rules);
170
+ }
171
+
172
+ return this.utils.resolveVisibility(atRuleNode, originalRules);
173
+ },
174
+
175
+ visitAtRuleWithoutBody: function(atRuleNode, visitArgs) {
176
+ if (atRuleNode.blocksVisibility()) {
177
+ return;
178
+ }
179
+
180
+ if (atRuleNode.name === '@charset') {
181
+ // Only output the debug info together with subsequent @charset definitions
182
+ // a comment (or @media statement) before the actual @charset atrule would
183
+ // be considered illegal css as it has to be on the first line
184
+ if (this.charset) {
185
+ if (atRuleNode.debugInfo) {
186
+ const comment = new tree.Comment(`/* ${atRuleNode.toCSS(this._context).replace(/\n/g, '')} */\n`);
187
+ comment.debugInfo = atRuleNode.debugInfo;
188
+ return this._visitor.visit(comment);
189
+ }
190
+ return;
191
+ }
192
+ this.charset = true;
193
+ }
194
+
195
+ return atRuleNode;
196
+ },
197
+
198
+ checkValidNodes: function(rules, isRoot) {
199
+ if (!rules) {
200
+ return;
201
+ }
202
+
203
+ for (let i = 0; i < rules.length; i++) {
204
+ const ruleNode = rules[i];
205
+ if (isRoot && ruleNode instanceof tree.Declaration && !ruleNode.variable) {
206
+ throw { message: 'Properties must be inside selector blocks. They cannot be in the root',
207
+ index: ruleNode.getIndex(), filename: ruleNode.fileInfo() && ruleNode.fileInfo().filename};
208
+ }
209
+ if (ruleNode instanceof tree.Call) {
210
+ throw { message: `Function '${ruleNode.name}' is undefined`,
211
+ index: ruleNode.getIndex(), filename: ruleNode.fileInfo() && ruleNode.fileInfo().filename};
212
+ }
213
+ if (ruleNode.type && !ruleNode.allowRoot) {
214
+ throw { message: `${ruleNode.type} node returned by a function is not valid here`,
215
+ index: ruleNode.getIndex(), filename: ruleNode.fileInfo() && ruleNode.fileInfo().filename};
216
+ }
217
+ }
218
+ },
219
+
220
+ visitRuleset: function (rulesetNode, visitArgs) {
221
+ // at this point rulesets are nested into each other
222
+ let rule;
223
+
224
+ const rulesets = [];
225
+
226
+ this.checkValidNodes(rulesetNode.rules, rulesetNode.firstRoot);
227
+
228
+ if (!rulesetNode.root) {
229
+ // remove invisible paths
230
+ this._compileRulesetPaths(rulesetNode);
231
+
232
+ // remove rulesets from this ruleset body and compile them separately
233
+ const nodeRules = rulesetNode.rules;
234
+
235
+ let nodeRuleCnt = nodeRules ? nodeRules.length : 0;
236
+ for (let i = 0; i < nodeRuleCnt; ) {
237
+ rule = nodeRules[i];
238
+ if (rule && rule.rules) {
239
+ // visit because we are moving them out from being a child
240
+ rulesets.push(this._visitor.visit(rule));
241
+ nodeRules.splice(i, 1);
242
+ nodeRuleCnt--;
243
+ continue;
244
+ }
245
+ i++;
246
+ }
247
+ // accept the visitor to remove rules and refactor itself
248
+ // then we can decide nogw whether we want it or not
249
+ // compile body
250
+ if (nodeRuleCnt > 0) {
251
+ rulesetNode.accept(this._visitor);
252
+ } else {
253
+ rulesetNode.rules = null;
254
+ }
255
+ visitArgs.visitDeeper = false;
256
+ } else { // if (! rulesetNode.root) {
257
+ rulesetNode.accept(this._visitor);
258
+ visitArgs.visitDeeper = false;
259
+ }
260
+
261
+ if (rulesetNode.rules) {
262
+ this._mergeRules(rulesetNode.rules);
263
+ this._removeDuplicateRules(rulesetNode.rules);
264
+ }
265
+
266
+ // now decide whether we keep the ruleset
267
+ if (this.utils.isVisibleRuleset(rulesetNode)) {
268
+ rulesetNode.ensureVisibility();
269
+ rulesets.splice(0, 0, rulesetNode);
270
+ }
271
+
272
+ if (rulesets.length === 1) {
273
+ return rulesets[0];
274
+ }
275
+ return rulesets;
276
+ },
277
+
278
+ _compileRulesetPaths: function(rulesetNode) {
279
+ if (rulesetNode.paths) {
280
+ rulesetNode.paths = rulesetNode.paths
281
+ .filter(p => {
282
+ let i;
283
+ if (p[0].elements[0].combinator.value === ' ') {
284
+ p[0].elements[0].combinator = new(tree.Combinator)('');
285
+ }
286
+ for (i = 0; i < p.length; i++) {
287
+ if (p[i].isVisible() && p[i].getIsOutput()) {
288
+ return true;
289
+ }
290
+ }
291
+ return false;
292
+ });
293
+ }
294
+ },
295
+
296
+ _removeDuplicateRules: function(rules) {
297
+ if (!rules) { return; }
298
+
299
+ // remove duplicates
300
+ const ruleCache = {};
301
+
302
+ let ruleList;
303
+ let rule;
304
+ let i;
305
+
306
+ for (i = rules.length - 1; i >= 0 ; i--) {
307
+ rule = rules[i];
308
+ if (rule instanceof tree.Declaration) {
309
+ if (!ruleCache[rule.name]) {
310
+ ruleCache[rule.name] = rule;
311
+ } else {
312
+ ruleList = ruleCache[rule.name];
313
+ if (ruleList instanceof tree.Declaration) {
314
+ ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._context)];
315
+ }
316
+ const ruleCSS = rule.toCSS(this._context);
317
+ if (ruleList.indexOf(ruleCSS) !== -1) {
318
+ rules.splice(i, 1);
319
+ } else {
320
+ ruleList.push(ruleCSS);
321
+ }
322
+ }
323
+ }
324
+ }
325
+ },
326
+
327
+ _mergeRules: function(rules) {
328
+ if (!rules) {
329
+ return;
330
+ }
331
+
332
+ const groups = {};
333
+ const groupsArr = [];
334
+
335
+ for (let i = 0; i < rules.length; i++) {
336
+ const rule = rules[i];
337
+ if (rule.merge) {
338
+ const key = rule.name;
339
+ groups[key] ? rules.splice(i--, 1) :
340
+ groupsArr.push(groups[key] = []);
341
+ groups[key].push(rule);
342
+ }
343
+ }
344
+
345
+ groupsArr.forEach(group => {
346
+ if (group.length > 0) {
347
+ const result = group[0];
348
+ let space = [];
349
+ const comma = [new tree.Expression(space)];
350
+ group.forEach(rule => {
351
+ if ((rule.merge === '+') && (space.length > 0)) {
352
+ comma.push(new tree.Expression(space = []));
353
+ }
354
+ space.push(rule.value);
355
+ result.important = result.important || rule.important;
356
+ });
357
+ result.value = new tree.Value(comma);
358
+ }
359
+ });
360
+ }
361
+ };
362
+
363
+ export default ToCSSVisitor;