less 2.1.1 → 2.3.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 (333) hide show
  1. package/.idea/inspectionProfiles/Project_Default.xml +1 -0
  2. package/.idea/jsLinters/jscs.xml +4 -0
  3. package/.idea/jsLinters/jshint.xml +58 -4
  4. package/.idea/workspace.xml +451 -758
  5. package/.jscsrc +38 -0
  6. package/.jshintrc +0 -2
  7. package/.travis.yml +0 -1
  8. package/CHANGELOG.md +52 -0
  9. package/Gruntfile.js +37 -3
  10. package/bin/lessc +380 -316
  11. package/bower.json +1 -1
  12. package/dist/less.js +790 -479
  13. package/dist/less.min.js +7 -7
  14. package/lib/less/contexts.js +1 -1
  15. package/lib/less/data/unit-conversions.js +3 -3
  16. package/lib/less/environment/abstract-file-manager.js +4 -4
  17. package/lib/less/environment/environment.js +9 -0
  18. package/lib/less/functions/color-blending.js +3 -3
  19. package/lib/less/functions/color.js +8 -8
  20. package/lib/less/functions/data-uri.js +39 -31
  21. package/lib/less/functions/string.js +3 -1
  22. package/lib/less/functions/svg.js +12 -15
  23. package/lib/less/functions/types.js +15 -2
  24. package/lib/less/import-manager.js +10 -5
  25. package/lib/less/index.js +2 -1
  26. package/lib/less/parse-tree.js +8 -2
  27. package/lib/less/parse.js +65 -0
  28. package/lib/less/parser/parser-input.js +3 -2
  29. package/lib/less/parser/parser.js +24 -11
  30. package/lib/less/render.js +7 -36
  31. package/lib/less/source-map-output.js +3 -3
  32. package/lib/less/transform-tree.js +1 -1
  33. package/lib/less/tree/assignment.js +0 -1
  34. package/lib/less/tree/color.js +4 -4
  35. package/lib/less/tree/condition.js +1 -1
  36. package/lib/less/tree/debug-info.js +6 -2
  37. package/lib/less/tree/dimension.js +1 -1
  38. package/lib/less/tree/directive.js +25 -6
  39. package/lib/less/tree/expression.js +1 -1
  40. package/lib/less/tree/import.js +2 -2
  41. package/lib/less/tree/index.js +1 -0
  42. package/lib/less/tree/media.js +3 -2
  43. package/lib/less/tree/mixin-call.js +3 -4
  44. package/lib/less/tree/mixin-definition.js +4 -3
  45. package/lib/less/tree/quoted.js +1 -1
  46. package/lib/less/tree/rule.js +2 -3
  47. package/lib/less/tree/ruleset.js +263 -157
  48. package/lib/less/tree/selector.js +2 -1
  49. package/lib/less/tree/url.js +5 -2
  50. package/lib/less/tree/value.js +1 -1
  51. package/lib/less/tree/variable.js +1 -1
  52. package/lib/less/visitors/extend-visitor.js +60 -25
  53. package/lib/less/visitors/import-sequencer.js +20 -14
  54. package/lib/less/visitors/import-visitor.js +13 -10
  55. package/lib/less/visitors/to-css-visitor.js +37 -5
  56. package/lib/less-browser/add-default-options.js +5 -1
  57. package/lib/less-browser/bootstrap.js +10 -8
  58. package/lib/less-browser/cache.js +1 -1
  59. package/lib/less-browser/index.js +13 -12
  60. package/lib/less-browser/utils.js +9 -6
  61. package/lib/less-node/file-manager.js +51 -28
  62. package/lib/less-node/image-size.js +34 -0
  63. package/lib/less-node/index.js +3 -0
  64. package/lib/less-node/lessc-helper.js +3 -2
  65. package/lib/less-node/url-file-manager.js +2 -2
  66. package/lib/less-rhino/index.js +2 -4
  67. package/package.json +10 -7
  68. package/test/browser/common.js +13 -11
  69. package/test/browser/css/urls.css +6 -3
  70. package/test/browser/jasmine-jsreporter.js +2 -3
  71. package/test/browser/less/nested-gradient-with-svg-gradient/mixin-consumer.less +5 -0
  72. package/test/browser/less/nested-gradient-with-svg-gradient/svg-gradient-mixin.less +15 -0
  73. package/test/browser/less/urls.less +1 -0
  74. package/test/browser/less.js +15 -9424
  75. package/test/browser/runner-errors-options.js +0 -1
  76. package/test/browser/runner-errors-spec.js +0 -1
  77. package/test/browser/runner-legacy-options.js +0 -1
  78. package/test/browser/runner-no-js-errors-spec.js +0 -1
  79. package/test/browser/runner-production-options.js +0 -1
  80. package/test/browser/runner-relative-urls-options.js +0 -1
  81. package/test/browser/runner-rootpath-options.js +0 -1
  82. package/test/browser/runner-rootpath-relative-options.js +0 -1
  83. package/test/browser/runner-strict-units-options.js +0 -1
  84. package/test/copy-bom.js +72 -0
  85. package/test/css/comments.css +1 -0
  86. package/test/css/css-3.css +2 -0
  87. package/test/css/functions.css +2 -0
  88. package/test/css/import-interpolation.css +6 -0
  89. package/test/css/import-reference.css +25 -0
  90. package/test/css/include-path/include-path.css +6 -0
  91. package/test/css/mixins-important.css +6 -0
  92. package/test/css/selectors.css +10 -1
  93. package/test/css/url-args/urls.css +3 -3
  94. package/test/css/urls.css +12 -4
  95. package/test/css/variables.css +7 -1
  96. package/test/data/image.svg +3 -0
  97. package/test/index.js +14 -10
  98. package/test/less/comments.less +1 -0
  99. package/test/less/css-3.less +3 -0
  100. package/test/less/css-guards.less +2 -1
  101. package/test/less/errors/detached-ruleset-2.txt +1 -1
  102. package/test/less/errors/import-missing.txt +1 -1
  103. package/test/less/errors/parens-error-1.txt +1 -1
  104. package/test/less/errors/parens-error-2.txt +1 -1
  105. package/test/less/errors/parens-error-3.txt +1 -1
  106. package/test/less/errors/single-character.less +1 -0
  107. package/test/less/errors/single-character.txt +2 -0
  108. package/test/less/functions.less +5 -0
  109. package/test/less/import/import-interpolation.less +1 -0
  110. package/test/less/import/import-reference.less +38 -0
  111. package/test/less/import-reference.less +3 -1
  112. package/test/less/import.less +2 -0
  113. package/test/less/include-path/include-path.less +6 -0
  114. package/test/less/mixins-important.less +12 -0
  115. package/test/less/nested-gradient-with-svg-gradient/mixin-consumer.less +5 -0
  116. package/test/less/nested-gradient-with-svg-gradient/svg-gradient-mixin.less +15 -0
  117. package/test/less/selectors.less +19 -4
  118. package/test/less/urls.less +10 -1
  119. package/test/less/variables.less +17 -3
  120. package/test/less-bom/charsets.less +3 -0
  121. package/test/less-bom/colors.less +98 -0
  122. package/test/less-bom/comments.less +102 -0
  123. package/test/less-bom/comments2.less +20 -0
  124. package/test/less-bom/compression/compression.less +36 -0
  125. package/test/less-bom/css-3.less +154 -0
  126. package/test/less-bom/css-escapes.less +33 -0
  127. package/test/less-bom/css-guards.less +103 -0
  128. package/test/less-bom/css.less +108 -0
  129. package/test/less-bom/debug/import/test.less +25 -0
  130. package/test/less-bom/debug/linenumbers.less +33 -0
  131. package/test/less-bom/detached-rulesets.less +103 -0
  132. package/test/less-bom/empty.less +1 -0
  133. package/test/less-bom/errors/add-mixed-units.less +3 -0
  134. package/test/less-bom/errors/add-mixed-units.txt +4 -0
  135. package/test/less-bom/errors/add-mixed-units2.less +3 -0
  136. package/test/less-bom/errors/add-mixed-units2.txt +4 -0
  137. package/test/less-bom/errors/at-rules-undefined-var.less +4 -0
  138. package/test/less-bom/errors/at-rules-undefined-var.txt +4 -0
  139. package/test/less-bom/errors/bad-variable-declaration1.less +1 -0
  140. package/test/less-bom/errors/bad-variable-declaration1.txt +2 -0
  141. package/test/less-bom/errors/color-func-invalid-color.less +3 -0
  142. package/test/less-bom/errors/color-func-invalid-color.txt +4 -0
  143. package/test/less-bom/errors/color-invalid-hex-code.less +3 -0
  144. package/test/less-bom/errors/color-invalid-hex-code.txt +4 -0
  145. package/test/less-bom/errors/color-invalid-hex-code2.less +3 -0
  146. package/test/less-bom/errors/color-invalid-hex-code2.txt +4 -0
  147. package/test/less-bom/errors/css-guard-default-func.less +4 -0
  148. package/test/less-bom/errors/css-guard-default-func.txt +4 -0
  149. package/test/less-bom/errors/detached-ruleset-1.less +6 -0
  150. package/test/less-bom/errors/detached-ruleset-1.txt +4 -0
  151. package/test/less-bom/errors/detached-ruleset-2.less +6 -0
  152. package/test/less-bom/errors/detached-ruleset-2.txt +4 -0
  153. package/test/less-bom/errors/detached-ruleset-3.less +4 -0
  154. package/test/less-bom/errors/detached-ruleset-3.txt +4 -0
  155. package/test/less-bom/errors/detached-ruleset-4.less +5 -0
  156. package/test/less-bom/errors/detached-ruleset-4.txt +4 -0
  157. package/test/less-bom/errors/detached-ruleset-5.less +4 -0
  158. package/test/less-bom/errors/detached-ruleset-5.txt +3 -0
  159. package/test/less-bom/errors/detached-ruleset-6.less +5 -0
  160. package/test/less-bom/errors/detached-ruleset-6.txt +4 -0
  161. package/test/less-bom/errors/divide-mixed-units.less +3 -0
  162. package/test/less-bom/errors/divide-mixed-units.txt +4 -0
  163. package/test/less-bom/errors/extend-no-selector.less +3 -0
  164. package/test/less-bom/errors/extend-no-selector.txt +3 -0
  165. package/test/less-bom/errors/extend-not-at-end.less +3 -0
  166. package/test/less-bom/errors/extend-not-at-end.txt +3 -0
  167. package/test/less-bom/errors/import-malformed.less +1 -0
  168. package/test/less-bom/errors/import-malformed.txt +3 -0
  169. package/test/less-bom/errors/import-missing.less +6 -0
  170. package/test/less-bom/errors/import-missing.txt +3 -0
  171. package/test/less-bom/errors/import-no-semi.less +1 -0
  172. package/test/less-bom/errors/import-no-semi.txt +2 -0
  173. package/test/less-bom/errors/import-subfolder1.less +1 -0
  174. package/test/less-bom/errors/import-subfolder1.txt +3 -0
  175. package/test/less-bom/errors/import-subfolder2.less +1 -0
  176. package/test/less-bom/errors/import-subfolder2.txt +4 -0
  177. package/test/less-bom/errors/imports/import-subfolder1.less +1 -0
  178. package/test/less-bom/errors/imports/import-subfolder2.less +1 -0
  179. package/test/less-bom/errors/imports/import-test.less +4 -0
  180. package/test/less-bom/errors/imports/subfolder/mixin-not-defined.less +1 -0
  181. package/test/less-bom/errors/imports/subfolder/parse-error-curly-bracket.less +1 -0
  182. package/test/less-bom/errors/javascript-error.less +3 -0
  183. package/test/less-bom/errors/javascript-error.txt +4 -0
  184. package/test/less-bom/errors/javascript-undefined-var.less +3 -0
  185. package/test/less-bom/errors/javascript-undefined-var.txt +4 -0
  186. package/test/less-bom/errors/mixed-mixin-definition-args-1.less +6 -0
  187. package/test/less-bom/errors/mixed-mixin-definition-args-1.txt +4 -0
  188. package/test/less-bom/errors/mixed-mixin-definition-args-2.less +6 -0
  189. package/test/less-bom/errors/mixed-mixin-definition-args-2.txt +4 -0
  190. package/test/less-bom/errors/mixin-not-defined.less +11 -0
  191. package/test/less-bom/errors/mixin-not-defined.txt +3 -0
  192. package/test/less-bom/errors/mixin-not-matched.less +6 -0
  193. package/test/less-bom/errors/mixin-not-matched.txt +3 -0
  194. package/test/less-bom/errors/mixin-not-matched2.less +6 -0
  195. package/test/less-bom/errors/mixin-not-matched2.txt +3 -0
  196. package/test/less-bom/errors/mixin-not-visible-in-scope-1.less +9 -0
  197. package/test/less-bom/errors/mixin-not-visible-in-scope-1.txt +4 -0
  198. package/test/less-bom/errors/mixins-guards-default-func-1.less +9 -0
  199. package/test/less-bom/errors/mixins-guards-default-func-1.txt +4 -0
  200. package/test/less-bom/errors/mixins-guards-default-func-2.less +9 -0
  201. package/test/less-bom/errors/mixins-guards-default-func-2.txt +4 -0
  202. package/test/less-bom/errors/mixins-guards-default-func-3.less +9 -0
  203. package/test/less-bom/errors/mixins-guards-default-func-3.txt +4 -0
  204. package/test/less-bom/errors/multiple-guards-on-css-selectors.less +4 -0
  205. package/test/less-bom/errors/multiple-guards-on-css-selectors.txt +4 -0
  206. package/test/less-bom/errors/multiple-guards-on-css-selectors2.less +4 -0
  207. package/test/less-bom/errors/multiple-guards-on-css-selectors2.txt +4 -0
  208. package/test/less-bom/errors/multiply-mixed-units.less +7 -0
  209. package/test/less-bom/errors/multiply-mixed-units.txt +4 -0
  210. package/test/less-bom/errors/parens-error-1.less +3 -0
  211. package/test/less-bom/errors/parens-error-1.txt +4 -0
  212. package/test/less-bom/errors/parens-error-2.less +3 -0
  213. package/test/less-bom/errors/parens-error-2.txt +4 -0
  214. package/test/less-bom/errors/parens-error-3.less +3 -0
  215. package/test/less-bom/errors/parens-error-3.txt +4 -0
  216. package/test/less-bom/errors/parse-error-curly-bracket.less +4 -0
  217. package/test/less-bom/errors/parse-error-curly-bracket.txt +4 -0
  218. package/test/less-bom/errors/parse-error-extra-parens.less +5 -0
  219. package/test/less-bom/errors/parse-error-extra-parens.txt +3 -0
  220. package/test/less-bom/errors/parse-error-missing-bracket.less +2 -0
  221. package/test/less-bom/errors/parse-error-missing-bracket.txt +3 -0
  222. package/test/less-bom/errors/parse-error-missing-parens.less +5 -0
  223. package/test/less-bom/errors/parse-error-missing-parens.txt +3 -0
  224. package/test/less-bom/errors/parse-error-with-import.less +13 -0
  225. package/test/less-bom/errors/parse-error-with-import.txt +4 -0
  226. package/test/less-bom/errors/percentage-missing-space.less +3 -0
  227. package/test/less-bom/errors/percentage-missing-space.txt +4 -0
  228. package/test/less-bom/errors/property-asterisk-only-name.less +3 -0
  229. package/test/less-bom/errors/property-asterisk-only-name.txt +4 -0
  230. package/test/less-bom/errors/property-ie5-hack.less +3 -0
  231. package/test/less-bom/errors/property-ie5-hack.txt +3 -0
  232. package/test/less-bom/errors/property-in-root.less +4 -0
  233. package/test/less-bom/errors/property-in-root.txt +4 -0
  234. package/test/less-bom/errors/property-in-root2.less +1 -0
  235. package/test/less-bom/errors/property-in-root2.txt +4 -0
  236. package/test/less-bom/errors/property-in-root3.less +4 -0
  237. package/test/less-bom/errors/property-in-root3.txt +3 -0
  238. package/test/less-bom/errors/property-interp-not-defined.less +1 -0
  239. package/test/less-bom/errors/property-interp-not-defined.txt +2 -0
  240. package/test/less-bom/errors/recursive-variable.less +1 -0
  241. package/test/less-bom/errors/recursive-variable.txt +2 -0
  242. package/test/less-bom/errors/single-character.less +1 -0
  243. package/test/less-bom/errors/single-character.txt +2 -0
  244. package/test/less-bom/errors/svg-gradient1.less +3 -0
  245. package/test/less-bom/errors/svg-gradient1.txt +4 -0
  246. package/test/less-bom/errors/svg-gradient2.less +3 -0
  247. package/test/less-bom/errors/svg-gradient2.txt +4 -0
  248. package/test/less-bom/errors/svg-gradient3.less +3 -0
  249. package/test/less-bom/errors/svg-gradient3.txt +4 -0
  250. package/test/less-bom/errors/unit-function.less +3 -0
  251. package/test/less-bom/errors/unit-function.txt +4 -0
  252. package/test/less-bom/extend-chaining.less +91 -0
  253. package/test/less-bom/extend-clearfix.less +19 -0
  254. package/test/less-bom/extend-exact.less +46 -0
  255. package/test/less-bom/extend-media.less +24 -0
  256. package/test/less-bom/extend-nest.less +65 -0
  257. package/test/less-bom/extend-selector.less +99 -0
  258. package/test/less-bom/extend.less +81 -0
  259. package/test/less-bom/extract-and-length.less +133 -0
  260. package/test/less-bom/functions.less +201 -0
  261. package/test/less-bom/globalVars/extended.json +5 -0
  262. package/test/less-bom/globalVars/extended.less +10 -0
  263. package/test/less-bom/globalVars/simple.json +3 -0
  264. package/test/less-bom/globalVars/simple.less +3 -0
  265. package/test/less-bom/ie-filters.less +15 -0
  266. package/test/less-bom/import/deeper/deeper-2/url-import-2.less +3 -0
  267. package/test/less-bom/import/deeper/deeper-2/url-import.less +1 -0
  268. package/test/less-bom/import/deeper/import-once-test-a.less +1 -0
  269. package/test/less-bom/import/deeper/url-import.less +1 -0
  270. package/test/less-bom/import/import-and-relative-paths-test.less +17 -0
  271. package/test/less-bom/import/import-charset-test.less +1 -0
  272. package/test/less-bom/import/import-interpolation.less +2 -0
  273. package/test/less-bom/import/import-interpolation2.less +5 -0
  274. package/test/less-bom/import/import-once-test-c.less +6 -0
  275. package/test/less-bom/import/import-reference.less +89 -0
  276. package/test/less-bom/import/import-test-a.less +4 -0
  277. package/test/less-bom/import/import-test-b.less +8 -0
  278. package/test/less-bom/import/import-test-c.less +6 -0
  279. package/test/less-bom/import/import-test-d.css +1 -0
  280. package/test/less-bom/import/import-test-e.less +2 -0
  281. package/test/less-bom/import/import-test-f.less +5 -0
  282. package/test/less-bom/import/imports/font.less +8 -0
  283. package/test/less-bom/import/imports/logo.less +5 -0
  284. package/test/less-bom/import/interpolation-vars.less +6 -0
  285. package/test/less-bom/import/invalid-css.less +1 -0
  286. package/test/less-bom/import/urls.less +1 -0
  287. package/test/less-bom/import-inline.less +3 -0
  288. package/test/less-bom/import-interpolation.less +8 -0
  289. package/test/less-bom/import-once.less +6 -0
  290. package/test/less-bom/import-reference.less +23 -0
  291. package/test/less-bom/import.less +30 -0
  292. package/test/less-bom/include-path/include-path.less +6 -0
  293. package/test/less-bom/javascript.less +38 -0
  294. package/test/less-bom/lazy-eval.less +6 -0
  295. package/test/less-bom/legacy/legacy.less +7 -0
  296. package/test/less-bom/media.less +234 -0
  297. package/test/less-bom/merge.less +78 -0
  298. package/test/less-bom/mixins-args.less +215 -0
  299. package/test/less-bom/mixins-closure.less +26 -0
  300. package/test/less-bom/mixins-guards-default-func.less +195 -0
  301. package/test/less-bom/mixins-guards.less +271 -0
  302. package/test/less-bom/mixins-important.less +37 -0
  303. package/test/less-bom/mixins-interpolated.less +75 -0
  304. package/test/less-bom/mixins-named-args.less +36 -0
  305. package/test/less-bom/mixins-nested.less +22 -0
  306. package/test/less-bom/mixins-pattern.less +102 -0
  307. package/test/less-bom/mixins.less +145 -0
  308. package/test/less-bom/modifyVars/extended.json +5 -0
  309. package/test/less-bom/modifyVars/extended.less +11 -0
  310. package/test/less-bom/nested-gradient-with-svg-gradient/mixin-consumer.less +5 -0
  311. package/test/less-bom/nested-gradient-with-svg-gradient/svg-gradient-mixin.less +15 -0
  312. package/test/less-bom/no-js-errors/no-js-errors.less +3 -0
  313. package/test/less-bom/no-js-errors/no-js-errors.txt +4 -0
  314. package/test/less-bom/no-output.less +2 -0
  315. package/test/less-bom/operations.less +62 -0
  316. package/test/less-bom/parens.less +45 -0
  317. package/test/less-bom/property-name-interp.less +56 -0
  318. package/test/less-bom/rulesets.less +30 -0
  319. package/test/less-bom/scope.less +104 -0
  320. package/test/less-bom/selectors.less +174 -0
  321. package/test/less-bom/sourcemaps/basic.json +3 -0
  322. package/test/less-bom/sourcemaps/basic.less +27 -0
  323. package/test/less-bom/sourcemaps/imported.css +7 -0
  324. package/test/less-bom/static-urls/urls.less +33 -0
  325. package/test/less-bom/strict-units/strict-units.less +4 -0
  326. package/test/less-bom/strings.less +73 -0
  327. package/test/less-bom/url-args/urls.less +63 -0
  328. package/test/less-bom/urls.less +94 -0
  329. package/test/less-bom/variables-in-at-rules.less +20 -0
  330. package/test/less-bom/variables.less +119 -0
  331. package/test/less-bom/whitespace.less +44 -0
  332. package/test/less-test.js +127 -28
  333. package/test/modify-vars.js +6 -4
