├── .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 | [](https://www.npmjs.com/package/gulp-less)
7 | [](https://travis-ci.org/gulp-community/gulp-less)
8 |
9 | ## Information
10 |
11 |
12 |
13 | Package | gulp-less |
14 |
15 |
16 | Description |
17 | Less plugin for gulp |
18 |
19 |
20 | Node Version |
21 | >= 6 |
22 |
23 |
24 | Less Version |
25 | 3.7+ | 4.0+ |
26 |
27 |
28 | Gulp Version |
29 | 3.x |
30 |
31 |
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 |
--------------------------------------------------------------------------------