less 4.5.1 → 4.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/README.md +70 -5
  2. package/bin/lessc +37 -23
  3. package/dist/less.js +14501 -11745
  4. package/dist/less.min.js +4 -4
  5. package/dist/less.min.js.map +1 -1
  6. package/index.cjs +41 -0
  7. package/lib/less/constants.js +5 -7
  8. package/lib/less/contexts.js +71 -55
  9. package/lib/less/data/colors.js +150 -153
  10. package/lib/less/data/index.js +4 -7
  11. package/lib/less/data/unit-conversions.js +2 -5
  12. package/lib/less/default-options.js +36 -26
  13. package/lib/less/deprecation.js +67 -0
  14. package/lib/less/environment/abstract-file-manager.js +62 -50
  15. package/lib/less/environment/abstract-plugin-loader.js +69 -46
  16. package/lib/less/environment/environment-api.ts +21 -0
  17. package/lib/less/environment/environment.js +34 -30
  18. package/lib/less/environment/file-manager-api.ts +77 -0
  19. package/lib/less/functions/boolean.js +13 -13
  20. package/lib/less/functions/color-blending.js +39 -31
  21. package/lib/less/functions/color.js +153 -137
  22. package/lib/less/functions/data-uri.js +73 -64
  23. package/lib/less/functions/default.js +9 -11
  24. package/lib/less/functions/function-caller.js +35 -34
  25. package/lib/less/functions/function-registry.js +16 -17
  26. package/lib/less/functions/index.js +33 -33
  27. package/lib/less/functions/list.js +88 -75
  28. package/lib/less/functions/math-helper.js +8 -11
  29. package/lib/less/functions/math.js +21 -21
  30. package/lib/less/functions/number.js +42 -53
  31. package/lib/less/functions/string.js +18 -22
  32. package/lib/less/functions/style.js +21 -20
  33. package/lib/less/functions/svg.js +83 -82
  34. package/lib/less/functions/types.js +28 -32
  35. package/lib/less/import-manager.js +73 -64
  36. package/lib/less/index.js +65 -65
  37. package/lib/less/less-error.js +63 -45
  38. package/lib/less/logger.js +11 -14
  39. package/lib/less/parse-tree.js +52 -53
  40. package/lib/less/parse.js +44 -44
  41. package/lib/less/parser/parser-input.js +151 -113
  42. package/lib/less/parser/parser.js +864 -658
  43. package/lib/less/plugin-manager.js +57 -46
  44. package/lib/less/render.js +17 -23
  45. package/lib/less/source-map-builder.js +52 -43
  46. package/lib/less/source-map-output.js +56 -43
  47. package/lib/less/transform-tree.js +40 -30
  48. package/lib/less/tree/anonymous.js +52 -28
  49. package/lib/less/tree/assignment.js +44 -27
  50. package/lib/less/tree/atrule-syntax.js +4 -6
  51. package/lib/less/tree/atrule.js +247 -140
  52. package/lib/less/tree/attribute.js +55 -24
  53. package/lib/less/tree/call.js +70 -42
  54. package/lib/less/tree/color.js +194 -145
  55. package/lib/less/tree/combinator.js +31 -21
  56. package/lib/less/tree/comment.js +44 -23
  57. package/lib/less/tree/condition.js +61 -36
  58. package/lib/less/tree/container.js +90 -34
  59. package/lib/less/tree/debug-info.js +28 -15
  60. package/lib/less/tree/declaration.js +97 -52
  61. package/lib/less/tree/detached-ruleset.js +42 -24
  62. package/lib/less/tree/dimension.js +127 -71
  63. package/lib/less/tree/element.js +82 -52
  64. package/lib/less/tree/expression.js +75 -50
  65. package/lib/less/tree/extend.js +71 -42
  66. package/lib/less/tree/import.js +184 -110
  67. package/lib/less/tree/index.js +52 -81
  68. package/lib/less/tree/javascript.js +43 -32
  69. package/lib/less/tree/js-eval-node.js +46 -32
  70. package/lib/less/tree/keyword.js +25 -17
  71. package/lib/less/tree/media.js +77 -35
  72. package/lib/less/tree/merge-rules.js +47 -0
  73. package/lib/less/tree/mixin-call.js +181 -98
  74. package/lib/less/tree/mixin-definition.js +211 -117
  75. package/lib/less/tree/namespace-value.js +63 -44
  76. package/lib/less/tree/negative.js +34 -21
  77. package/lib/less/tree/nested-at-rule.js +149 -66
  78. package/lib/less/tree/node.js +225 -90
  79. package/lib/less/tree/operation.js +59 -34
  80. package/lib/less/tree/paren.js +34 -17
  81. package/lib/less/tree/property.js +66 -35
  82. package/lib/less/tree/query-in-parens.js +52 -60
  83. package/lib/less/tree/quoted.js +93 -44
  84. package/lib/less/tree/ruleset.js +632 -318
  85. package/lib/less/tree/selector.js +151 -77
  86. package/lib/less/tree/unicode-descriptor.js +14 -12
  87. package/lib/less/tree/unit.js +111 -63
  88. package/lib/less/tree/url.js +62 -38
  89. package/lib/less/tree/value.js +50 -34
  90. package/lib/less/tree/variable-call.js +47 -30
  91. package/lib/less/tree/variable.js +53 -31
  92. package/lib/less/utils.js +44 -47
  93. package/lib/less/visitors/extend-visitor.js +233 -172
  94. package/lib/less/visitors/import-sequencer.js +24 -24
  95. package/lib/less/visitors/import-visitor.js +62 -52
  96. package/lib/less/visitors/index.js +14 -17
  97. package/lib/less/visitors/join-selector-visitor.js +40 -37
  98. package/lib/less/visitors/set-tree-visibility-visitor.js +32 -18
  99. package/lib/less/visitors/to-css-visitor.js +135 -128
  100. package/lib/less/visitors/visitor.js +60 -45
  101. package/lib/less-browser/add-default-options.js +23 -13
  102. package/lib/less-browser/bootstrap.js +23 -17
  103. package/lib/less-browser/browser.js +24 -21
  104. package/lib/less-browser/cache.js +17 -19
  105. package/lib/less-browser/error-reporting.js +52 -44
  106. package/lib/less-browser/file-manager.js +52 -42
  107. package/lib/less-browser/image-size.js +12 -12
  108. package/lib/less-browser/index.js +131 -105
  109. package/lib/less-browser/log-listener.js +30 -31
  110. package/lib/less-browser/plugin-loader.js +10 -10
  111. package/lib/less-browser/utils.js +19 -21
  112. package/lib/less-node/environment.js +32 -8
  113. package/lib/less-node/file-manager.js +62 -45
  114. package/lib/less-node/fs.js +11 -8
  115. package/lib/less-node/image-size.js +40 -31
  116. package/lib/less-node/index.js +28 -19
  117. package/lib/less-node/lessc-helper.js +20 -21
  118. package/lib/less-node/plugin-loader.js +35 -27
  119. package/lib/less-node/url-file-manager.js +33 -31
  120. package/package.json +29 -18
  121. package/.eslintignore +0 -6
  122. package/.eslintrc.js +0 -63
  123. package/Gruntfile.js +0 -403
  124. package/bower.json +0 -22
  125. package/lib/less/constants.js.map +0 -1
  126. package/lib/less/contexts.js.map +0 -1
  127. package/lib/less/data/colors.js.map +0 -1
  128. package/lib/less/data/index.js.map +0 -1
  129. package/lib/less/data/unit-conversions.js.map +0 -1
  130. package/lib/less/default-options.js.map +0 -1
  131. package/lib/less/environment/abstract-file-manager.js.map +0 -1
  132. package/lib/less/environment/abstract-plugin-loader.js.map +0 -1
  133. package/lib/less/environment/environment-api.js +0 -3
  134. package/lib/less/environment/environment-api.js.map +0 -1
  135. package/lib/less/environment/environment.js.map +0 -1
  136. package/lib/less/environment/file-manager-api.js +0 -3
  137. package/lib/less/environment/file-manager-api.js.map +0 -1
  138. package/lib/less/functions/boolean.js.map +0 -1
  139. package/lib/less/functions/color-blending.js.map +0 -1
  140. package/lib/less/functions/color.js.map +0 -1
  141. package/lib/less/functions/data-uri.js.map +0 -1
  142. package/lib/less/functions/default.js.map +0 -1
  143. package/lib/less/functions/function-caller.js.map +0 -1
  144. package/lib/less/functions/function-registry.js.map +0 -1
  145. package/lib/less/functions/index.js.map +0 -1
  146. package/lib/less/functions/list.js.map +0 -1
  147. package/lib/less/functions/math-helper.js.map +0 -1
  148. package/lib/less/functions/math.js.map +0 -1
  149. package/lib/less/functions/number.js.map +0 -1
  150. package/lib/less/functions/string.js.map +0 -1
  151. package/lib/less/functions/style.js.map +0 -1
  152. package/lib/less/functions/svg.js.map +0 -1
  153. package/lib/less/functions/types.js.map +0 -1
  154. package/lib/less/import-manager.js.map +0 -1
  155. package/lib/less/index.js.map +0 -1
  156. package/lib/less/less-error.js.map +0 -1
  157. package/lib/less/logger.js.map +0 -1
  158. package/lib/less/parse-tree.js.map +0 -1
  159. package/lib/less/parse.js.map +0 -1
  160. package/lib/less/parser/parser-input.js.map +0 -1
  161. package/lib/less/parser/parser.js.map +0 -1
  162. package/lib/less/plugin-manager.js.map +0 -1
  163. package/lib/less/render.js.map +0 -1
  164. package/lib/less/source-map-builder.js.map +0 -1
  165. package/lib/less/source-map-output.js.map +0 -1
  166. package/lib/less/transform-tree.js.map +0 -1
  167. package/lib/less/tree/anonymous.js.map +0 -1
  168. package/lib/less/tree/assignment.js.map +0 -1
  169. package/lib/less/tree/atrule-syntax.js.map +0 -1
  170. package/lib/less/tree/atrule.js.map +0 -1
  171. package/lib/less/tree/attribute.js.map +0 -1
  172. package/lib/less/tree/call.js.map +0 -1
  173. package/lib/less/tree/color.js.map +0 -1
  174. package/lib/less/tree/combinator.js.map +0 -1
  175. package/lib/less/tree/comment.js.map +0 -1
  176. package/lib/less/tree/condition.js.map +0 -1
  177. package/lib/less/tree/container.js.map +0 -1
  178. package/lib/less/tree/debug-info.js.map +0 -1
  179. package/lib/less/tree/declaration.js.map +0 -1
  180. package/lib/less/tree/detached-ruleset.js.map +0 -1
  181. package/lib/less/tree/dimension.js.map +0 -1
  182. package/lib/less/tree/element.js.map +0 -1
  183. package/lib/less/tree/expression.js.map +0 -1
  184. package/lib/less/tree/extend.js.map +0 -1
  185. package/lib/less/tree/import.js.map +0 -1
  186. package/lib/less/tree/index.js.map +0 -1
  187. package/lib/less/tree/javascript.js.map +0 -1
  188. package/lib/less/tree/js-eval-node.js.map +0 -1
  189. package/lib/less/tree/keyword.js.map +0 -1
  190. package/lib/less/tree/media.js.map +0 -1
  191. package/lib/less/tree/mixin-call.js.map +0 -1
  192. package/lib/less/tree/mixin-definition.js.map +0 -1
  193. package/lib/less/tree/namespace-value.js.map +0 -1
  194. package/lib/less/tree/negative.js.map +0 -1
  195. package/lib/less/tree/nested-at-rule.js.map +0 -1
  196. package/lib/less/tree/node.js.map +0 -1
  197. package/lib/less/tree/operation.js.map +0 -1
  198. package/lib/less/tree/paren.js.map +0 -1
  199. package/lib/less/tree/property.js.map +0 -1
  200. package/lib/less/tree/query-in-parens.js.map +0 -1
  201. package/lib/less/tree/quoted.js.map +0 -1
  202. package/lib/less/tree/ruleset.js.map +0 -1
  203. package/lib/less/tree/selector.js.map +0 -1
  204. package/lib/less/tree/unicode-descriptor.js.map +0 -1
  205. package/lib/less/tree/unit.js.map +0 -1
  206. package/lib/less/tree/url.js.map +0 -1
  207. package/lib/less/tree/value.js.map +0 -1
  208. package/lib/less/tree/variable-call.js.map +0 -1
  209. package/lib/less/tree/variable.js.map +0 -1
  210. package/lib/less/utils.js.map +0 -1
  211. package/lib/less/visitors/extend-visitor.js.map +0 -1
  212. package/lib/less/visitors/import-sequencer.js.map +0 -1
  213. package/lib/less/visitors/import-visitor.js.map +0 -1
  214. package/lib/less/visitors/index.js.map +0 -1
  215. package/lib/less/visitors/join-selector-visitor.js.map +0 -1
  216. package/lib/less/visitors/set-tree-visibility-visitor.js.map +0 -1
  217. package/lib/less/visitors/to-css-visitor.js.map +0 -1
  218. package/lib/less/visitors/visitor.js.map +0 -1
  219. package/lib/less-browser/add-default-options.js.map +0 -1
  220. package/lib/less-browser/bootstrap.js.map +0 -1
  221. package/lib/less-browser/browser.js.map +0 -1
  222. package/lib/less-browser/cache.js.map +0 -1
  223. package/lib/less-browser/error-reporting.js.map +0 -1
  224. package/lib/less-browser/file-manager.js.map +0 -1
  225. package/lib/less-browser/image-size.js.map +0 -1
  226. package/lib/less-browser/index.js.map +0 -1
  227. package/lib/less-browser/log-listener.js.map +0 -1
  228. package/lib/less-browser/plugin-loader.js.map +0 -1
  229. package/lib/less-browser/utils.js.map +0 -1
  230. package/lib/less-node/environment.js.map +0 -1
  231. package/lib/less-node/file-manager.js.map +0 -1
  232. package/lib/less-node/fs.js.map +0 -1
  233. package/lib/less-node/image-size.js.map +0 -1
  234. package/lib/less-node/index.js.map +0 -1
  235. package/lib/less-node/lessc-helper.js.map +0 -1
  236. package/lib/less-node/plugin-loader.js.map +0 -1
  237. package/lib/less-node/url-file-manager.js.map +0 -1
  238. package/scripts/coverage-lines.js +0 -207
  239. package/scripts/coverage-report.js +0 -158
  240. package/scripts/postinstall.js +0 -61
  241. package/test/.eslintrc.json +0 -9
  242. package/test/README.md +0 -3
  243. package/test/browser/common.js +0 -251
  244. package/test/browser/css/global-vars/simple.css +0 -3
  245. package/test/browser/css/modify-vars/simple.css +0 -8
  246. package/test/browser/css/plugin/plugin.css +0 -3
  247. package/test/browser/css/postProcessor/postProcessor.css +0 -4
  248. package/test/browser/css/relative-urls/urls.css +0 -36
  249. package/test/browser/css/rewrite-urls/urls.css +0 -36
  250. package/test/browser/css/rootpath/urls.css +0 -35
  251. package/test/browser/css/rootpath-relative/urls.css +0 -35
  252. package/test/browser/css/rootpath-rewrite-urls/urls.css +0 -35
  253. package/test/browser/css/urls.css +0 -54
  254. package/test/browser/generator/benchmark.config.js +0 -50
  255. package/test/browser/generator/generate.js +0 -78
  256. package/test/browser/generator/runner.config.js +0 -184
  257. package/test/browser/generator/runner.js +0 -2
  258. package/test/browser/generator/template.js +0 -104
  259. package/test/browser/generator/utils.js +0 -14
  260. package/test/browser/less/console-errors/test-error.less +0 -3
  261. package/test/browser/less/console-errors/test-error.txt +0 -2
  262. package/test/browser/less/errors/image-height-error.less +0 -3
  263. package/test/browser/less/errors/image-height-error.txt +0 -4
  264. package/test/browser/less/errors/image-size-error.less +0 -3
  265. package/test/browser/less/errors/image-size-error.txt +0 -4
  266. package/test/browser/less/errors/image-width-error.less +0 -3
  267. package/test/browser/less/errors/image-width-error.txt +0 -4
  268. package/test/browser/less/global-vars/simple.less +0 -3
  269. package/test/browser/less/imports/urls.less +0 -4
  270. package/test/browser/less/imports/urls2.less +0 -4
  271. package/test/browser/less/modify-vars/imports/simple2.less +0 -4
  272. package/test/browser/less/modify-vars/simple.less +0 -8
  273. package/test/browser/less/nested-gradient-with-svg-gradient/mixin-consumer.less +0 -5
  274. package/test/browser/less/nested-gradient-with-svg-gradient/svg-gradient-mixin.less +0 -15
  275. package/test/browser/less/plugin/plugin.js +0 -4
  276. package/test/browser/less/plugin/plugin.less +0 -4
  277. package/test/browser/less/postProcessor/postProcessor.less +0 -4
  278. package/test/browser/less/relative-urls/urls.less +0 -34
  279. package/test/browser/less/rewrite-urls/urls.less +0 -34
  280. package/test/browser/less/rootpath/urls.less +0 -33
  281. package/test/browser/less/rootpath-relative/urls.less +0 -33
  282. package/test/browser/less/rootpath-rewrite-urls/urls.less +0 -33
  283. package/test/browser/less/urls.less +0 -62
  284. package/test/browser/runner-VisitorPlugin-options.js +0 -3
  285. package/test/browser/runner-VisitorPlugin.js +0 -3
  286. package/test/browser/runner-browser-options.js +0 -51
  287. package/test/browser/runner-browser-spec.js +0 -12
  288. package/test/browser/runner-console-errors.js +0 -5
  289. package/test/browser/runner-errors-options.js +0 -6
  290. package/test/browser/runner-errors-spec.js +0 -3
  291. package/test/browser/runner-filemanagerPlugin-options.js +0 -5
  292. package/test/browser/runner-filemanagerPlugin.js +0 -3
  293. package/test/browser/runner-global-vars-options.js +0 -7
  294. package/test/browser/runner-global-vars-spec.js +0 -3
  295. package/test/browser/runner-main-options.js +0 -17
  296. package/test/browser/runner-main-spec.js +0 -7
  297. package/test/browser/runner-modify-vars-options.js +0 -5
  298. package/test/browser/runner-modify-vars-spec.js +0 -33
  299. package/test/browser/runner-no-js-errors-options.js +0 -4
  300. package/test/browser/runner-no-js-errors-spec.js +0 -3
  301. package/test/browser/runner-postProcessorPlugin-options.js +0 -3
  302. package/test/browser/runner-postProcessorPlugin.js +0 -3
  303. package/test/browser/runner-preProcessorPlugin-options.js +0 -3
  304. package/test/browser/runner-preProcessorPlugin.js +0 -3
  305. package/test/browser/runner-production-options.js +0 -3
  306. package/test/browser/runner-production-spec.js +0 -5
  307. package/test/browser/runner-relative-urls-options.js +0 -3
  308. package/test/browser/runner-relative-urls-spec.js +0 -3
  309. package/test/browser/runner-rewrite-urls-options.js +0 -3
  310. package/test/browser/runner-rewrite-urls-spec.js +0 -3
  311. package/test/browser/runner-rootpath-options.js +0 -3
  312. package/test/browser/runner-rootpath-relative-options.js +0 -4
  313. package/test/browser/runner-rootpath-relative-spec.js +0 -3
  314. package/test/browser/runner-rootpath-rewrite-urls-options.js +0 -4
  315. package/test/browser/runner-rootpath-rewrite-urls-spec.js +0 -3
  316. package/test/browser/runner-rootpath-spec.js +0 -3
  317. package/test/browser/runner-strict-units-options.js +0 -5
  318. package/test/browser/runner-strict-units-spec.js +0 -3
  319. package/test/index.js +0 -307
  320. package/test/less-test.js +0 -984
  321. package/test/mocha-playwright/runner.js +0 -210
  322. package/test/modify-vars.js +0 -28
  323. package/test/plugins/filemanager/index.js +0 -19
  324. package/test/plugins/postprocess/index.js +0 -14
  325. package/test/plugins/preprocess/index.js +0 -19
  326. package/test/plugins/visitor/index.js +0 -24
  327. package/test/sourcemaps/basic.json +0 -1
  328. package/test/sourcemaps/comprehensive.json +0 -1
  329. package/test/sourcemaps/custom-props.json +0 -1
  330. package/test/sourcemaps/index.html +0 -17
  331. package/test/sourcemaps/sourcemaps-basepath.json +0 -1
  332. package/test/sourcemaps/sourcemaps-include-source.json +0 -1
  333. package/test/sourcemaps/sourcemaps-rootpath.json +0 -1
  334. package/test/sourcemaps/sourcemaps-url.json +0 -1
  335. package/test/sourcemaps-disable-annotation/basic.json +0 -1
  336. package/test/sourcemaps-variable-selector/basic.json +0 -1
  337. package/test/test-es6.ts +0 -17
  338. package/test.less +0 -1
  339. package/tsconfig.build.json +0 -7
  340. package/tsconfig.json +0 -21