@@ -1,5 +1,6 @@
1
1
  var tree = require("../tree"),
2
- Visitor = require("./visitor");
2
+ Visitor = require("./visitor"),
3
+ logger = require("../logger");
3
4
 
4
5
  /*jshint loopfunc:true */
5
6
 
@@ -60,7 +61,7 @@ ExtendFinderVisitor.prototype = {
60
61
  extend.findSelfSelectors(selectorPath);
61
62
  extend.ruleset = rulesetNode;
62
63
  if (j === 0) { extend.firstExtendOnThisSelectorPath = true; }
63
- this.allExtendsStack[this.allExtendsStack.length-1].push(extend);
64
+ this.allExtendsStack[this.allExtendsStack.length - 1].push(extend);
64
65
  }
65
66
  }
66
67
 
@@ -94,23 +95,43 @@ var ProcessExtendsVisitor = function() {
94
95
  ProcessExtendsVisitor.prototype = {
95
96
  run: function(root) {
96
97
  var extendFinder = new ExtendFinderVisitor();
98
+ this.extendIndicies = {};
97
99
  extendFinder.run(root);
98
100
  if (!extendFinder.foundExtends) { return root; }
99
101
  root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends));
100
102
  this.allExtendsStack = [root.allExtends];
101
- return this._visitor.visit(root);
103
+ var newRoot = this._visitor.visit(root);
104
+ this.checkExtendsForNonMatched(root.allExtends);
105
+ return newRoot;
106
+ },
107
+ checkExtendsForNonMatched: function(extendList) {
108
+ var indicies = this.extendIndicies;
109
+ extendList.filter(function(extend) {
110
+ return !extend.hasFoundMatches && extend.parent_ids.length == 1;
111
+ }).forEach(function(extend) {
112
+ var selector = "_unknown_";
113
+ try {
114
+ selector = extend.selector.toCSS({});
115
+ }catch(_){}
116
+
117
+ if(!indicies[extend.index + ' ' + selector]) {
118
+ indicies[extend.index + ' ' + selector] = true;
119
+ logger.warn("extend '" + selector + "' has no matches");
120
+ }
121
+ });
102
122
  },
