├── .gitignore ├── .npmignore ├── .travis.yml ├── README.md ├── examples ├── gulpfile.js └── normal.less ├── index.js ├── package.json └── test ├── expect └── buttons.css ├── fixtures ├── buttons.less ├── errors.less ├── forms.less ├── mixins.less ├── normalize.less └── variables.less └── main.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build 3 | lib-cov 4 | *.seed 5 | *.log 6 | *.csv 7 | *.dat 8 | *.out 9 | *.pid 10 | *.gz 11 | 12 | pids 13 | logs 14 | results 15 | 16 | npm-debug.log 17 | node_modules 18 | package-lock.json 19 | *.sublime* 20 | .travis.yml 21 | test 22 | examples 23 | .idea 24 | yarn.lock 25 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build 3 | lib-cov 4 | *.seed 5 | *.log 6 | *.csv 7 | *.dat 8 | *.out 9 | *.pid 10 | *.gz 11 | 12 | pids 13 | logs 14 | results 15 | 16 | npm-debug.log 17 | node_modules 18 | *.sublime* 19 | .travis.yml 20 | test 21 | examples 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | node_js: 5 | - "6" 6 | - "7" 7 | - "8" 8 | - "10" 9 | - "12" 10 | - "14" 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gulp-less 2 | 3 | 4 | > A [LESS](http://lesscss.org/) plugin for Gulp 5 | 6 | [![NPM Version](https://img.shields.io/npm/v/gulp-less.svg)](https://www.npmjs.com/package/gulp-less) 7 | [![Build Status](https://img.shields.io/travis/gulp-community/gulp-less.svg)](https://travis-ci.org/gulp-community/gulp-less) 8 | 9 | ## Information 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
Packagegulp-less
DescriptionLess plugin for gulp
Node Version>= 6
Less Version3.7+ | 4.0+
Gulp Version3.x
32 | 33 | ## Installation 34 | 35 | ``` 36 | npm install gulp-less 37 | ``` 38 | 39 | ## Basic Usage 40 | 41 | ```js 42 | var less = require('gulp-less'); 43 | var path = require('path'); 44 | 45 | gulp.task('less', function () { 46 | return gulp.src('./less/**/*.less') 47 | .pipe(less({ 48 | paths: [ path.join(__dirname, 'less', 'includes') ] 49 | })) 50 | .pipe(gulp.dest('./public/css')); 51 | }); 52 | ``` 53 | 54 | ## Options 55 | 56 | The options you can use [can be found here](http://lesscss.org/#using-less-configuration). Below is a list of valid options as of the time of writing: 57 | 58 | - `paths`: Array of paths to be used for `@import` directives 59 | - `plugins`: Array of less plugins ([details](#using-plugins)) 60 | 61 | The `filename` option is not necessary, it's handled automatically by this plugin. The `compress` option is not supported -- if you are trying to minify your css, use a css minifier. No `sourceMap` options are supported -- if you are trying to generate sourcemaps, use [gulp-sourcemaps](https://github.com/floridoo/gulp-sourcemaps). 62 | 63 | ## Using Plugins 64 | 65 | Less now supports plugins, which can add additional functionality. Here's an example of how to use a plugin with `gulp-less`. 66 | 67 | ```js 68 | var LessAutoprefix = require('less-plugin-autoprefix'); 69 | var autoprefix = new LessAutoprefix({ browsers: ['last 2 versions'] }); 70 | 71 | return gulp.src('./less/**/*.less') 72 | .pipe(less({ 73 | plugins: [autoprefix] 74 | })) 75 | .pipe(gulp.dest('./public/css')); 76 | ``` 77 | 78 | More info on LESS plugins can be found at http://lesscss.org/usage/#plugins, including a current list of all available plugins. 79 | 80 | ## Source Maps 81 | 82 | `gulp-less` can be used in tandem with [gulp-sourcemaps](https://github.com/floridoo/gulp-sourcemaps) to generate source maps for your files. You will need to initialize [gulp-sourcemaps](https://github.com/floridoo/gulp-sourcemaps) prior to running the gulp-less compiler and write the source maps after, as such: 83 | 84 | ```js 85 | var sourcemaps = require('gulp-sourcemaps'); 86 | 87 | gulp.src('./less/**/*.less') 88 | .pipe(sourcemaps.init()) 89 | .pipe(less()) 90 | .pipe(sourcemaps.write()) 91 | .pipe(gulp.dest('./public/css')); 92 | ``` 93 | 94 | By default, [gulp-sourcemaps](https://github.com/floridoo/gulp-sourcemaps) writes the source maps inline in the compiled CSS files. To write them to a separate file, specify a relative file path in the `sourcemaps.write()` function, as such: 95 | 96 | ```js 97 | var sourcemaps = require('gulp-sourcemaps'); 98 | 99 | gulp.src('./less/**/*.less') 100 | .pipe(sourcemaps.init()) 101 | .pipe(less()) 102 | .pipe(sourcemaps.write('./maps')) 103 | .pipe(gulp.dest('./public/css')); 104 | ``` 105 | 106 | ## Error Handling 107 | 108 | By default, a gulp task will fail and all streams will halt when an error happens. To change this behavior check out the error handling documentation [here](https://github.com/gulpjs/gulp/blob/master/docs/recipes/combining-streams-to-handle-errors.md) 109 | 110 | ## License 111 | 112 | (MIT License) 113 | 114 | Copyright (c) 2015 Plus 3 Network dev@plus3network.com 115 | 116 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 117 | 118 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 119 | 120 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 121 | -------------------------------------------------------------------------------- /examples/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var less = require('../'); 3 | 4 | gulp.task('less', function(){ 5 | gulp.src('./normal.less') 6 | .pipe(less()) 7 | .pipe(gulp.dest('build')); 8 | }); 9 | 10 | gulp.task('default', ['less']); 11 | -------------------------------------------------------------------------------- /examples/normal.less: -------------------------------------------------------------------------------- 1 | @base: #f938ab; 2 | 3 | .box-shadow(@style, @c) when (iscolor(@c)) { 4 | -webkit-box-shadow: @style @c; 5 | box-shadow: @style @c; 6 | } 7 | .box-shadow(@style, @alpha: 50%) when (isnumber(@alpha)) { 8 | .box-shadow(@style, rgba(0, 0, 0, @alpha)); 9 | } 10 | .box { 11 | color: saturate(@base, 5%); 12 | border-color: lighten(@base, 30%); 13 | div { .box-shadow(0 0 5px, 30%) } 14 | } 15 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var less = require('less'); 4 | var through2 = require('through2'); 5 | var replaceExt = require('replace-ext'); 6 | var assign = require('object-assign'); 7 | var applySourceMap = require('vinyl-sourcemaps-apply'); 8 | var PluginError = require('plugin-error'); 9 | 10 | function inlineSources(map) { 11 | if (map.sourcesContent) { 12 | return Promise.resolve(map); 13 | } 14 | 15 | return Promise.all( 16 | map.sources.map(function (source) { 17 | return new Promise(function (resolve, reject) { 18 | fs.readFile(source, 'utf8', function (err, data) { 19 | if (err) { 20 | reject(err); 21 | } else { 22 | resolve(data); 23 | } 24 | }); 25 | }); 26 | }) 27 | ).then( 28 | function (contents) { 29 | map.sourcesContent = contents; 30 | return map; 31 | }, 32 | function () { 33 | return map; 34 | } 35 | ); 36 | } 37 | 38 | function renderLess(str, opts) { 39 | return new Promise(function (resolve, reject) { 40 | less.render(str, opts, function (err, res) { 41 | if (err) { 42 | reject(err); 43 | } else { 44 | var obj = { 45 | result: res.css, 46 | imports: res.imports, 47 | }; 48 | if (opts.sourceMap && res.map) { 49 | obj.sourcemap = JSON.parse(res.map); 50 | inlineSources(obj.sourcemap).then(function (map) { 51 | obj.sourcemap = map; 52 | resolve(obj); 53 | }); 54 | } else { 55 | resolve(obj); 56 | } 57 | } 58 | }); 59 | }); 60 | } 61 | 62 | module.exports = function (options) { 63 | // Mixes in default options. 64 | var opts = assign({}, { 65 | compress: false, 66 | paths: [] 67 | }, options); 68 | 69 | return through2.obj(function (file, enc, cb) { 70 | if (file.isNull()) { 71 | return cb(null, file); 72 | } 73 | 74 | if (file.isStream()) { 75 | return cb(new PluginError('gulp-less', 'Streaming not supported')); 76 | } 77 | 78 | var str = file.contents.toString(); 79 | 80 | // Injects the path of the current file 81 | opts.filename = file.path; 82 | 83 | // Bootstrap source maps 84 | if (file.sourceMap || opts.sourcemap) { 85 | opts.sourceMap = true; 86 | } 87 | 88 | renderLess(str, opts).then(function(res) { 89 | file.contents = Buffer.from(res.result); 90 | file.path = replaceExt(file.path, '.css'); 91 | if (res.sourcemap) { 92 | res.sourcemap.file = file.relative; 93 | res.sourcemap.sources = res.sourcemap.sources.map(function (source) { 94 | return path.relative(file.base, source); 95 | }); 96 | 97 | applySourceMap(file, res.sourcemap); 98 | } 99 | return file; 100 | }).then(function(file) { 101 | cb(null, file); 102 | }).catch(function(err) { 103 | // Convert the keys so PluginError can read them 104 | err.lineNumber = err.line; 105 | err.fileName = err.filename; 106 | 107 | // Add a better error message 108 | err.message = err.message + ' in file ' + err.fileName + ' line no. ' + err.lineNumber; 109 | return cb(new PluginError('gulp-less', err)); 110 | }); 111 | }); 112 | }; 113 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-less", 3 | "version": "5.0.0", 4 | "description": "Less for Gulp", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jshint index.js && node_modules/.bin/mocha" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/gulp-community/gulp-less.git" 12 | }, 13 | "engines": { 14 | "node": ">=6" 15 | }, 16 | "keywords": [ 17 | "gulpplugin", 18 | "gulp", 19 | "less" 20 | ], 21 | "author": "Chris Cowan", 22 | "license": "MIT", 23 | "dependencies": { 24 | "less": "^3.7.1 || ^4.0.0", 25 | "object-assign": "^4.0.1", 26 | "plugin-error": "^1.0.0", 27 | "replace-ext": "^2.0.0", 28 | "through2": "^4.0.0", 29 | "vinyl-sourcemaps-apply": "^0.2.0" 30 | }, 31 | "devDependencies": { 32 | "jshint": "^2.9.4", 33 | "mocha": "^9.0.0", 34 | "should": "^13.0.0", 35 | "vinyl": "^2.1.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/expect/buttons.css: -------------------------------------------------------------------------------- 1 | .btn { 2 | display: inline-block; 3 | margin-bottom: 0; 4 | font-weight: normal; 5 | text-align: center; 6 | vertical-align: middle; 7 | cursor: pointer; 8 | background-image: none; 9 | border: 1px solid transparent; 10 | white-space: nowrap; 11 | padding: 6px 12px; 12 | font-size: 14px; 13 | line-height: 1.42857143; 14 | border-radius: 4px; 15 | -webkit-user-select: none; 16 | -moz-user-select: none; 17 | -ms-user-select: none; 18 | -o-user-select: none; 19 | user-select: none; 20 | } 21 | .btn:focus { 22 | outline: thin dotted; 23 | outline: 5px auto -webkit-focus-ring-color; 24 | outline-offset: -2px; 25 | } 26 | .btn:hover, 27 | .btn:focus { 28 | color: #333; 29 | text-decoration: none; 30 | } 31 | .btn:active, 32 | .btn.active { 33 | outline: 0; 34 | background-image: none; 35 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 36 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 37 | } 38 | .btn.disabled, 39 | .btn[disabled], 40 | fieldset[disabled] .btn { 41 | cursor: not-allowed; 42 | pointer-events: none; 43 | opacity: 0.65; 44 | filter: alpha(opacity=65); 45 | -webkit-box-shadow: none; 46 | box-shadow: none; 47 | } 48 | .btn-default { 49 | color: #333; 50 | background-color: #fff; 51 | border-color: #ccc; 52 | } 53 | .btn-primary { 54 | color: #fff; 55 | background-color: #428bca; 56 | border-color: #357ebd; 57 | } 58 | .btn-warning { 59 | color: #fff; 60 | background-color: #f0ad4e; 61 | border-color: #eea236; 62 | } 63 | .btn-danger { 64 | color: #fff; 65 | background-color: #d9534f; 66 | border-color: #d43f3a; 67 | } 68 | .btn-success { 69 | color: #fff; 70 | background-color: #5cb85c; 71 | border-color: #4cae4c; 72 | } 73 | .btn-info { 74 | color: #fff; 75 | background-color: #5bc0de; 76 | border-color: #46b8da; 77 | } 78 | .btn-link { 79 | color: #428bca; 80 | font-weight: normal; 81 | cursor: pointer; 82 | border-radius: 0; 83 | } 84 | .btn-link, 85 | .btn-link:active, 86 | .btn-link[disabled], 87 | fieldset[disabled] .btn-link { 88 | background-color: transparent; 89 | -webkit-box-shadow: none; 90 | box-shadow: none; 91 | } 92 | .btn-link, 93 | .btn-link:hover, 94 | .btn-link:focus, 95 | .btn-link:active { 96 | border-color: transparent; 97 | } 98 | .btn-link:hover, 99 | .btn-link:focus { 100 | color: #2a6496; 101 | text-decoration: underline; 102 | background-color: transparent; 103 | } 104 | .btn-link[disabled]:hover, 105 | fieldset[disabled] .btn-link:hover, 106 | .btn-link[disabled]:focus, 107 | fieldset[disabled] .btn-link:focus { 108 | color: #999999; 109 | text-decoration: none; 110 | } 111 | -------------------------------------------------------------------------------- /test/fixtures/buttons.less: -------------------------------------------------------------------------------- 1 | @import "variables.less"; 2 | @import "mixins.less"; 3 | // 4 | // Buttons 5 | // -------------------------------------------------- 6 | 7 | 8 | // Base styles 9 | // -------------------------------------------------- 10 | 11 | .btn { 12 | display: inline-block; 13 | margin-bottom: 0; // For input.btn 14 | font-weight: @btn-font-weight; 15 | text-align: center; 16 | vertical-align: middle; 17 | cursor: pointer; 18 | background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 19 | border: 1px solid transparent; 20 | white-space: nowrap; 21 | .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base); 22 | .user-select(none); 23 | 24 | &:focus { 25 | .tab-focus(); 26 | } 27 | 28 | &:hover, 29 | &:focus { 30 | color: @btn-default-color; 31 | text-decoration: none; 32 | } 33 | 34 | &:active, 35 | &.active { 36 | outline: 0; 37 | background-image: none; 38 | .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); 39 | } 40 | 41 | &.disabled, 42 | &[disabled], 43 | fieldset[disabled] & { 44 | cursor: not-allowed; 45 | pointer-events: none; // Future-proof disabling of clicks 46 | .opacity(.65); 47 | .box-shadow(none); 48 | } 49 | } 50 | 51 | 52 | // Alternate buttons 53 | // -------------------------------------------------- 54 | 55 | .btn-default { 56 | .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border); 57 | } 58 | .btn-primary { 59 | .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border); 60 | } 61 | // Warning appears as orange 62 | .btn-warning { 63 | .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border); 64 | } 65 | // Danger and error appear as red 66 | .btn-danger { 67 | .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border); 68 | } 69 | // Success appears as green 70 | .btn-success { 71 | .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border); 72 | } 73 | // Info appears as blue-green 74 | .btn-info { 75 | .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border); 76 | } 77 | 78 | 79 | // Link buttons 80 | // ------------------------- 81 | 82 | // Make a button look and behave like a link 83 | .btn-link { 84 | color: @link-color; 85 | font-weight: normal; 86 | cursor: pointer; 87 | border-radius: 0; 88 | 89 | &, 90 | &:active, 91 | &[disabled], 92 | fieldset[disabled] & { 93 | background-color: transparent; 94 | .box-shadow(none); 95 | } 96 | &, 97 | &:hover, 98 | &:focus, 99 | &:active { 100 | border-color: transparent; 101 | } 102 | &:hover, 103 | &:focus { 104 | color: @link-hover-color; 105 | text-decoration: underline; 106 | background-color: transparent; 107 | } 108 | &[disabled], 109 | fieldset[disabled] & { 110 | &:hover, 111 | &:focus { 112 | color: @btn-link-disabled-color; 113 | text-decoration: none; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /test/fixtures/errors.less: -------------------------------------------------------------------------------- 1 | @import lol; 2 | 3 | lol { 4 | fsdfsdf 5 | } -------------------------------------------------------------------------------- /test/fixtures/forms.less: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | // 5 | // Forms 6 | // -------------------------------------------------- 7 | 8 | 9 | // Normalize non-controls 10 | // 11 | // Restyle and baseline non-control form elements. 12 | 13 | fieldset { 14 | padding: 0; 15 | margin: 0; 16 | border: 0; 17 | } 18 | 19 | legend { 20 | display: block; 21 | width: 100%; 22 | padding: 0; 23 | margin-bottom: @line-height-computed; 24 | font-size: (@font-size-base * 1.5); 25 | line-height: inherit; 26 | color: @legend-color; 27 | border: 0; 28 | border-bottom: 1px solid @legend-border-color; 29 | } 30 | 31 | label { 32 | display: inline-block; 33 | margin-bottom: 5px; 34 | font-weight: bold; 35 | } 36 | 37 | 38 | // Normalize form controls 39 | 40 | // Override content-box in Normalize (* isn't specific enough) 41 | input[type="search"] { 42 | .box-sizing(border-box); 43 | } 44 | 45 | // Position radios and checkboxes better 46 | input[type="radio"], 47 | input[type="checkbox"] { 48 | margin: 4px 0 0; 49 | margin-top: 1px \9; /* IE8-9 */ 50 | line-height: normal; 51 | } 52 | 53 | // Set the height of select and file controls to match text inputs 54 | input[type="file"] { 55 | display: block; 56 | } 57 | 58 | // Make multiple select elements height not fixed 59 | select[multiple], 60 | select[size] { 61 | height: auto; 62 | } 63 | 64 | // Fix optgroup Firefox bug per https://github.com/twbs/bootstrap/issues/7611 65 | select optgroup { 66 | font-size: inherit; 67 | font-style: inherit; 68 | font-family: inherit; 69 | } 70 | 71 | // Focus for select, file, radio, and checkbox 72 | input[type="file"]:focus, 73 | input[type="radio"]:focus, 74 | input[type="checkbox"]:focus { 75 | .tab-focus(); 76 | } 77 | 78 | // Fix for Chrome number input 79 | // Setting certain font-sizes causes the `I` bar to appear on hover of the bottom increment button. 80 | // See https://github.com/twbs/bootstrap/issues/8350 for more. 81 | input[type="number"] { 82 | &::-webkit-outer-spin-button, 83 | &::-webkit-inner-spin-button { 84 | height: auto; 85 | } 86 | } 87 | 88 | // Adjust output element 89 | output { 90 | display: block; 91 | padding-top: (@padding-base-vertical + 1); 92 | font-size: @font-size-base; 93 | line-height: @line-height-base; 94 | color: @input-color; 95 | vertical-align: middle; 96 | } 97 | -------------------------------------------------------------------------------- /test/fixtures/mixins.less: -------------------------------------------------------------------------------- 1 | // WebKit-style focus 2 | .tab-focus() { 3 | // Default 4 | outline: thin dotted; 5 | // WebKit 6 | outline: 5px auto -webkit-focus-ring-color; 7 | outline-offset: -2px; 8 | } 9 | 10 | // Drop shadows 11 | .box-shadow(@shadow) { 12 | -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1 13 | box-shadow: @shadow; 14 | } 15 | // Box sizing 16 | .box-sizing(@boxmodel) { 17 | -webkit-box-sizing: @boxmodel; 18 | -moz-box-sizing: @boxmodel; 19 | box-sizing: @boxmodel; 20 | } 21 | 22 | // User select 23 | // For selecting text on the page 24 | .user-select(@select) { 25 | -webkit-user-select: @select; 26 | -moz-user-select: @select; 27 | -ms-user-select: @select; // IE10+ 28 | -o-user-select: @select; 29 | user-select: @select; 30 | } 31 | 32 | // Opacity 33 | .opacity(@opacity) { 34 | opacity: @opacity; 35 | // IE8 filter 36 | @opacity-ie: (@opacity * 100); 37 | filter: ~"alpha(opacity=@{opacity-ie})"; 38 | } 39 | 40 | .button-variant(@color; @background; @border) { 41 | color: @color; 42 | background-color: @background; 43 | border-color: @border; 44 | } 45 | 46 | // Button sizes 47 | // ------------------------- 48 | .button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) { 49 | padding: @padding-vertical @padding-horizontal; 50 | font-size: @font-size; 51 | line-height: @line-height; 52 | border-radius: @border-radius; 53 | } 54 | -------------------------------------------------------------------------------- /test/fixtures/normalize.less: -------------------------------------------------------------------------------- 1 | /*! normalize.css v2.1.3 | MIT License | git.io/normalize */ 2 | 3 | // ========================================================================== 4 | // HTML5 display definitions 5 | // ========================================================================== 6 | 7 | // 8 | // Correct `block` display not defined in IE 8/9. 9 | // 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | main, 20 | nav, 21 | section, 22 | summary { 23 | display: block; 24 | } 25 | 26 | // 27 | // Correct `inline-block` display not defined in IE 8/9. 28 | // 29 | 30 | audio, 31 | canvas, 32 | video { 33 | display: inline-block; 34 | } 35 | 36 | // 37 | // Prevent modern browsers from displaying `audio` without controls. 38 | // Remove excess height in iOS 5 devices. 39 | // 40 | 41 | audio:not([controls]) { 42 | display: none; 43 | height: 0; 44 | } 45 | 46 | // 47 | // Address `[hidden]` styling not present in IE 8/9. 48 | // Hide the `template` element in IE, Safari, and Firefox < 22. 49 | // 50 | 51 | [hidden], 52 | template { 53 | display: none; 54 | } 55 | 56 | // ========================================================================== 57 | // Base 58 | // ========================================================================== 59 | 60 | // 61 | // 1. Set default font family to sans-serif. 62 | // 2. Prevent iOS text size adjust after orientation change, without disabling 63 | // user zoom. 64 | // 65 | 66 | html { 67 | font-family: sans-serif; // 1 68 | -ms-text-size-adjust: 100%; // 2 69 | -webkit-text-size-adjust: 100%; // 2 70 | } 71 | 72 | // 73 | // Remove default margin. 74 | // 75 | 76 | body { 77 | margin: 0; 78 | } 79 | 80 | // ========================================================================== 81 | // Links 82 | // ========================================================================== 83 | 84 | // 85 | // Remove the gray background color from active links in IE 10. 86 | // 87 | 88 | a { 89 | background: transparent; 90 | } 91 | 92 | // 93 | // Address `outline` inconsistency between Chrome and other browsers. 94 | // 95 | 96 | a:focus { 97 | outline: thin dotted; 98 | } 99 | 100 | // 101 | // Improve readability when focused and also mouse hovered in all browsers. 102 | // 103 | 104 | a:active, 105 | a:hover { 106 | outline: 0; 107 | } 108 | -------------------------------------------------------------------------------- /test/fixtures/variables.less: -------------------------------------------------------------------------------- 1 | // 2 | // Variables 3 | // -------------------------------------------------- 4 | 5 | 6 | // Global values 7 | // -------------------------------------------------- 8 | 9 | // Grays 10 | // ------------------------- 11 | 12 | @gray-darker: lighten(#000, 13.5%); // #222 13 | @gray-dark: lighten(#000, 20%); // #333 14 | @gray: lighten(#000, 33.5%); // #555 15 | @gray-light: lighten(#000, 60%); // #999 16 | @gray-lighter: lighten(#000, 93.5%); // #eee 17 | 18 | // // Brand colors 19 | // // ------------------------- 20 | 21 | @brand-primary: #428bca; 22 | @brand-success: #5cb85c; 23 | @brand-warning: #f0ad4e; 24 | @brand-danger: #d9534f; 25 | @brand-info: #5bc0de; 26 | 27 | // // Scaffolding 28 | // // ------------------------- 29 | 30 | @body-bg: #fff; 31 | @text-color: @gray-dark; 32 | 33 | // // Links 34 | // // ------------------------- 35 | 36 | @link-color: @brand-primary; 37 | @link-hover-color: darken(@link-color, 15%); 38 | 39 | // // Typography 40 | // // ------------------------- 41 | 42 | @font-size-base: 14px; 43 | @font-size-large: ceil(@font-size-base * 1.25); // ~18px 44 | @font-size-small: ceil(@font-size-base * 0.85); // ~12px 45 | 46 | @line-height-base: 1.428571429; // 20/14 47 | @line-height-computed: floor(@font-size-base * @line-height-base); // ~20px 48 | 49 | @padding-base-vertical: 6px; 50 | @padding-base-horizontal: 12px; 51 | 52 | @padding-large-vertical: 10px; 53 | @padding-large-horizontal: 16px; 54 | 55 | @padding-small-vertical: 5px; 56 | @padding-small-horizontal: 10px; 57 | 58 | @padding-xs-vertical: 1px; 59 | @padding-xs-horizontal: 5px; 60 | 61 | @line-height-large: 1.33; 62 | @line-height-small: 1.5; 63 | 64 | @border-radius-base: 4px; 65 | @border-radius-large: 6px; 66 | @border-radius-small: 3px; 67 | 68 | @component-active-color: #fff; 69 | @component-active-bg: @brand-primary; 70 | 71 | @caret-width-base: 4px; 72 | @caret-width-large: 5px; 73 | 74 | 75 | // Buttons 76 | // ------------------------- 77 | 78 | @btn-font-weight: normal; 79 | 80 | @btn-default-color: #333; 81 | @btn-default-bg: #fff; 82 | @btn-default-border: #ccc; 83 | 84 | @btn-primary-color: #fff; 85 | @btn-primary-bg: @brand-primary; 86 | @btn-primary-border: darken(@btn-primary-bg, 5%); 87 | 88 | @btn-success-color: #fff; 89 | @btn-success-bg: @brand-success; 90 | @btn-success-border: darken(@btn-success-bg, 5%); 91 | 92 | @btn-warning-color: #fff; 93 | @btn-warning-bg: @brand-warning; 94 | @btn-warning-border: darken(@btn-warning-bg, 5%); 95 | 96 | @btn-danger-color: #fff; 97 | @btn-danger-bg: @brand-danger; 98 | @btn-danger-border: darken(@btn-danger-bg, 5%); 99 | 100 | @btn-info-color: #fff; 101 | @btn-info-bg: @brand-info; 102 | @btn-info-border: darken(@btn-info-bg, 5%); 103 | 104 | @btn-link-disabled-color: @gray-light; 105 | 106 | 107 | // Forms 108 | // ------------------------- 109 | 110 | @input-bg: #fff; 111 | @input-bg-disabled: @gray-lighter; 112 | 113 | @input-color: @gray; 114 | @input-border: #ccc; 115 | @input-border-radius: @border-radius-base; 116 | @input-border-focus: #66afe9; 117 | 118 | @input-color-placeholder: @gray-light; 119 | 120 | @input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2); 121 | @input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2); 122 | @input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2); 123 | 124 | @legend-color: @gray-dark; 125 | @legend-border-color: #e5e5e5; 126 | 127 | @input-group-addon-bg: @gray-lighter; 128 | @input-group-addon-border-color: @input-border; 129 | 130 | @state-success-text: #3c763d; 131 | @state-success-bg: #dff0d8; 132 | @state-success-border: darken(spin(@state-success-bg, -10), 5%); 133 | 134 | @state-info-text: #31708f; 135 | @state-info-bg: #d9edf7; 136 | @state-info-border: darken(spin(@state-info-bg, -10), 7%); 137 | 138 | @state-warning-text: #8a6d3b; 139 | @state-warning-bg: #fcf8e3; 140 | @state-warning-border: darken(spin(@state-warning-bg, -10), 5%); 141 | 142 | @state-danger-text: #a94442; 143 | @state-danger-bg: #f2dede; 144 | @state-danger-border: darken(spin(@state-danger-bg, -10), 5%); 145 | -------------------------------------------------------------------------------- /test/main.js: -------------------------------------------------------------------------------- 1 | var should = require('should'); 2 | var less = require('../'); 3 | var File = require('vinyl') 4 | var fs = require('fs'); 5 | var pj = require('path').join; 6 | 7 | function createVinyl(lessFileName, contents) { 8 | var base = pj(__dirname, 'fixtures'); 9 | var filePath = pj(base, lessFileName); 10 | 11 | return new File({ 12 | cwd: __dirname, 13 | base: base, 14 | path: filePath, 15 | contents: contents || fs.readFileSync(filePath) 16 | }); 17 | } 18 | 19 | describe('gulp-less', function () { 20 | describe('less()', function () { 21 | 22 | it('should pass file when it isNull()', function (done) { 23 | var stream = less(); 24 | var emptyFile = { 25 | isNull: function () { return true; } 26 | }; 27 | stream.once('data', function (data) { 28 | data.should.equal(emptyFile); 29 | done(); 30 | }); 31 | stream.write(emptyFile); 32 | stream.end(); 33 | }); 34 | 35 | it('should emit error when file isStream()', function (done) { 36 | var stream = less(); 37 | var streamFile = { 38 | isNull: function () { return false; }, 39 | isStream: function () { return true; } 40 | }; 41 | stream.once('error', function (err) { 42 | err.message.should.equal('Streaming not supported'); 43 | done(); 44 | }); 45 | stream.write(streamFile); 46 | stream.end(); 47 | }); 48 | 49 | it('should compile single less file', function (done) { 50 | var lessFile = createVinyl('buttons.less'); 51 | 52 | var stream = less(); 53 | stream.once('data', function (cssFile) { 54 | should.exist(cssFile); 55 | should.exist(cssFile.path); 56 | should.exist(cssFile.relative); 57 | should.exist(cssFile.contents); 58 | cssFile.path.should.equal(pj(__dirname, 'fixtures', 'buttons.css')); 59 | String(cssFile.contents).should.equal( 60 | fs.readFileSync(pj(__dirname, 'expect/buttons.css'), 'utf8')); 61 | done(); 62 | }); 63 | stream.write(lessFile); 64 | stream.end(); 65 | }); 66 | 67 | it('should emit error when less contains errors', function (done) { 68 | var errorCalled = false; 69 | var stream = less(); 70 | var errorFile = createVinyl('somefile.less', 71 | Buffer.from('html { color: @undefined-variable; }')); 72 | stream.once('error', function (err) { 73 | err.message.should.equal('variable @undefined-variable is undefined in file '+errorFile.path+' line no. 1'); 74 | errorCalled = true; 75 | errorCalled.should.equal(true); 76 | done(); 77 | }); 78 | stream.once('end', function(){ 79 | errorCalled.should.equal(true); 80 | done(); 81 | }); 82 | stream.write(errorFile); 83 | stream.end(); 84 | }); 85 | 86 | it('should compile multiple less files', function (done) { 87 | var files = [ 88 | createVinyl('buttons.less'), 89 | createVinyl('forms.less'), 90 | createVinyl('normalize.less') 91 | ]; 92 | 93 | var stream = less(); 94 | var count = files.length; 95 | stream.on('data', function (cssFile) { 96 | should.exist(cssFile); 97 | should.exist(cssFile.path); 98 | should.exist(cssFile.relative); 99 | should.exist(cssFile.contents); 100 | if (!--count) { done(); } 101 | }); 102 | 103 | files.forEach(function (file) { 104 | stream.write(file); 105 | }); 106 | stream.end(); 107 | }); 108 | 109 | it('should produce sourcemap filenames and mappings', function (done) { 110 | var files = [ 111 | createVinyl('buttons.less'), 112 | createVinyl('forms.less'), 113 | createVinyl('normalize.less') 114 | ] .map(function (file) { 115 | file.sourceMap = { 116 | file: '', 117 | version : 3, 118 | sourceRoot : '', 119 | sources: [], 120 | names: [], 121 | mappings: '' 122 | }; 123 | 124 | return file; 125 | }); 126 | 127 | var stream = less(); 128 | var count = files.length; 129 | stream.on('data', function (cssFile) { 130 | should.exist(cssFile.sourceMap.file); 131 | should.exist(cssFile.sourceMap.mappings); 132 | should(cssFile.sourceMap.mappings.length).be.greaterThan(1); 133 | }); 134 | stream.once('end', done); 135 | 136 | files.forEach(function (file) { 137 | stream.write(file); 138 | }); 139 | stream.end(); 140 | }); 141 | }); 142 | }); 143 | --------------------------------------------------------------------------------