package/test/less-test.js DELETED
@@ -1,984 +0,0 @@
1
- /* jshint latedef: nofunc */
2
- var semver = require('semver');
3
- var logger = require('../lib/less/logger').default;
4
- var { cosmiconfigSync } = require('cosmiconfig');
5
- var glob = require('glob');
6
-
7
- var isVerbose = process.env.npm_config_loglevel !== 'concise';
8
- logger.addListener({
9
- info(msg) {
10
- if (isVerbose) {
11
- process.stdout.write(msg + '\n');
12
- }
13
- },
14
- warn(msg) {
15
- process.stdout.write(msg + '\n');
16
- },
17
- error(msg) {
18
- process.stdout.write(msg + '\n');
19
- }
20
- });
21
-
22
-
23
- module.exports = function(testFilter) {
24
- var path = require('path'),
25
- fs = require('fs'),
26
- clone = require('copy-anything').copy;
27
-
28
- var less = require('../');
29
-
30
- var stylize = require('../lib/less-node/lessc-helper').stylize;
31
-
32
- var globals = Object.keys(global);
33
-
34
- var oneTestOnly = testFilter || process.argv[2],
35
- isFinished = false;
36
-
37
- var testFolder = path.dirname(require.resolve('@less/test-data'));
38
- var lessFolder = testFolder;
39
-
40
- // Define String.prototype.endsWith if it doesn't exist (in older versions of node)
41
- // This is required by the testSourceMap function below
42
- if (typeof String.prototype.endsWith !== 'function') {
43
- String.prototype.endsWith = function (str) {
44
- return this.slice(-str.length) === str;
45
- }
46
- }
47
-
48
- var queueList = [],
49
- queueRunning = false;
50
- function queue(func) {
51
- if (queueRunning) {
52
- // console.log("adding to queue");
53
- queueList.push(func);
54
- } else {
55
- // console.log("first in queue - starting");
56
- queueRunning = true;
57
- func();
58
- }
59
- }
60
- function release() {
61
- if (queueList.length) {
62
- // console.log("running next in queue");
63
- var func = queueList.shift();
64
- setTimeout(func, 0);
65
- } else {
66
- // console.log("stopping queue");
67
- queueRunning = false;
68
- }
69
- }
70
-
71
- var totalTests = 0,
72
- failedTests = 0,
73
- passedTests = 0,
74
- finishTimer = setInterval(endTest, 500);
75
-
76
- less.functions.functionRegistry.addMultiple({
77
- add: function (a, b) {
78
- return new(less.tree.Dimension)(a.value + b.value);
79
- },
80
- increment: function (a) {
81
- return new(less.tree.Dimension)(a.value + 1);
82
- },
83
- _color: function (str) {
84
- if (str.value === 'evil red') { return new(less.tree.Color)('600'); }
85
- }
86
- });
87
-
88
- function validateSourcemapMappings(sourcemap, lessFile, compiledCSS) {
89
- // Validate sourcemap mappings using SourceMapConsumer
90
- var SourceMapConsumer = require('source-map').SourceMapConsumer;
91
- // sourcemap can be either a string or already parsed object
92
- var sourceMapObj = typeof sourcemap === 'string' ? JSON.parse(sourcemap) : sourcemap;
93
- var consumer = new SourceMapConsumer(sourceMapObj);
94
-
95
- // Read the LESS source file
96
- var lessSource = fs.readFileSync(lessFile, 'utf8');
97
- var lessLines = lessSource.split('\n');
98
-
99
- // Use the compiled CSS (remove sourcemap annotation for validation)
100
- var cssSource = compiledCSS.replace(/\/\*# sourceMappingURL=.*\*\/\s*$/, '').trim();
101
- var cssLines = cssSource.split('\n');
102
-
103
- var errors = [];
104
- var validatedMappings = 0;
105
-
106
- // Validate mappings for each line in the CSS
107
- for (var cssLine = 1; cssLine <= cssLines.length; cssLine++) {
108
- var cssLineContent = cssLines[cssLine - 1];
109
- // Skip empty lines
110
- if (!cssLineContent.trim()) {
111
- continue;
112
- }
113
-
114
- // Check mapping for the start of this CSS line
115
- var mapping = consumer.originalPositionFor({
116
- line: cssLine,
117
- column: 0
118
- });
119
-
120
- if (mapping.source) {
121
- validatedMappings++;
122
-
123
- // Verify the source file exists in the sourcemap
124
- if (!sourceMapObj.sources || sourceMapObj.sources.indexOf(mapping.source) === -1) {
125
- errors.push('Line ' + cssLine + ': mapped to source "' + mapping.source + '" which is not in sources array');
126
- }
127
-
128
- // Verify the line number is valid
129
- if (mapping.line && mapping.line > 0) {
130
- // If we can find the source file, validate the line exists
131
- var sourceIndex = sourceMapObj.sources.indexOf(mapping.source);
132
- if (sourceIndex >= 0 && sourceMapObj.sourcesContent && sourceMapObj.sourcesContent[sourceIndex] !== undefined && sourceMapObj.sourcesContent[sourceIndex] !== null) {
133
- var sourceContent = sourceMapObj.sourcesContent[sourceIndex];
134
- // Ensure sourceContent is a string (it should be, but be defensive)
135
- if (typeof sourceContent !== 'string') {
136
- sourceContent = String(sourceContent);
137
- }
138
- // Split by newline - handle both \n and \r\n
139
- var sourceLines = sourceContent.split(/\r?\n/);
140
- if (mapping.line > sourceLines.length) {
141
- errors.push('Line ' + cssLine + ': mapped to line ' + mapping.line + ' in "' + mapping.source + '" but source only has ' + sourceLines.length + ' lines');
142
- }
143
- } else if (sourceIndex >= 0) {
144
- // Source content not embedded, try to validate against the actual file if it matches
145
- // This is a best-effort validation
146
- }
147
- }
148
- }
149
- }
150
-
151
- // Validate that all sources in the sourcemap are valid
152
- if (sourceMapObj.sources) {
153
- sourceMapObj.sources.forEach(function(source, index) {
154
- if (sourceMapObj.sourcesContent && sourceMapObj.sourcesContent[index]) {
155
- // Source content is embedded, validate it's not empty
156
- if (!sourceMapObj.sourcesContent[index].trim()) {
157
- errors.push('Source "' + source + '" has empty content');
158
- }
159
- }
160
- });
161
- }
162
-
163
- if (consumer.destroy && typeof consumer.destroy === 'function') {
164
- consumer.destroy();
165
- }
166
-
167
- return {
168
- valid: errors.length === 0,
169
- errors: errors,
170
- mappingsValidated: validatedMappings
171
- };
172
- }
173
-
174
- function testSourcemap(name, err, compiledLess, doReplacements, sourcemap, baseFolder, getFilename) {
175
- if (err) {
176
- fail('ERROR: ' + (err && err.message));
177
- return;
178
- }
179
- // Check the sourceMappingURL at the bottom of the file
180
- // Default expected URL is name + '.css.map', but can be overridden by sourceMapURL option
181
- var sourceMappingPrefix = '/*# sourceMappingURL=',
182
- sourceMappingSuffix = ' */';
183
- var indexOfSourceMappingPrefix = compiledLess.indexOf(sourceMappingPrefix);
184
- if (indexOfSourceMappingPrefix === -1) {
185
- fail('ERROR: sourceMappingURL was not found in ' + baseFolder + '/' + name + '.css.');
186
- return;
187
- }
188
-
189
- var startOfSourceMappingValue = indexOfSourceMappingPrefix + sourceMappingPrefix.length,
190
- indexOfSuffix = compiledLess.indexOf(sourceMappingSuffix, startOfSourceMappingValue),
191
- actualSourceMapURL = compiledLess.substring(startOfSourceMappingValue, indexOfSuffix === -1 ? compiledLess.length : indexOfSuffix).trim();
192
-
193
- // For tests with custom sourceMapURL, we just verify it exists and is non-empty
194
- // The actual value will be validated by comparing the sourcemap JSON
195
- if (!actualSourceMapURL) {
196
- fail('ERROR: sourceMappingURL is empty in ' + baseFolder + '/' + name + '.css.');
197
- return;
198
- }
199
-
200
- // Use getFilename if available (for sourcemap tests with subdirectories)
201
- var jsonPath;
202
- if (getFilename && typeof getFilename === 'function') {
203
- jsonPath = getFilename(name, 'sourcemap', baseFolder);
204
- } else {
205
- // Fallback: extract just the filename for sourcemap JSON files
206
- var jsonFilename = path.basename(name);
207
- jsonPath = path.join('test/sourcemaps', jsonFilename) + '.json';
208
- }
209
- fs.readFile(jsonPath, 'utf8', function (e, expectedSourcemap) {
210
- process.stdout.write('- ' + path.join(baseFolder, name) + ': ');
211
- if (e) {
212
- fail('ERROR: Could not read expected sourcemap file: ' + jsonPath + ' - ' + e.message);
213
- return;
214
- }
215
-
216
- // Apply doReplacements to the expected sourcemap to handle {path} placeholders
217
- // This normalizes absolute paths that differ between environments
218
- // For sourcemaps, we need to ensure {path} uses forward slashes to avoid breaking JSON
219
- // (backslashes in JSON strings need escaping, and sourcemaps should use forward slashes anyway)
220
- var replacementPath = path.join(path.dirname(path.join(baseFolder, name) + '.less'), '/');
221
- // Normalize to forward slashes for sourcemap JSON (web-compatible)
222
- replacementPath = replacementPath.replace(/\\/g, '/');
223
- // Replace {path} with normalized forward-slash path BEFORE calling doReplacements
224
- // This ensures the JSON is always valid and uses web-compatible paths
225
- expectedSourcemap = expectedSourcemap.replace(/\{path\}/g, replacementPath);
226
- // Also handle other placeholders that might be in the sourcemap (but {path} is already done)
227
- expectedSourcemap = doReplacements(expectedSourcemap, baseFolder, path.join(baseFolder, name) + '.less');
228
-
229
- // Normalize paths in sourcemap JSON to use forward slashes (web-compatible)
230
- // We need to parse the JSON, normalize the file property, then stringify for comparison
231
- // This avoids breaking escape sequences like \n in the JSON string
232
- function normalizeSourcemapPaths(sm) {
233
- try {
234
- var parsed = typeof sm === 'string' ? JSON.parse(sm) : sm;
235
- if (parsed.file) {
236
- parsed.file = parsed.file.replace(/\\/g, '/');
237
- }
238
- // Also normalize paths in sources array
239
- if (parsed.sources && Array.isArray(parsed.sources)) {
240
- parsed.sources = parsed.sources.map(function(src) {
241
- return src.replace(/\\/g, '/');
242
- });
243
- }
244
- return JSON.stringify(parsed, null, 0);
245
- } catch (parseErr) {
246
- // If parsing fails, return original (shouldn't happen)
247
- return sm;
248
- }
249
- }
250
-
251
- var normalizedSourcemap = normalizeSourcemapPaths(sourcemap);
252
- var normalizedExpected = normalizeSourcemapPaths(expectedSourcemap);
253
-
254
- if (normalizedSourcemap === normalizedExpected) {
255
- // Validate the sourcemap mappings are correct
256
- // Find the actual LESS file - it might be in a subdirectory
257
- var nameParts = name.split('/');
258
- var lessFileName = nameParts[nameParts.length - 1];
259
- var lessFileDir = nameParts.length > 1 ? nameParts.slice(0, -1).join('/') : '';
260
- var lessFile = path.join(lessFolder, lessFileDir, lessFileName) + '.less';
261
-
262
- // Only validate if the LESS file exists
263
- if (fs.existsSync(lessFile)) {
264
- try {
265
- // Parse the sourcemap once for validation (avoid re-parsing)
266
- // Use the original sourcemap string, not the normalized one
267
- var sourceMapObjForValidation = typeof sourcemap === 'string' ? JSON.parse(sourcemap) : sourcemap;
268
- var validation = validateSourcemapMappings(sourceMapObjForValidation, lessFile, compiledLess);
269
- if (!validation.valid) {
270
- fail('ERROR: Sourcemap validation failed:\n' + validation.errors.join('\n'));
271
- return;
272
- }
273
- if (isVerbose && validation.mappingsValidated > 0) {
274
- process.stdout.write(' (validated ' + validation.mappingsValidated + ' mappings)');
275
- }
276
- } catch (validationErr) {
277
- if (isVerbose) {
278
- process.stdout.write(' (validation error: ' + validationErr.message + ')');
279
- }
280
- // Don't fail the test if validation has an error, just log it
281
- }
282
- }
283
-
284
- ok('OK');
285
- } else if (err) {
286
- fail('ERROR: ' + (err && err.message));
287
- if (isVerbose) {
288
- process.stdout.write('\n');
289
- process.stdout.write(err.stack + '\n');
290
- }
291
- } else {
292
- difference('FAIL', normalizedExpected, normalizedSourcemap);
293
- }
294
- });
295
- }
296
-
297
- function testSourcemapWithoutUrlAnnotation(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
298
- if (err) {
299
- fail('ERROR: ' + (err && err.message));
300
- return;
301
- }
302
- // This matches with strings that end($) with source mapping url annotation.
303
- var sourceMapRegExp = /\/\*# sourceMappingURL=.+\.css\.map \*\/$/;
304
- if (sourceMapRegExp.test(compiledLess)) {
305
- fail('ERROR: sourceMappingURL found in ' + baseFolder + '/' + name + '.css.');
306
- return;
307
- }
308
-
309
- // Even if annotation is not necessary, the map file should be there.
310
- fs.readFile(path.join('test/', name) + '.json', 'utf8', function (e, expectedSourcemap) {
311
- process.stdout.write('- ' + path.join(baseFolder, name) + ': ');
312
- if (sourcemap === expectedSourcemap) {
313
- ok('OK');
314
- } else if (err) {
315
- fail('ERROR: ' + (err && err.message));
316
- if (isVerbose) {
317
- process.stdout.write('\n');
318
- process.stdout.write(err.stack + '\n');
319
- }
320
- } else {
321
- difference('FAIL', expectedSourcemap, sourcemap);
322
- }
323
- });
324
- }
325
-
326
- function testEmptySourcemap(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
327
- process.stdout.write('- ' + path.join(baseFolder, name) + ': ');
328
- if (err) {
329
- fail('ERROR: ' + (err && err.message));
330
- } else {
331
- var expectedSourcemap = undefined;
332
- if ( compiledLess !== '' ) {
333
- difference('\nCompiledLess must be empty', '', compiledLess);
334
-
335
- } else if (sourcemap !== expectedSourcemap) {
336
- fail('Sourcemap must be undefined');
337
- } else {
338
- ok('OK');
339
- }
340
- }
341
- }
342
-
343
- function testSourcemapWithVariableInSelector(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
344
- if (err) {
345
- fail('ERROR: ' + (err && err.message));
346
- return;
347
- }
348
-
349
- // Even if annotation is not necessary, the map file should be there.
350
- fs.readFile(path.join('test/', name) + '.json', 'utf8', function (e, expectedSourcemap) {
351
- process.stdout.write('- ' + path.join(baseFolder, name) + ': ');
352
- if (sourcemap === expectedSourcemap) {
353
- ok('OK');
354
- } else if (err) {
355
- fail('ERROR: ' + (err && err.message));
356
- if (isVerbose) {
357
- process.stdout.write('\n');
358
- process.stdout.write(err.stack + '\n');
359
- }
360
- } else {
361
- difference('FAIL', expectedSourcemap, sourcemap);
362
- }
363
- });
364
- }
365
-
366
- function testImports(name, err, compiledLess, doReplacements, sourcemap, baseFolder, imports) {
367
- if (err) {
368
- fail('ERROR: ' + (err && err.message));
369
- return;
370
- }
371
-
372
- function stringify(str) {
373
- return JSON.stringify(imports, null, ' ')
374
- }
375
-
376
- /** Imports are not sorted */
377
- const importsString = stringify(imports.sort())
378
-
379
- fs.readFile(path.join(lessFolder, name) + '.json', 'utf8', function (e, expectedImports) {
380
- if (e) {
381
- fail('ERROR: ' + (e && e.message));
382
- return;
383
- }
384
- process.stdout.write('- ' + path.join(baseFolder, name) + ': ');
385
- expectedImports = stringify(JSON.parse(expectedImports).sort());
386
- expectedImports = globalReplacements(expectedImports, baseFolder);
387
-
388
- if (expectedImports === importsString) {
389
- ok('OK');
390
- } else if (err) {
391
- fail('ERROR: ' + (err && err.message));
392
- if (isVerbose) {
393
- process.stdout.write('\n');
394
- process.stdout.write(err.stack + '\n');
395
- }
396
- } else {
397
- difference('FAIL', expectedImports, importsString);
398
- }
399
- });
400
- }
401
-
402
- function testErrors(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
403
- fs.readFile(path.join(baseFolder, name) + '.txt', 'utf8', function (e, expectedErr) {
404
- process.stdout.write('- ' + path.join(baseFolder, name) + ': ');
405
- expectedErr = doReplacements(expectedErr, baseFolder, err && err.filename);
406
- if (!err) {
407
- if (compiledLess) {
408
- fail('No Error', 'red');
409
- } else {
410
- fail('No Error, No Output');
411
- }
412
- } else {
413
- var errMessage = err.toString();
414
- if (errMessage === expectedErr) {
415
- ok('OK');
416
- } else {
417
- difference('FAIL', expectedErr, errMessage);
418
- }
419
- }
420
- });
421
- }
422
-
423
- // To fix ci fail about error format change in upstream v8 project
424
- // https://github.com/v8/v8/commit/c0fd89c3c089e888c4f4e8582e56db7066fa779b
425
- // Node 16.9.0+ include this change via https://github.com/nodejs/node/pull/39947
426
- function testTypeErrors(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
427
- const fileSuffix = semver.gte(process.version, 'v16.9.0') ? '-2.txt' : '.txt';
428
- fs.readFile(path.join(baseFolder, name) + fileSuffix, 'utf8', function (e, expectedErr) {
429
- process.stdout.write('- ' + path.join(baseFolder, name) + ': ');
430
- expectedErr = doReplacements(expectedErr, baseFolder, err && err.filename);
431
- if (!err) {
432
- if (compiledLess) {
433
- fail('No Error', 'red');
434
- } else {
435
- fail('No Error, No Output');
436
- }
437
- } else {
438
- var errMessage = err.toString();
439
- if (errMessage === expectedErr) {
440
- ok('OK');
441
- } else {
442
- difference('FAIL', expectedErr, errMessage);
443
- }
444
- }
445
- });
446
- }
447
-
448
- // https://github.com/less/less.js/issues/3112
449
- function testJSImport() {
450
- process.stdout.write('- Testing root function registry');
451
- less.functions.functionRegistry.add('ext', function() {
452
- return new less.tree.Anonymous('file');
453
- });
454
- var expected = '@charset "utf-8";\n';
455
- toCSS({}, path.join(lessFolder, 'tests-config', 'root-registry', 'root.less'), function(error, output) {
456
- if (error) {
457
- return fail('ERROR: ' + error);
458
- }
459
- if (output.css === expected) {
460
- return ok('OK');
461
- }
462
- difference('FAIL', expected, output.css);
463
- });
464
- }
465
-
466
- function globalReplacements(input, directory, filename) {
467
- var path = require('path');
468
- var p = filename ? path.join(path.dirname(filename), '/') : directory;
469
-
470
- // For debug tests in subdirectories (comments/, mediaquery/, all/),
471
- // the import/ directory and main linenumbers.less file are at the parent debug/ level, not in the subdirectory
472
- var isDebugSubdirectory = false;
473
- var debugParentPath = null;
474
-
475
- if (directory) {
476
- // Normalize directory path separators for matching
477
- var normalizedDir = directory.replace(/\\/g, '/');
478
- // Check if we're in a debug subdirectory
479
- if (normalizedDir.includes('/debug/') && (normalizedDir.includes('/comments/') || normalizedDir.includes('/mediaquery/') || normalizedDir.includes('/all/'))) {
480
- isDebugSubdirectory = true;
481
- // Extract the debug/ directory path (parent of the subdirectory)
482
- // Match everything up to and including /debug/ (works with both absolute and relative paths)
483
- var debugMatch = normalizedDir.match(/(.+\/debug)\//);
484
- if (debugMatch) {
485
- debugParentPath = debugMatch[1];
486
- }
487
- }
488
- }
489
-
490
- if (isDebugSubdirectory && debugParentPath) {
491
- // For {path} placeholder, use the parent debug/ directory
492
- // Convert back to native path format
493
- p = debugParentPath.replace(/\//g, path.sep) + path.sep;
494
- }
495
-
496
- var pathimport;
497
- if (isDebugSubdirectory && debugParentPath) {
498
- pathimport = path.join(debugParentPath.replace(/\//g, path.sep), 'import') + path.sep;
499
- } else {
500
- pathimport = path.join(directory + 'import/');
501
- }
502
-
503
- var pathesc = p.replace(/[.:/\\]/g, function(a) { return '\\' + (a == '\\' ? '\/' : a); }),
504
- pathimportesc = pathimport.replace(/[.:/\\]/g, function(a) { return '\\' + (a == '\\' ? '\/' : a); });
505
-
506
- return input.replace(/\{path\}/g, p)
507
- .replace(/\{node\}/g, '')
508
- .replace(/\{\/node\}/g, '')
509
- .replace(/\{pathhref\}/g, '')
510
- .replace(/\{404status\}/g, '')
511
- .replace(/\{nodepath\}/g, path.join(process.cwd(), 'node_modules', '/'))
512
- .replace(/\{pathrel\}/g, path.join(path.relative(lessFolder, p), '/'))
513
- .replace(/\{pathesc\}/g, pathesc)
514
- .replace(/\{pathimport\}/g, pathimport)
515
- .replace(/\{pathimportesc\}/g, pathimportesc)
516
- .replace(/\r\n/g, '\n');
517
- }
518
-
519
- function checkGlobalLeaks() {
520
- return Object.keys(global).filter(function(v) {
521
- return globals.indexOf(v) < 0;
522
- });
523
- }
524
-
525
- function testSyncronous(options, filenameNoExtension) {
526
- if (oneTestOnly && ('Test Sync ' + filenameNoExtension) !== oneTestOnly) {
527
- return;
528
- }
529
- totalTests++;
530
- queue(function() {
531
- var isSync = true;
532
- toCSS(options, path.join(lessFolder, filenameNoExtension + '.less'), function (err, result) {
533
- process.stdout.write('- Test Sync ' + filenameNoExtension + ': ');
534
-
535
- if (isSync) {
536
- ok('OK');
537
- } else {
538
- fail('Not Sync');
539
- }
540
- release();
541
- });
542
- isSync = false;
543
- });
544
- }
545
-
546
- function runTestSet(options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
547
- // Handle case where first parameter is glob patterns (no options object)
548
- if (Array.isArray(options)) {
549
- // First parameter is glob patterns, no options object
550
- foldername = options;
551
- options = {};
552
- } else if (typeof options === 'string') {
553
- // First parameter is foldername (no options object)
554
- foldername = options;
555
- options = {};
556
- } else {
557
- options = options ? clone(options) : {};
558
- }
559
- runTestSetInternal(lessFolder, options, foldername, verifyFunction, nameModifier, doReplacements, getFilename);
560
- }
561
-
562
- function runTestSetNormalOnly(options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
563
- runTestSetInternal(lessFolder, options, foldername, verifyFunction, nameModifier, doReplacements, getFilename);
564
- }
565
-
566
- function runTestSetInternal(baseFolder, opts, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
567
- foldername = foldername || '';
568
-
569
- var originalOptions = opts || {};
570
-
571
- if (!doReplacements) {
572
- doReplacements = globalReplacements;
573
- }
574
-
575
- // Handle glob patterns with exclusions
576
- if (Array.isArray(foldername)) {
577
- var patterns = foldername;
578
- var includePatterns = [];
579
- var excludePatterns = [];
580
-
581
-
582
- patterns.forEach(function(pattern) {
583
- if (pattern.startsWith('!')) {
584
- excludePatterns.push(pattern.substring(1));
585
- } else {
586
- includePatterns.push(pattern);
587
- }
588
- });
589
-
590
- // Use glob to find all matching files, excluding the excluded patterns
591
- var allFiles = [];
592
- includePatterns.forEach(function(pattern) {
593
- var files = glob.sync(pattern, {
594
- cwd: baseFolder,
595
- absolute: true,
596
- ignore: excludePatterns
597
- });
598
-
599
- allFiles = allFiles.concat(files);
600
- });
601
-
602
- // Note: needle mocking is set up globally in index.js
603
-
604
- // Process each .less file found
605
- allFiles.forEach(function(filePath) {
606
- if (/\.less$/.test(filePath)) {
607
- var file = path.basename(filePath);
608
- // For glob patterns, we need to construct the relative path differently
609
- // The filePath is absolute, so we need to get the path relative to the test-data directory
610
- var relativePath = path.relative(baseFolder, path.dirname(filePath)) + '/';
611
-
612
- // Only process files that have corresponding .css files (these are the actual tests)
613
- var cssPath = path.join(path.dirname(filePath), path.basename(file, '.less') + '.css');
614
- if (fs.existsSync(cssPath)) {
615
- // Process this file using the existing logic
616
- processFileWithInfo({
617
- file: file,
618
- fullPath: filePath,
619
- relativePath: relativePath
620
- });
621
- }
622
- }
623
- });
624
-
625
-
626
- return;
627
- }
628
-
629
- function processFileWithInfo(fileInfo) {
630
- var file = fileInfo.file;
631
- var fullPath = fileInfo.fullPath;
632
- var relativePath = fileInfo.relativePath;
633
-
634
- // Load config for this specific file using cosmiconfig
635
- var configResult = cosmiconfigSync('styles').search(path.dirname(fullPath));
636
-
637
- // Deep clone the original options to prevent Less from modifying shared objects
638
- var options = JSON.parse(JSON.stringify(originalOptions || {}));
639
-
640
- if (configResult && configResult.config && configResult.config.language && configResult.config.language.less) {
641
- // Deep clone and merge the language.less settings with the original options
642
- var lessConfig = JSON.parse(JSON.stringify(configResult.config.language.less));
643
- Object.keys(lessConfig).forEach(function(key) {
644
- options[key] = lessConfig[key];
645
- });
646
- }
647
-
648
- // Merge any lessOptions from the testMap (for dynamic options like getVars functions)
649
- if (originalOptions && originalOptions.lessOptions) {
650
- Object.keys(originalOptions.lessOptions).forEach(function(key) {
651
- var value = originalOptions.lessOptions[key];
652
- if (typeof value === 'function') {
653
- // For functions, call them with the file path
654
- var result = value(fullPath);
655
- options[key] = result;
656
- } else {
657
- // For static values, use them directly
658
- options[key] = value;
659
- }
660
- });
661
- }
662
-
663
- // Don't pass stylize to less.render as it's not a valid option
664
-
665
- var name = getBasename(file, relativePath);
666
-
667
-
668
- if (oneTestOnly && typeof oneTestOnly === 'string' && !name.includes(oneTestOnly)) {
669
- return;
670
- }
671
-
672
- totalTests++;
673
-
674
- if (options.sourceMap && !options.sourceMap.sourceMapFileInline) {
675
- // Set test infrastructure defaults only if not already set by styles.config.cjs
676
- // Less.js core (parse-tree.js) will handle normalization of:
677
- // - sourceMapBasepath (defaults to input file's directory)
678
- // - sourceMapInputFilename (defaults to options.filename)
679
- // - sourceMapFilename (derived from sourceMapOutputFilename or input filename)
680
- // - sourceMapOutputFilename (derived from input filename if not set)
681
- if (!options.sourceMap.sourceMapOutputFilename) {
682
- // Needed for sourcemap file name in JSON output
683
- options.sourceMap.sourceMapOutputFilename = name + '.css';
684
- }
685
- if (!options.sourceMap.sourceMapRootpath) {
686
- // Test-specific default for consistent test output paths
687
- options.sourceMap.sourceMapRootpath = 'testweb/';
688
- }
689
- }
690
-
691
- options.getVars = function(file) {
692
- try {
693
- return JSON.parse(fs.readFileSync(getFilename(getBasename(file, relativePath), 'vars', baseFolder), 'utf8'));
694
- }
695
- catch (e) {
696
- return {};
697
- }
698
- };
699
-
700
- var doubleCallCheck = false;
701
- queue(function() {
702
- toCSS(options, fullPath, function (err, result) {
703
-
704
- if (doubleCallCheck) {
705
- totalTests++;
706
- fail('less is calling back twice');
707
- process.stdout.write(doubleCallCheck + '\n');
708
- process.stdout.write((new Error()).stack + '\n');
709
- return;
710
- }
711
- doubleCallCheck = (new Error()).stack;
712
-
713
- /**
714
- * @todo - refactor so the result object is sent to the verify function
715
- */
716
- if (verifyFunction) {
717
- var verificationResult = verifyFunction(
718
- name, err, result && result.css, doReplacements, result && result.map, baseFolder, result && result.imports, getFilename
719
- );
720
- release();
721
- return verificationResult;
722
- }
723
-
724
- if (err) {
725
- fail('ERROR: ' + (err && err.message));
726
- if (isVerbose) {
727
- process.stdout.write('\n');
728
- if (err.stack) {
729
- process.stdout.write(err.stack + '\n');
730
- } else {
731
- // this sometimes happen - show the whole error object
732
- console.log(err);
733
- }
734
- }
735
- release();
736
- return;
737
- }
738
- var css_name = name;
739
- if (nameModifier) { css_name = nameModifier(name); }
740
-
741
- // Check if we're using the new co-located structure (tests-unit/ or tests-config/) or the old separated structure
742
- var cssPath;
743
- if (relativePath.startsWith('tests-unit/') || relativePath.startsWith('tests-config/')) {
744
- // New co-located structure: CSS file is in the same directory as LESS file
745
- cssPath = path.join(path.dirname(fullPath), path.basename(file, '.less') + '.css');
746
- } else {
747
- // Old separated structure: CSS file is in separate css/ folder
748
- // Windows compatibility: css_name may already contain path separators
749
- // Use path.join with empty string to let path.join handle normalization
750
- cssPath = path.join(testFolder, css_name) + '.css';
751
- }
752
-
753
- // For the new structure, we need to handle replacements differently
754
- var replacementPath;
755
- if (relativePath.startsWith('tests-unit/') || relativePath.startsWith('tests-config/')) {
756
- replacementPath = path.dirname(fullPath);
757
- // Ensure replacementPath ends with a path separator for consistent matching
758
- if (!replacementPath.endsWith(path.sep)) {
759
- replacementPath += path.sep;
760
- }
761
- } else {
762
- replacementPath = path.join(baseFolder, relativePath);
763
- }
764
-
765
- var testName = fullPath.replace(/\.less$/, '');
766
- process.stdout.write('- ' + testName + ': ');
767
-
768
-
769
- var css = fs.readFileSync(cssPath, 'utf8');
770
- css = css && doReplacements(css, replacementPath);
771
- if (result.css === css) { ok('OK'); }
772
- else {
773
- difference('FAIL', css, result.css);
774
- }
775
-
776
- release();
777
- });
778
- });
779
- }
780
-
781
- function getBasename(file, relativePath) {
782
- var basePath = relativePath || foldername;
783
- // Ensure basePath ends with a slash for proper path construction
784
- if (basePath.charAt(basePath.length - 1) !== '/') {
785
- basePath = basePath + '/';
786
- }
787
- return basePath + path.basename(file, '.less');
788
- }
789
-
790
-
791
- // This function is only called for non-glob patterns now
792
- // For glob patterns, we use the glob library in the calling code
793
- var dirPath = path.join(baseFolder, foldername);
794
- var items = fs.readdirSync(dirPath);
795
-
796
- items.forEach(function(item) {
797
- if (/\.less$/.test(item)) {
798
- processFileWithInfo({
799
- file: item,
800
- fullPath: path.join(dirPath, item),
801
- relativePath: foldername
802
- });
803
- }
804
- });
805
- }
806
-
807
- function diff(left, right) {
808
- // Configure chalk to always show colors
809
- var chalk = require('chalk');
810
- chalk.level = 3; // Force colors on
811
-
812
- // Use jest-diff for much clearer output like Vitest
813
- var diffResult = require('jest-diff').diffStringsUnified(left || '', right || '', {
814
- expand: false,
815
- includeChangeCounts: true,
816
- contextLines: 1,
817
- aColor: chalk.red,
818
- bColor: chalk.green,
819
- changeColor: chalk.inverse,
820
- commonColor: chalk.dim
821
- });
822
-
823
- // jest-diff returns a string with ANSI colors, so we can output it directly
824
- process.stdout.write(diffResult + '\n');
825
- }
826
-
827
- function fail(msg) {
828
- process.stdout.write(stylize(msg, 'red') + '\n');
829
- failedTests++;
830
- endTest();
831
- }
832
-
833
- function difference(msg, left, right) {
834
- process.stdout.write(stylize(msg, 'yellow') + '\n');
835
- failedTests++;
836
-
837
- // Only show the diff, not the full text
838
- process.stdout.write(stylize('Diff:', 'yellow') + '\n');
839
-
840
- diff(left || '', right || '');
841
- endTest();
842
- }
843
-
844
- function ok(msg) {
845
- process.stdout.write(stylize(msg, 'green') + '\n');
846
- passedTests++;
847
- endTest();
848
- }
849
-
850
- function finished() {
851
- isFinished = true;
852
- endTest();
853
- }
854
-
855
- function endTest() {
856
- if (isFinished && ((failedTests + passedTests) >= totalTests)) {
857
- clearInterval(finishTimer);
858
- var leaked = checkGlobalLeaks();
859
- process.stdout.write('\n');
860
- if (failedTests > 0) {
861
- process.stdout.write(failedTests + stylize(' Failed', 'red') + ', ' + passedTests + ' passed\n');
862
- } else {
863
- process.stdout.write(stylize('All Passed ', 'green') + passedTests + ' run\n');
864
- }
865
- if (leaked.length > 0) {
866
- process.stdout.write('\n');
867
- process.stdout.write(stylize('Global leak detected: ', 'red') + leaked.join(', ') + '\n');
868
- }
869
-
870
- if (leaked.length || failedTests) {
871
- process.on('exit', function() { process.reallyExit(1); });
872
- }
873
- }
874
- }
875
-
876
- function contains(fullArray, obj) {
877
- for (var i = 0; i < fullArray.length; i++) {
878
- if (fullArray[i] === obj) {
879
- return true;
880
- }
881
- }
882
- return false;
883
- }
884
-
885
- /**
886
- *
887
- * @param {Object} options
888
- * @param {string} filePath
889
- * @param {Function} callback
890
- */
891
- function toCSS(options, filePath, callback) {
892
- // Deep clone options to prevent modifying the original, but preserve functions
893
- var originalOptions = options || {};
894
- options = JSON.parse(JSON.stringify(originalOptions));
895
-
896
- // Restore functions that were lost in JSON serialization
897
- if (originalOptions.getVars) {
898
- options.getVars = originalOptions.getVars;
899
- }
900
- var str = fs.readFileSync(filePath, 'utf8'), addPath = path.dirname(filePath);
901
-
902
- // Initialize paths array if it doesn't exist
903
- if (typeof options.paths !== 'string') {
904
- options.paths = options.paths || [];
905
- } else {
906
- options.paths = [options.paths];
907
- }
908
-
909
- // Add the current directory to paths if not already present
910
- if (!contains(options.paths, addPath)) {
911
- options.paths.push(addPath);
912
- }
913
-
914
- // Resolve all paths relative to the test file's directory
915
- options.paths = options.paths.map(searchPath => {
916
- if (path.isAbsolute(searchPath)) {
917
- return searchPath;
918
- }
919
- // Resolve relative to the test file's directory
920
- return path.resolve(path.dirname(filePath), searchPath);
921
- })
922
-
923
- options.filename = path.resolve(process.cwd(), filePath);
924
- options.optimization = options.optimization || 0;
925
-
926
- // Note: globalVars and modifyVars are now handled via styles.config.cjs or lessOptions
927
- if (options.plugin) {
928
- var Plugin = require(path.resolve(process.cwd(), options.plugin));
929
- options.plugins = [Plugin];
930
- }
931
- less.render(str, options, callback);
932
- }
933
-
934
- function testNoOptions() {
935
- if (oneTestOnly && 'Integration' !== oneTestOnly) {
936
- return;
937
- }
938
- totalTests++;
939
- try {
940
- process.stdout.write('- Integration - creating parser without options: ');
941
- less.render('');
942
- } catch (e) {
943
- fail(stylize('FAIL\n', 'red'));
944
- return;
945
- }
946
- ok(stylize('OK\n', 'green'));
947
- }
948
-
949
- // HTTP redirect testing is now handled directly in test/index.js
950
-
951
- function testDisablePluginRule() {
952
- less.render(
953
- '@plugin "../../plugin/some_plugin";',
954
- {disablePluginRule: true},
955
- function(err) {
956
- // TODO: Need a better way of identifing exactly which error is thrown. Checking
957
- // text like this tends to be rather brittle.
958
- const EXPECTED = '@plugin statements are not allowed when disablePluginRule is set to true';
959
- if (!err || String(err).indexOf(EXPECTED) < 0) {
960
- fail('ERROR: Expected "' + EXPECTED + '" error');
961
- return;
962
- }
963
- ok(stylize('OK\n', 'green'));
964
- }
965
- );
966
- }
967
-
968
- return {
969
- runTestSet: runTestSet,
970
- runTestSetNormalOnly: runTestSetNormalOnly,
971
- testSyncronous: testSyncronous,
972
- testErrors: testErrors,
973
- testTypeErrors: testTypeErrors,
974
- testSourcemap: testSourcemap,
975
- testSourcemapWithoutUrlAnnotation: testSourcemapWithoutUrlAnnotation,
976
- testSourcemapWithVariableInSelector: testSourcemapWithVariableInSelector,
977
- testImports: testImports,
978
- testEmptySourcemap: testEmptySourcemap,
979
- testNoOptions: testNoOptions,
980
- testDisablePluginRule: testDisablePluginRule,
981
- testJSImport: testJSImport,
982
- finished: finished
983
- };
984
- };