103
123
  doExtendChaining: function (extendsList, extendsListTarget, iterationCount) {
104
124
  //
105
- // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting
106
- // the selector we would do normally, but we are also adding an extend with the same target selector
125
+ // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering
126
+ // and pasting the selector we would do normally, but we are also adding an extend with the same target selector
107
127
  // this means this new extend can then go and alter other extends
108
128
  //
109
129
  // this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors
110
- // this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already processed if
111
- // we look at each selector at a time, as is done in visitRuleset
130
+ // this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already
131
+ // processed if we look at each selector at a time, as is done in visitRuleset
112
132
 
113
- var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend;
133
+ var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath,
134
+ extend, targetExtend, newExtend;
114
135
 
115
136
  iterationCount = iterationCount || 0;
116
137
 
@@ -135,6 +156,8 @@ ProcessExtendsVisitor.prototype = {
135
156
 
136
157
  if (matches.length) {
137
158
 
159
+ extend.hasFoundMatches = true;
160
+
138
161
  // we found a match, so for each self selector..
139
162
  extend.selfSelectors.forEach(function(selfSelector) {
140
163
 
@@ -146,7 +169,7 @@ ProcessExtendsVisitor.prototype = {
146
169
  newExtend.selfSelectors = newSelector;
147
170
 
148
171
  // add the extend onto the list of extends for that selector
149
- newSelector[newSelector.length-1].extendList = [newExtend];
172
+ newSelector[newSelector.length - 1].extendList = [newExtend];
150
173
 
151
174
  // record that we need to add it.
152
175
  extendsToAdd.push(newExtend);
@@ -180,11 +203,13 @@ ProcessExtendsVisitor.prototype = {
180
203
  selectorTwo = extendsToAdd[0].selector.toCSS();
181
204
  }
182
205
  catch(e) {}
183
- throw {message: "extend circular reference detected. One of the circular extends is currently:"+selectorOne+":extend(" + selectorTwo+")"};
206
+ throw { message: "extend circular reference detected. One of the circular extends is currently:" +
207
+ selectorOne + ":extend(" + selectorTwo + ")"};
184
208
  }
185
209
 
186
- // now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e...
187
- return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1));
210
+ // now process the new extends on the existing rules so that we can handle a extending b extending c extending
211
+ // d extending e...
212
+ return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount + 1));
188
213
  } else {
189
214
  return extendsToAdd;
190
215
  }
