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,143 @@
1
+ import Comment from '../tree/comment';
2
+ import Dimension from '../tree/dimension';
3
+ import Declaration from '../tree/declaration';
4
+ import Expression from '../tree/expression';
5
+ import Ruleset from '../tree/ruleset';
6
+ import Selector from '../tree/selector';
7
+ import Element from '../tree/element';
8
+ import Quote from '../tree/quoted';
9
+
10
+ const getItemsFromNode = node => {
11
+ // handle non-array values as an array of length 1
12
+ // return 'undefined' if index is invalid
13
+ const items = Array.isArray(node.value) ?
14
+ node.value : Array(node);
15
+
16
+ return items;
17
+ };
18
+
19
+ export default {
20
+ _SELF: function(n) {
21
+ return n;
22
+ },
23
+ extract: function(values, index) {
24
+ // (1-based index)
25
+ index = index.value - 1;
26
+
27
+ return getItemsFromNode(values)[index];
28
+ },
29
+ length: function(values) {
30
+ return new Dimension(getItemsFromNode(values).length);
31
+ },
32
+ /**
33
+ * Creates a Less list of incremental values.
34
+ * Modeled after Lodash's range function, also exists natively in PHP
35
+ *
36
+ * @param {Dimension} [start=1]
37
+ * @param {Dimension} end - e.g. 10 or 10px - unit is added to output
38
+ * @param {Dimension} [step=1]
39
+ */
40
+ range: function(start, end, step) {
41
+ let from;
42
+ let to;
43
+ let stepValue = 1;
44
+ const list = [];
45
+ if (end) {
46
+ to = end;
47
+ from = start.value;
48
+ if (step) {
49
+ stepValue = step.value;
50
+ }
51
+ }
52
+ else {
53
+ from = 1;
54
+ to = start;
55
+ }
56
+
57
+ for (let i = from; i <= to.value; i += stepValue) {
58
+ list.push(new Dimension(i, to.unit));
59
+ }
60
+
61
+ return new Expression(list);
62
+ },
63
+ each: function(list, rs) {
64
+ const rules = [];
65
+ let newRules;
66
+ let iterator;
67
+
68
+ if (list.value && !(list instanceof Quote)) {
69
+ if (Array.isArray(list.value)) {
70
+ iterator = list.value;
71
+ } else {
72
+ iterator = [list.value];
73
+ }
74
+ } else if (list.ruleset) {
75
+ iterator = list.ruleset.rules;
76
+ } else if (list.rules) {
77
+ iterator = list.rules;
78
+ } else if (Array.isArray(list)) {
79
+ iterator = list;
80
+ } else {
81
+ iterator = [list];
82
+ }
83
+
84
+ let valueName = '@value';
85
+ let keyName = '@key';
86
+ let indexName = '@index';
87
+
88
+ if (rs.params) {
89
+ valueName = rs.params[0] && rs.params[0].name;
90
+ keyName = rs.params[1] && rs.params[1].name;
91
+ indexName = rs.params[2] && rs.params[2].name;
92
+ rs = rs.rules;
93
+ } else {
94
+ rs = rs.ruleset;
95
+ }
96
+
97
+ for (let i = 0; i < iterator.length; i++) {
98
+ let key;
99
+ let value;
100
+ const item = iterator[i];
101
+ if (item instanceof Declaration) {
102
+ key = typeof item.name === 'string' ? item.name : item.name[0].value;
103
+ value = item.value;
104
+ } else {
105
+ key = new Dimension(i + 1);
106
+ value = item;
107
+ }
108
+
109
+ if (item instanceof Comment) {
110
+ continue;
111
+ }
112
+
113
+ newRules = rs.rules.slice(0);
114
+ if (valueName) {
115
+ newRules.push(new Declaration(valueName,
116
+ value,
117
+ false, false, this.index, this.currentFileInfo));
118
+ }
119
+ if (indexName) {
120
+ newRules.push(new Declaration(indexName,
121
+ new Dimension(i + 1),
122
+ false, false, this.index, this.currentFileInfo));
123
+ }
124
+ if (keyName) {
125
+ newRules.push(new Declaration(keyName,
126
+ key,
127
+ false, false, this.index, this.currentFileInfo));
128
+ }
129
+
130
+ rules.push(new Ruleset([ new(Selector)([ new Element("", '&') ]) ],
131
+ newRules,
132
+ rs.strictImports,
133
+ rs.visibilityInfo()
134
+ ));
135
+ }
136
+
137
+ return new Ruleset([ new(Selector)([ new Element("", '&') ]) ],
138
+ rules,
139
+ rs.strictImports,
140
+ rs.visibilityInfo()
141
+ ).eval(this.context);
142
+ }
143
+ };
@@ -0,0 +1,15 @@
1
+ import Dimension from '../tree/dimension';
2
+
3
+ const MathHelper = (fn, unit, n) => {
4
+ if (!(n instanceof Dimension)) {
5
+ throw { type: 'Argument', message: 'argument must be a number' };
6
+ }
7
+ if (unit == null) {
8
+ unit = n.unit;
9
+ } else {
10
+ n = n.unify();
11
+ }
12
+ return new Dimension(fn(parseFloat(n.value)), unit);
13
+ };
14
+
15
+ export default MathHelper;
@@ -0,0 +1,28 @@
1
+ import mathHelper from './math-helper.js';
2
+
3
+ const mathFunctions = {
4
+ // name, unit
5
+ ceil: null,
6
+ floor: null,
7
+ sqrt: null,
8
+ abs: null,
9
+ tan: '',
10
+ sin: '',
11
+ cos: '',
12
+ atan: 'rad',
13
+ asin: 'rad',
14
+ acos: 'rad'
15
+ };
16
+
17
+ for (const f in mathFunctions) {
18
+ if (mathFunctions.hasOwnProperty(f)) {
19
+ mathFunctions[f] = mathHelper.bind(null, Math[f], mathFunctions[f]);
20
+ }
21
+ }
22
+
23
+ mathFunctions.round = (n, f) => {
24
+ const fraction = typeof f === 'undefined' ? 0 : f.value;
25
+ return mathHelper(num => num.toFixed(fraction), null, n);
26
+ };
27
+
28
+ export default mathFunctions;
@@ -0,0 +1,89 @@
1
+ import Dimension from '../tree/dimension';
2
+ import Anonymous from '../tree/anonymous';
3
+ import mathHelper from './math-helper.js';
4
+
5
+ const minMax = function (isMin, args) {
6
+ args = Array.prototype.slice.call(args);
7
+ switch (args.length) {
8
+ case 0: throw { type: 'Argument', message: 'one or more arguments required' };
9
+ }
10
+ let i; // key is the unit.toString() for unified Dimension values,
11
+ let j;
12
+ let current;
13
+ let currentUnified;
14
+ let referenceUnified;
15
+ let unit;
16
+ let unitStatic;
17
+ let unitClone;
18
+
19
+ const // elems only contains original argument values.
20
+ order = [];
21
+
22
+ const values = {};
23
+ // value is the index into the order array.
24
+ for (i = 0; i < args.length; i++) {
25
+ current = args[i];
26
+ if (!(current instanceof Dimension)) {
27
+ if (Array.isArray(args[i].value)) {
28
+ Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value));
29
+ }
30
+ continue;
31
+ }
32
+ currentUnified = current.unit.toString() === '' && unitClone !== undefined ? new Dimension(current.value, unitClone).unify() : current.unify();
33
+ unit = currentUnified.unit.toString() === '' && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString();
34
+ unitStatic = unit !== '' && unitStatic === undefined || unit !== '' && order[0].unify().unit.toString() === '' ? unit : unitStatic;
35
+ unitClone = unit !== '' && unitClone === undefined ? current.unit.toString() : unitClone;
36
+ j = values[''] !== undefined && unit !== '' && unit === unitStatic ? values[''] : values[unit];
37
+ if (j === undefined) {
38
+ if (unitStatic !== undefined && unit !== unitStatic) {
39
+ throw { type: 'Argument', message: 'incompatible types' };
40
+ }
41
+ values[unit] = order.length;
42
+ order.push(current);
43
+ continue;
44
+ }
45
+ referenceUnified = order[j].unit.toString() === '' && unitClone !== undefined ? new Dimension(order[j].value, unitClone).unify() : order[j].unify();
46
+ if ( isMin && currentUnified.value < referenceUnified.value ||
47
+ !isMin && currentUnified.value > referenceUnified.value) {
48
+ order[j] = current;
49
+ }
50
+ }
51
+ if (order.length == 1) {
52
+ return order[0];
53
+ }
54
+ args = order.map(function (a) { return a.toCSS(this.context); }).join(this.context.compress ? ',' : ', ');
55
+ return new Anonymous(`${isMin ? 'min' : 'max'}(${args})`);
56
+ };
57
+
58
+ export default {
59
+ min: function(...args) {
60
+ return minMax(true, args);
61
+ },
62
+ max: function(...args) {
63
+ return minMax(false, args);
64
+ },
65
+ convert: function (val, unit) {
66
+ return val.convertTo(unit.value);
67
+ },
68
+ pi: function () {
69
+ return new Dimension(Math.PI);
70
+ },
71
+ mod: function(a, b) {
72
+ return new Dimension(a.value % b.value, a.unit);
73
+ },
74
+ pow: function(x, y) {
75
+ if (typeof x === 'number' && typeof y === 'number') {
76
+ x = new Dimension(x);
77
+ y = new Dimension(y);
78
+ } else if (!(x instanceof Dimension) || !(y instanceof Dimension)) {
79
+ throw { type: 'Argument', message: 'arguments must be numbers' };
80
+ }
81
+
82
+ return new Dimension(Math.pow(x.value, y.value), x.unit);
83
+ },
84
+ percentage: function (n) {
85
+ const result = mathHelper(num => num * 100, '%', n);
86
+
87
+ return result;
88
+ }
89
+ };
@@ -0,0 +1,36 @@
1
+ import Quoted from '../tree/quoted';
2
+ import Anonymous from '../tree/anonymous';
3
+ import JavaScript from '../tree/javascript';
4
+
5
+ export default {
6
+ e: function (str) {
7
+ return new Quoted('"', str instanceof JavaScript ? str.evaluated : str.value, true);
8
+ },
9
+ escape: function (str) {
10
+ return new Anonymous(
11
+ encodeURI(str.value).replace(/=/g, '%3D').replace(/:/g, '%3A').replace(/#/g, '%23').replace(/;/g, '%3B')
12
+ .replace(/\(/g, '%28').replace(/\)/g, '%29'));
13
+ },
14
+ replace: function (string, pattern, replacement, flags) {
15
+ let result = string.value;
16
+ replacement = (replacement.type === 'Quoted') ?
17
+ replacement.value : replacement.toCSS();
18
+ result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement);
19
+ return new Quoted(string.quote || '', result, string.escaped);
20
+ },
21
+ '%': function (string /* arg, arg, ... */) {
22
+ const args = Array.prototype.slice.call(arguments, 1);
23
+ let result = string.value;
24
+
25
+ for (let i = 0; i < args.length; i++) {
26
+ /* jshint loopfunc:true */
27
+ result = result.replace(/%[sda]/i, token => {
28
+ const value = ((args[i].type === 'Quoted') &&
29
+ token.match(/s/i)) ? args[i].value : args[i].toCSS();
30
+ return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value;
31
+ });
32
+ }
33
+ result = result.replace(/%%/g, '%');
34
+ return new Quoted(string.quote || '', result, string.escaped);
35
+ }
36
+ };
@@ -0,0 +1,87 @@
1
+ import Dimension from '../tree/dimension';
2
+ import Color from '../tree/color';
3
+ import Expression from '../tree/expression';
4
+ import Quoted from '../tree/quoted';
5
+ import URL from '../tree/url';
6
+
7
+ export default environment => {
8
+ return { 'svg-gradient': function(direction) {
9
+ let stops;
10
+ let gradientDirectionSvg;
11
+ let gradientType = 'linear';
12
+ let rectangleDimension = 'x="0" y="0" width="1" height="1"';
13
+ const renderEnv = {compress: false};
14
+ let returner;
15
+ const directionValue = direction.toCSS(renderEnv);
16
+ let i;
17
+ let color;
18
+ let position;
19
+ let positionValue;
20
+ let alpha;
21
+
22
+ function throwArgumentDescriptor() {
23
+ throw { type: 'Argument',
24
+ message: 'svg-gradient expects direction, start_color [start_position], [color position,]...,' +
25
+ ' end_color [end_position] or direction, color list' };
26
+ }
27
+
28
+ if (arguments.length == 2) {
29
+ if (arguments[1].value.length < 2) {
30
+ throwArgumentDescriptor();
31
+ }
32
+ stops = arguments[1].value;
33
+ } else if (arguments.length < 3) {
34
+ throwArgumentDescriptor();
35
+ } else {
36
+ stops = Array.prototype.slice.call(arguments, 1);
37
+ }
38
+
39
+ switch (directionValue) {
40
+ case 'to bottom':
41
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
42
+ break;
43
+ case 'to right':
44
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
45
+ break;
46
+ case 'to bottom right':
47
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
48
+ break;
49
+ case 'to top right':
50
+ gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
51
+ break;
52
+ case 'ellipse':
53
+ case 'ellipse at center':
54
+ gradientType = 'radial';
55
+ gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
56
+ rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
57
+ break;
58
+ default:
59
+ throw { type: 'Argument', message: 'svg-gradient direction must be \'to bottom\', \'to right\',' +
60
+ ' \'to bottom right\', \'to top right\' or \'ellipse at center\'' };
61
+ }
62
+ returner = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1"><${gradientType}Gradient id="g" ${gradientDirectionSvg}>`;
63
+
64
+ for (i = 0; i < stops.length; i += 1) {
65
+ if (stops[i] instanceof Expression) {
66
+ color = stops[i].value[0];
67
+ position = stops[i].value[1];
68
+ } else {
69
+ color = stops[i];
70
+ position = undefined;
71
+ }
72
+
73
+ if (!(color instanceof Color) || (!((i === 0 || i + 1 === stops.length) && position === undefined) && !(position instanceof Dimension))) {
74
+ throwArgumentDescriptor();
75
+ }
76
+ positionValue = position ? position.toCSS(renderEnv) : i === 0 ? '0%' : '100%';
77
+ alpha = color.alpha;
78
+ returner += `<stop offset="${positionValue}" stop-color="${color.toRGB()}"${alpha < 1 ? ` stop-opacity="${alpha}"` : ''}/>`;
79
+ }
80
+ returner += `</${gradientType}Gradient><rect ${rectangleDimension} fill="url(#g)" /></svg>`;
81
+
82
+ returner = encodeURIComponent(returner);
83
+
84
+ returner = `data:image/svg+xml,${returner}`;
85
+ return new URL(new Quoted(`'${returner}'`, returner, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo);
86
+ }};
87
+ };
@@ -0,0 +1,70 @@
1
+ import Keyword from '../tree/keyword';
2
+ import DetachedRuleset from '../tree/detached-ruleset';
3
+ import Dimension from '../tree/dimension';
4
+ import Color from '../tree/color';
5
+ import Quoted from '../tree/quoted';
6
+ import Anonymous from '../tree/anonymous';
7
+ import URL from '../tree/url';
8
+ import Operation from '../tree/operation';
9
+
10
+ const isa = (n, Type) => (n instanceof Type) ? Keyword.True : Keyword.False;
11
+ const isunit = (n, unit) => {
12
+ if (unit === undefined) {
13
+ throw { type: 'Argument', message: 'missing the required second argument to isunit.' };
14
+ }
15
+ unit = typeof unit.value === 'string' ? unit.value : unit;
16
+ if (typeof unit !== 'string') {
17
+ throw { type: 'Argument', message: 'Second argument to isunit should be a unit or a string.' };
18
+ }
19
+ return (n instanceof Dimension) && n.unit.is(unit) ? Keyword.True : Keyword.False;
20
+ };
21
+
22
+ export default {
23
+ isruleset: function (n) {
24
+ return isa(n, DetachedRuleset);
25
+ },
26
+ iscolor: function (n) {
27
+ return isa(n, Color);
28
+ },
29
+ isnumber: function (n) {
30
+ return isa(n, Dimension);
31
+ },
32
+ isstring: function (n) {
33
+ return isa(n, Quoted);
34
+ },
35
+ iskeyword: function (n) {
36
+ return isa(n, Keyword);
37
+ },
38
+ isurl: function (n) {
39
+ return isa(n, URL);
40
+ },
41
+ ispixel: function (n) {
42
+ return isunit(n, 'px');
43
+ },
44
+ ispercentage: function (n) {
45
+ return isunit(n, '%');
46
+ },
47
+ isem: function (n) {
48
+ return isunit(n, 'em');
49
+ },
50
+ isunit,
51
+ unit: function (val, unit) {
52
+ if (!(val instanceof Dimension)) {
53
+ throw { type: 'Argument',
54
+ message: `the first argument to unit must be a number${val instanceof Operation ? '. Have you forgotten parenthesis?' : ''}` };
55
+ }
56
+ if (unit) {
57
+ if (unit instanceof Keyword) {
58
+ unit = unit.value;
59
+ } else {
60
+ unit = unit.toCSS();
61
+ }
62
+ } else {
63
+ unit = '';
64
+ }
65
+ return new Dimension(val.value, unit);
66
+ },
67
+ 'get-unit': function (n) {
68
+ return new Anonymous(n.unit);
69
+ }
70
+ };
@@ -0,0 +1,169 @@
1
+ import contexts from './contexts';
2
+ import Parser from './parser/parser';
3
+ import LessError from './less-error';
4
+ import * as utils from './utils';
5
+ import logger from './logger';
6
+
7
+ export default environment => {
8
+ // FileInfo = {
9
+ // 'rewriteUrls' - option - whether to adjust URL's to be relative
10
+ // 'filename' - full resolved filename of current file
11
+ // 'rootpath' - path to append to normal URLs for this node
12
+ // 'currentDirectory' - path to the current file, absolute
13
+ // 'rootFilename' - filename of the base file
14
+ // 'entryPath' - absolute path to the entry file
15
+ // 'reference' - whether the file should not be output and only output parts that are referenced
16
+
17
+ class ImportManager {
18
+ constructor(less, context, rootFileInfo) {
19
+ this.less = less;
20
+ this.rootFilename = rootFileInfo.filename;
21
+ this.paths = context.paths || []; // Search paths, when importing
22
+ this.contents = {}; // map - filename to contents of all the files
23
+ this.contentsIgnoredChars = {}; // map - filename to lines at the beginning of each file to ignore
24
+ this.mime = context.mime;
25
+ this.error = null;
26
+ this.context = context;
27
+ // Deprecated? Unused outside of here, could be useful.
28
+ this.queue = []; // Files which haven't been imported yet
29
+ this.files = {}; // Holds the imported parse trees.
30
+ }
31
+
32
+ /**
33
+ * Add an import to be imported
34
+ * @param path - the raw path
35
+ * @param tryAppendExtension - whether to try appending a file extension (.less or .js if the path has no extension)
36
+ * @param currentFileInfo - the current file info (used for instance to work out relative paths)
37
+ * @param importOptions - import options
38
+ * @param callback - callback for when it is imported
39
+ */
40
+ push(path, tryAppendExtension, currentFileInfo, importOptions, callback) {
41
+ const importManager = this;
42
+ const pluginLoader = this.context.pluginManager.Loader;
43
+
44
+ this.queue.push(path);
45
+
46
+ const fileParsedFunc = (e, root, fullPath) => {
47
+ importManager.queue.splice(importManager.queue.indexOf(path), 1); // Remove the path from the queue
48
+
49
+ const importedEqualsRoot = fullPath === importManager.rootFilename;
50
+ if (importOptions.optional && e) {
51
+ callback(null, {rules:[]}, false, null);
52
+ logger.info(`The file ${fullPath} was skipped because it was not found and the import was marked optional.`);
53
+ }
54
+ else {
55
+ // Inline imports aren't cached here.
56
+ // If we start to cache them, please make sure they won't conflict with non-inline imports of the
57
+ // same name as they used to do before this comment and the condition below have been added.
58
+ if (!importManager.files[fullPath] && !importOptions.inline) {
59
+ importManager.files[fullPath] = { root, options: importOptions };
60
+ }
61
+ if (e && !importManager.error) { importManager.error = e; }
62
+ callback(e, root, importedEqualsRoot, fullPath);
63
+ }
64
+ };
65
+
66
+ const newFileInfo = {
67
+ rewriteUrls: this.context.rewriteUrls,
68
+ entryPath: currentFileInfo.entryPath,
69
+ rootpath: currentFileInfo.rootpath,
70
+ rootFilename: currentFileInfo.rootFilename
71
+ };
72
+
73
+ const fileManager = environment.getFileManager(path, currentFileInfo.currentDirectory, this.context, environment);
74
+
75
+ if (!fileManager) {
76
+ fileParsedFunc({ message: `Could not find a file-manager for ${path}` });
77
+ return;
78
+ }
79
+
80
+ const loadFileCallback = loadedFile => {
81
+ let plugin;
82
+ const resolvedFilename = loadedFile.filename;
83
+ const contents = loadedFile.contents.replace(/^\uFEFF/, '');
84
+
85
+ // Pass on an updated rootpath if path of imported file is relative and file
86
+ // is in a (sub|sup) directory
87
+ //
88
+ // Examples:
89
+ // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/',
90
+ // then rootpath should become 'less/module/nav/'
91
+ // - If path of imported file is '../mixins.less' and rootpath is 'less/',
92
+ // then rootpath should become 'less/../'
93
+ newFileInfo.currentDirectory = fileManager.getPath(resolvedFilename);
94
+ if (newFileInfo.rewriteUrls) {
95
+ newFileInfo.rootpath = fileManager.join(
96
+ (importManager.context.rootpath || ''),
97
+ fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath));
98
+
99
+ if (!fileManager.isPathAbsolute(newFileInfo.rootpath) && fileManager.alwaysMakePathsAbsolute()) {
100
+ newFileInfo.rootpath = fileManager.join(newFileInfo.entryPath, newFileInfo.rootpath);
101
+ }
102
+ }
103
+ newFileInfo.filename = resolvedFilename;
104
+
105
+ const newEnv = new contexts.Parse(importManager.context);
106
+
107
+ newEnv.processImports = false;
108
+ importManager.contents[resolvedFilename] = contents;
109
+
110
+ if (currentFileInfo.reference || importOptions.reference) {
111
+ newFileInfo.reference = true;
112
+ }
113
+
114
+ if (importOptions.isPlugin) {
115
+ plugin = pluginLoader.evalPlugin(contents, newEnv, importManager, importOptions.pluginArgs, newFileInfo);
116
+ if (plugin instanceof LessError) {
117
+ fileParsedFunc(plugin, null, resolvedFilename);
118
+ }
119
+ else {
120
+ fileParsedFunc(null, plugin, resolvedFilename);
121
+ }
122
+ } else if (importOptions.inline) {
123
+ fileParsedFunc(null, contents, resolvedFilename);
124
+ } else {
125
+
126
+ // import (multiple) parse trees apparently get altered and can't be cached.
127
+ // TODO: investigate why this is
128
+ if (importManager.files[resolvedFilename]
129
+ && !importManager.files[resolvedFilename].options.multiple
130
+ && !importOptions.multiple) {
131
+
132
+ fileParsedFunc(null, importManager.files[resolvedFilename].root, resolvedFilename);
133
+ }
134
+ else {
135
+ new Parser(newEnv, importManager, newFileInfo).parse(contents, (e, root) => {
136
+ fileParsedFunc(e, root, resolvedFilename);
137
+ });
138
+ }
139
+ }
140
+ };
141
+ let promise;
142
+ const context = utils.clone(this.context);
143
+
144
+ if (tryAppendExtension) {
145
+ context.ext = importOptions.isPlugin ? '.js' : '.less';
146
+ }
147
+
148
+ if (importOptions.isPlugin) {
149
+ context.mime = 'application/javascript';
150
+ promise = pluginLoader.loadPlugin(path, currentFileInfo.currentDirectory, context, environment, fileManager);
151
+ }
152
+ else {
153
+ promise = fileManager.loadFile(path, currentFileInfo.currentDirectory, context, environment,
154
+ (err, loadedFile) => {
155
+ if (err) {
156
+ fileParsedFunc(err);
157
+ } else {
158
+ loadFileCallback(loadedFile);
159
+ }
160
+ });
161
+ }
162
+ if (promise) {
163
+ promise.then(loadFileCallback, fileParsedFunc);
164
+ }
165
+ }
166
+ }
167
+
168
+ return ImportManager;
169
+ };