@@ -202,7 +227,8 @@ ProcessExtendsVisitor.prototype = {
202
227
  if (rulesetNode.root) {
203
228
  return;
204
229
  }
205
- var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath;
230
+ var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length - 1],
231
+ selectorsToAdd = [], extendVisitor = this, selectorPath;
206
232
 
207
233
  // look at each selector path in the ruleset, find any extend matches and then copy, find and replace
208
234
 
@@ -212,12 +238,13 @@ ProcessExtendsVisitor.prototype = {
212
238
 
213
239
  // extending extends happens initially, before the main pass
214
240
  if (rulesetNode.extendOnEveryPath) { continue; }
215
- var extendList = selectorPath[selectorPath.length-1].extendList;
241
+ var extendList = selectorPath[selectorPath.length - 1].extendList;
216
242
  if (extendList && extendList.length) { continue; }
217
243
 
218
244
  matches = this.findMatch(allExtends[extendIndex], selectorPath);
219
245
 
220
246
  if (matches.length) {
247
+ allExtends[extendIndex].hasFoundMatches = true;
221
248
 
222
249
  allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) {
223
250
  selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector));
@@ -248,15 +275,16 @@ ProcessExtendsVisitor.prototype = {
248
275
 
249
276
  // if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
250
277
  if (extend.allowBefore || (haystackSelectorIndex === 0 && hackstackElementIndex === 0)) {
251
- potentialMatches.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator});
278
+ potentialMatches.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0,
279
+ initialCombinator: haystackElement.combinator});
252
280
  }
253
281
 
254
282
  for(i = 0; i < potentialMatches.length; i++) {
255
283
  potentialMatch = potentialMatches[i];
256
284
 
257
285
  // selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
258
- // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
259
- // what the resulting combinator will be
286
+ // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to
287
+ // work out what the resulting combinator will be
260
288
  targetCombinator = haystackElement.combinator.value;
261
289
  if (targetCombinator === '' && hackstackElementIndex === 0) {
262
290
  targetCombinator = ' ';
@@ -274,7 +302,8 @@ ProcessExtendsVisitor.prototype = {
274
302
  if (potentialMatch) {
275
303
  potentialMatch.finished = potentialMatch.matched === needleElements.length;
276
304
  if (potentialMatch.finished &&
277
- (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) {
305
+ (!extend.allowAfter &&
306
+ (hackstackElementIndex + 1 < hackstackSelector.elements.length || haystackSelectorIndex + 1 < haystackSelectorPath.length))) {
278
307
  potentialMatch = null;
279
308
  }
280
309
  }
@@ -320,7 +349,7 @@ ProcessExtendsVisitor.prototype = {
320
349
  if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) {
321
350
  return false;
322
351
  }
323
- for(var i = 0; i <elementValue1.elements.length; i++) {
352
+ for(var i = 0; i < elementValue1.elements.length; i++) {
324
353
  if (elementValue1.elements[i].combinator.value !== elementValue2.elements[i].combinator.value) {
325
354
  if (i !== 0 || (elementValue1.elements[i].combinator.value || ' ') !== (elementValue2.elements[i].combinator.value || ' ')) {
326
355
  return false;
@@ -358,7 +387,8 @@ ProcessExtendsVisitor.prototype = {
358
387
  );
359
388
 
360
389
  if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) {
361
- path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
390
+ path[path.length - 1].elements = path[path.length - 1]
391
+ .elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
362
392
  currentSelectorPathElementIndex = 0;
363
393
  currentSelectorPathIndex++;
364
394
  }
@@ -387,7 +417,8 @@ ProcessExtendsVisitor.prototype = {
387
417
  }
388
418
 
389
419
  if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) {
390
- path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
420
+ path[path.length - 1].elements = path[path.length - 1]
421
+ .elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
391
422
  currentSelectorPathIndex++;
392
423
  }
393
424
 
@@ -398,20 +429,24 @@ ProcessExtendsVisitor.prototype = {
398
429
  visitRulesetOut: function (rulesetNode) {
399
430
  },
400
431
  visitMedia: function (mediaNode, visitArgs) {
401
- var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);
432
+ var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length - 1]);
402
433
  newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends));
403
434
  this.allExtendsStack.push(newAllExtends);
404
435
  },
405
436
  visitMediaOut: function (mediaNode) {
406
- this.allExtendsStack.length = this.allExtendsStack.length - 1;
437
+ var lastIndex = this.allExtendsStack.length - 1;
438
+ this.checkExtendsForNonMatched(this.allExtendsStack[lastIndex]);
439
+ this.allExtendsStack.length = lastIndex;
407
440
  },
408
441
  visitDirective: function (directiveNode, visitArgs) {
409
- var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);
442
+ var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length - 1]);
410
443
  newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends));
411
444
  this.allExtendsStack.push(newAllExtends);
412
445
  },
413
446
  visitDirectiveOut: function (directiveNode) {
414
- this.allExtendsStack.length = this.allExtendsStack.length - 1;
447
+ var lastIndex = this.allExtendsStack.length - 1;
448
+ this.checkExtendsForNonMatched(this.allExtendsStack[lastIndex]);
449
+ this.allExtendsStack.length = lastIndex;
415
450
  }
416
451
  };
417
452
 
@@ -2,6 +2,7 @@ function ImportSequencer(onSequencerEmpty) {
2
2
  this.imports = [];
3
3
  this.variableImports = [];
4
4
  this._onSequencerEmpty = onSequencerEmpty;
5
+ this._currentDepth = 0;
5
6
  }
6
7
 
7
8
  ImportSequencer.prototype.addImport = function(callback) {
@@ -24,23 +25,28 @@ ImportSequencer.prototype.addVariableImport = function(callback) {
24
25
  };
25
26
 
26
27
  ImportSequencer.prototype.tryRun = function() {
27
- while(true) {
28
- while(this.imports.length > 0) {
29
- var importItem = this.imports[0];
30
- if (!importItem.isReady) {
31
- return;
28
+ this._currentDepth++;
29
+ try {
30
+ while(true) {
31
+ while(this.imports.length > 0) {
32
+ var importItem = this.imports[0];
33
+ if (!importItem.isReady) {
34
+ return;
35
+ }
36
+ this.imports = this.imports.slice(1);
37
+ importItem.callback.apply(null, importItem.args);
32
38
  }
33
- this.imports = this.imports.slice(1);
34
- importItem.callback.apply(null, importItem.args);
35
- }
36
- if (this.variableImports.length === 0) {
37
- break;
39
+ if (this.variableImports.length === 0) {
40
+ break;
41
+ }
42
+ var variableImport = this.variableImports[0];
43
+ this.variableImports = this.variableImports.slice(1);
44
+ variableImport();
38
45
  }
39
- var variableImport = this.variableImports[0];
40
- this.variableImports = this.variableImports.slice(1);
41
- variableImport();
46
+ } finally {
47
+ this._currentDepth--;
42
48
  }
43
- if (this._onSequencerEmpty) {
49
+ if (this._currentDepth === 0 && this._onSequencerEmpty) {
44
50
  this._onSequencerEmpty();
45
51
  }
46
52
  };
@@ -11,26 +11,28 @@ var ImportVisitor = function(importer, finish) {
11
11
  this.importCount = 0;
12
12
  this.onceFileDetectionMap = {};
13
13
  this.recursionDetector = {};
14
- this._sequencer = new ImportSequencer();
14
+ this._sequencer = new ImportSequencer(this._onSequencerEmpty.bind(this));
15
15
  };
16
16
 
17
17
  ImportVisitor.prototype = {
18
18
  isReplacing: false,
19
19
  run: function (root) {
20
- var error;
21
20
  try {
22
21
  // process the contents
23
22
  this._visitor.visit(root);
24
23
  }
25
24
  catch(e) {
26
- error = e;
25
+ this.error = e;
27
26
  }
28
27
 
29
28
  this.isFinished = true;
30
29
  this._sequencer.tryRun();
31
- if (this.importCount === 0) {
32
- this._finish(error || this.error);
30
+ },
31
+ _onSequencerEmpty: function() {
32
+ if (!this.isFinished) {
33
+ return;
33
34
  }
35
+ this._finish(this.error);
34
36
  },
35
37
  visitImport: function (importNode, visitArgs) {
36
38
  var inlineCSS = importNode.options.inline;
@@ -82,9 +84,13 @@ ImportVisitor.prototype = {
82
84
  var onImported = this.onImported.bind(this, evaldImportNode, context),
83
85
  sequencedOnImported = this._sequencer.addImport(onImported);
84
86
 
85
- this._importer.push(evaldImportNode.getPath(), tryAppendLessExtension, evaldImportNode.currentFileInfo, evaldImportNode.options, sequencedOnImported);
87
+ this._importer.push(evaldImportNode.getPath(), tryAppendLessExtension, evaldImportNode.currentFileInfo,
88
+ evaldImportNode.options, sequencedOnImported);
86
89
  } else {
87
90
  this.importCount--;
91
+ if (this.isFinished) {
92
+ this._sequencer.tryRun();
93
+ }
88
94
  }
89
95
  },
90
96
  onImported: function (importNode, context, e, root, importedAtRoot, fullPath) {
@@ -134,10 +140,7 @@ ImportVisitor.prototype = {
134
140
  importVisitor.importCount--;
135
141
 
136
142
  if (importVisitor.isFinished) {
137
- this._sequencer.tryRun();
138
- if (importVisitor.importCount === 0) {
139
- importVisitor._finish(importVisitor.error);
140
- }
143
+ importVisitor._sequencer.tryRun();
141
144
  }
142
145
  },
143
146
  visitRule: function (ruleNode, visitArgs) {
@@ -46,16 +46,16 @@ ToCSSVisitor.prototype = {
46
46
  },
47
47
 
48
48
  visitDirective: function(directiveNode, visitArgs) {
49
- if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) {
50
- return;
51
- }
52
49
  if (directiveNode.name === "@charset") {
50
+ if (!directiveNode.getIsReferenced()) {
51
+ return;
52
+ }
53
53
  // Only output the debug info together with subsequent @charset definitions
54
54
  // a comment (or @media statement) before the actual @charset directive would
55
55
  // be considered illegal css as it has to be on the first line
56
56
  if (this.charset) {
57
57
  if (directiveNode.debugInfo) {
58
- var comment = new tree.Comment("/* " + directiveNode.toCSS(this._context).replace(/\n/g, "")+" */\n");
58
+ var comment = new tree.Comment("/* " + directiveNode.toCSS(this._context).replace(/\n/g, "") + " */\n");
59
59
  comment.debugInfo = directiveNode.debugInfo;
60
60
  return this._visitor.visit(comment);
61
61
  }
@@ -65,6 +65,38 @@ ToCSSVisitor.prototype = {
65
65
  }
66
66
  if (directiveNode.rules && directiveNode.rules.rules) {
67
67
  this._mergeRules(directiveNode.rules.rules);
68
+ //process childs
69
+ directiveNode.accept(this._visitor);
70
+ visitArgs.visitDeeper = false;
71
+
72
+ // the directive was directly referenced and therefore needs to be shown in the output
73
+ if (directiveNode.getIsReferenced()) {
74
+ return directiveNode;
75
+ }
76
+
77
+ if (!directiveNode.rules.rules) {
78
+ return ;
79
+ }
80
+
81
+ //the directive was not directly referenced
82
+ for (var r = 0; r < directiveNode.rules.rules.length; r++) {
83
+ var rule = directiveNode.rules.rules[r];
84
+ if (rule.getIsReferenced && rule.getIsReferenced()) {
85
+ //the directive contains something that was referenced (likely by extend)
86
+ //therefore it needs to be shown in output too
87
+
88
+ //marking as referenced in case the directive is stored inside another directive
89
+ directiveNode.markReferenced();
90
+ return directiveNode;
91
+ }
92
+ }
93
+ //The directive was not directly referenced and does not contain anything that
94
+ //was referenced. Therefore it must not be shown in output.
95
+ return ;
96
+ } else {
97
+ if (!directiveNode.getIsReferenced()) {
98
+ return;
99
+ }
68
100
  }
69
101
  return directiveNode;
70
102
  },
@@ -225,7 +257,7 @@ ToCSSVisitor.prototype = {
225
257
  var spacedGroups = [];
226
258
  var lastSpacedGroup = [];
227
259
  parts.map(function (p) {
228
- if (p.merge==="+") {
260
+ if (p.merge === "+") {
229
261
  if (lastSpacedGroup.length > 0) {
230
262
  spacedGroups.push(toExpression(lastSpacedGroup));
231
263
  }
@@ -7,7 +7,7 @@ module.exports = function(window, options) {
7
7
  addDataAttr(options, browser.currentScript(window));
8
8
 
9
9
  if (options.isFileProtocol === undefined) {
10
- options.isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(window.location.protocol);
10
+ options.isFileProtocol = /^(file|(chrome|safari)(-extension)?|resource|qrc|app):/.test(window.location.protocol);
11
11
  }
12
12
 
13
13
  // Load styles asynchronously (default: false)
@@ -39,4 +39,8 @@ module.exports = function(window, options) {
39
39
  options.useFileCache = true;
40
40
  }
41
41
 
42
+ if (options.onReady === undefined) {
43
+ options.onReady = true;
44
+ }
45
+
42
46
  };
@@ -13,12 +13,14 @@ require("./add-default-options")(window, options);
13
13
 
14
14
  var less = module.exports = require("./index")(window, options);
15
15
 
16
- if (/!watch/.test(window.location.hash)) {
17
- less.watch();
18
- }
19
-
20
- less.pageLoadFinished = less.registerStylesheets().then(
21
- function () {
22
- return less.refresh(less.env === 'development');
16
+ if (options.onReady) {
17
+ if (/!watch/.test(window.location.hash)) {
18
+ less.watch();
23
19
  }
24
- );
20
+
21
+ less.pageLoadFinished = less.registerStylesheets().then(
22
+ function () {
23
+ return less.refresh(less.env === 'development');
24
+ }
25
+ );
26
+ }
@@ -16,7 +16,7 @@ module.exports = function(window, options, logger) {
16
16
  cache.setItem(path + ':timestamp', lastModified);
17
17
  } catch(e) {
18
18
  //TODO - could do with adding more robust error handling
19
- logger.error('failed to save');
19
+ logger.error('failed to save "' + path + '" to local storage for caching.');
20
20
  }
21
21
  }
22
22
  },
@@ -112,12 +112,13 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
112
112
  if (webInfo) {
113
113
  webInfo.remaining = remaining;
114
114
 
115
- var css = cache.getCSS(path, webInfo);
116
- if (!reload && css) {
117
- browser.createCSS(window.document, css, sheet);
118
- webInfo.local = true;
119
- callback(null, null, data, sheet, webInfo, path);
120
- return;
115
+ if (!instanceOptions.modifyVars) {
116
+ var css = cache.getCSS(path, webInfo);
117
+ if (!reload && css) {
118
+ webInfo.local = true;
119
+ callback(null, css, data, sheet, webInfo, path);
120
+ return;
121
+ }
121
122
  }
122
123
  }
123
124
 
@@ -130,6 +131,10 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
130
131
  e.href = path;
131
132
  callback(e);
132
133
  } else {
134
+ result.css = postProcessCSS(result.css);
135
+ if (!instanceOptions.modifyVars) {
136
+ cache.setCSS(sheet.href, webInfo.lastModified, result.css);
137
+ }
133
138
  callback(null, result.css, data, sheet, webInfo, path);
134
139
  }
135
140
  });
@@ -155,13 +160,11 @@ function initRunningMode(){
155
160
  less.watchTimer = setInterval(function () {
156
161
  if (less.watchMode) {
157
162
  fileManager.clearFileCache();
158
- loadStyleSheets(function (e, css, _, sheet, context) {
163
+ loadStyleSheets(function (e, css, _, sheet, webInfo) {
159
164
  if (e) {
160
165
  errors.add(e, e.href || sheet.href);
161
166
  } else if (css) {
162
- css = postProcessCSS(css);
163
167
  browser.createCSS(window.document, css, sheet);
164
- cache.setCSS(sheet.href, context.lastModified, css);
165
168
  }
166
169
  });
167
170
  }
@@ -228,10 +231,8 @@ less.refresh = function (reload, modifyVars, clearFileCache) {
228
231
  less.logger.info("loading " + sheet.href + " from cache.");
229
232
  } else {
230
233
  less.logger.info("rendered " + sheet.href + " successfully.");
231
- css = postProcessCSS(css);
232
- browser.createCSS(window.document, css, sheet);
233
- cache.setCSS(sheet.href, webInfo.lastModified, css);
234
234
  }
235
+ browser.createCSS(window.document, css, sheet);
235
236
  less.logger.info("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms');
236
237
  if (webInfo.remaining === 0) {
237
238
  totalMilliseconds = new Date() - startTime;
@@ -1,11 +1,11 @@
1
1
  module.exports = {
2
2
  extractId: function(href) {
3
3
  return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain
4
- .replace(/[\?\&]livereload=\w+/,'' ) // Remove LiveReload cachebuster
5
- .replace(/^\//, '' ) // Remove root /
6
- .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension
7
- .replace(/[^\.\w-]+/g, '-') // Replace illegal characters
8
- .replace(/\./g, ':'); // Replace dots with colons(for valid id)
4
+ .replace(/[\?\&]livereload=\w+/, '' ) // Remove LiveReload cachebuster
5
+ .replace(/^\//, '' ) // Remove root /
6
+ .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension
7
+ .replace(/[^\.\w-]+/g, '-') // Replace illegal characters
8
+ .replace(/\./g, ':'); // Replace dots with colons(for valid id)
9
9
  },
10
10
  addDataAttr: function(options, tag) {
11
11
  for (var opt in tag.dataset) {
@@ -13,7 +13,10 @@ module.exports = {
13
13
  if (opt === "env" || opt === "dumpLineNumbers" || opt === "rootpath" || opt === "errorReporting") {
14
14
  options[opt] = tag.dataset[opt];
15
15
  } else {
16
- options[opt] = JSON.parse(tag.dataset[opt]);
16
+ try {
17
+ options[opt] = JSON.parse(tag.dataset[opt]);
18
+ }
19
+ catch(_) {}
17
20
  }
18
21
  }
19
22
  }
@@ -17,45 +17,35 @@ FileManager.prototype.supportsSync = function(filename, currentDirectory, option
17
17
 
18
18
  FileManager.prototype.loadFile = function(filename, currentDirectory, options, environment, callback) {
19
19
  var fullFilename,
20
- data;
20
+ data,
21
+ isAbsoluteFilename = this.isPathAbsolute(filename),
22
+ filenamesTried = [];
21
23
 
22
24
  options = options || {};
23
25
 
24
- var paths = [currentDirectory];
25
- if (options.paths) paths.push.apply(paths, options.paths);
26
- if (paths.indexOf('.') === -1) paths.push('.');
27
-
28
26
  if (options.syncImport) {
29
- var err, result;
30
- for (var i = 0; i < paths.length; i++) {
31
- try {
32
- fullFilename = path.join(paths[i], filename);
33
- fs.statSync(fullFilename);
34
- break;
35
- } catch (e) {
36
- fullFilename = null;
37
- }
38
- }
39
-
40
- if (!fullFilename) {
41
- err = { type: 'File', message: "'" + filename + "' wasn't found" };
42
- } else {
43
- data = fs.readFileSync(fullFilename, 'utf-8');
44
- result = { contents: data, filename: fullFilename};
45
- }
46
- callback(err, result);
27
+ data = this.loadFileSync(filename, currentDirectory, options, environment, 'utf-8');
28
+ callback(data.error, data);
47
29
  return;
48
30
  }
49
31
 
32
+ var paths = isAbsoluteFilename ? [""] : [currentDirectory];
33
+ if (options.paths) { paths.push.apply(paths, options.paths); }
34
+ if (!isAbsoluteFilename && paths.indexOf('.') === -1) { paths.push('.'); }
35
+
50
36
  // promise is guarenteed to be asyncronous
51
37
  // which helps as it allows the file handle
52
38
  // to be closed before it continues with the next file
53
39
  return new PromiseConstructor(function(fulfill, reject) {
54
40
  (function tryPathIndex(i) {
55
41
  if (i < paths.length) {
56
- fullFilename = path.join(paths[i], filename);
42
+ fullFilename = filename;
43
+ if (paths[i]) {
44
+ fullFilename = path.join(paths[i], fullFilename);
45
+ }
57
46
  fs.stat(fullFilename, function (err) {
58
47
  if (err) {
48
+ filenamesTried.push(fullFilename);
59
49
  tryPathIndex(i + 1);
60
50
  } else {
61
51
  fs.readFile(fullFilename, 'utf-8', function(e, data) {
@@ -66,15 +56,48 @@ FileManager.prototype.loadFile = function(filename, currentDirectory, options, e
66
56
  }
67
57
  });
68
58
  } else {
69
- reject({ type: 'File', message: "'" + filename + "' wasn't found" });
59
+ reject({ type: 'File', message: "'" + filename + "' wasn't found. Tried - " + filenamesTried.join(",") });
70
60
  }
71
61
  }(0));
72
62
  });
73
63
  };
74
64
 
75
- FileManager.prototype.loadFileSync = function(filename, currentDirectory, options, environment) {
76
- filename = path.join(currentDirectory, filename);
77
- return { contents: fs.readFileSync(filename), filename: filename };
65
+ FileManager.prototype.loadFileSync = function(filename, currentDirectory, options, environment, encoding) {
66
+ var fullFilename, paths, filenamesTried = [], isAbsoluteFilename = this.isPathAbsolute(filename) , data;
67
+ options = options || {};
68
+
69
+ paths = isAbsoluteFilename ? [""] : [currentDirectory];
70
+ if (options.paths) {
71
+ paths.push.apply(paths, options.paths);
72
+ }
73
+ if (!isAbsoluteFilename && paths.indexOf('.') === -1) {
74
+ paths.push('.');
75
+ }
76
+
77
+ var err, result;
78
+ for (var i = 0; i < paths.length; i++) {
79
+ try {
80
+ fullFilename = filename;
81
+ if (paths[i]) {
82
+ fullFilename = path.join(paths[i], fullFilename);
83
+ }
84
+ filenamesTried.push(fullFilename);
85
+ fs.statSync(fullFilename);
86
+ break;
87
+ } catch (e) {
88
+ fullFilename = null;
89
+ }
90
+ }
91
+
92
+ if (!fullFilename) {
93
+ err = { type: 'File', message: "'" + filename + "' wasn't found. Tried - " + filenamesTried.join(",") };
94
+ result = { error: err };
95
+ } else {
96
+ data = fs.readFileSync(fullFilename, encoding);
97
+ result = { contents: data, filename: fullFilename};
98
+ }
99
+
100
+ return result;
78
101
  };
79
102
 
80
103
  module.exports = FileManager;