├── .gitignore ├── transform.js ├── .github └── workflows │ └── ci.yml ├── package.json ├── README.md ├── LICENSE ├── index.js └── test └── lessify.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /transform.js: -------------------------------------------------------------------------------- 1 | module.exports = require('cssify'); 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - "**" 7 | pull_request: 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | node-version: [18.x, 20.x] 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | cache: npm 25 | 26 | - name: Install dependencies 27 | run: npm ci 28 | 29 | - name: Run tests 30 | run: npm test 31 | 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lessify", 3 | "version": "1.0.2", 4 | "description": "Middleware and Browserify transform for less files", 5 | "main": "./index.js", 6 | "browserify": "./transform.js", 7 | "scripts": { 8 | "test": "node test/lessify.js" 9 | }, 10 | "author": "Drew Stokes", 11 | "license": "BSD-2-Clause", 12 | "dependencies": { 13 | "cssify": "^0.6.0", 14 | "less": "^2.2.0", 15 | "through": "^2.3.6" 16 | }, 17 | "devDependencies": { 18 | "tape": "~2.1.0" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git://github.com/dstokes/lessify.git" 23 | }, 24 | "keywords": [ 25 | "browserify", 26 | "less", 27 | "transform" 28 | ], 29 | "bugs": { 30 | "url": "https://github.com/dstokes/lessify/issues" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | lessify 2 | ======= 3 | Middleware and Browserify transform for less files. 4 | 5 | [![CI](https://github.com/dstokes/lessify/actions/workflows/ci.yml/badge.svg)](https://github.com/dstokes/lessify/actions/workflows/ci.yml) 6 | [![NPM](https://nodei.co/npm/lessify.png?downloads=true)](https://nodei.co/npm/lessify/) 7 | 8 | usage 9 | ===== 10 | some.less 11 | ``` less 12 | .nav { width: (1 + 1); } 13 | ``` 14 | 15 | entry.js 16 | ``` 17 | require('some.less'); 18 | ``` 19 | 20 | then 21 | 22 | ``` 23 | > browserify -t lessify entry.js > app.js 24 | ``` 25 | 26 | we haz css in our bundle! 27 | 28 | options 29 | ======= 30 | 31 | Less options can be specified either on the command line: 32 | 33 | ``` 34 | > browserify -t [ lessify --relativeUrls --rootpath http://www.example.com/ ] entry.js 35 | ``` 36 | 37 | Or using the API: 38 | 39 | ``` 40 | var browserify = require('browserify'); 41 | var lessify = require('lessify'); 42 | 43 | var b = browserify(); 44 | b.transform({relativeUrls: true, rootpath: 'http://www.example.com/'}, lessify); 45 | ... 46 | ``` 47 | 48 | install 49 | ======= 50 | 51 | With [npm](http://npmjs.org) do: 52 | 53 | ``` 54 | npm install lessify 55 | ``` 56 | 57 | contributors 58 | ============ 59 | 60 | [https://github.com/dstokes/lessify/graphs/contributors](https://github.com/dstokes/lessify/graphs/contributors) 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Drew Stokes 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var less = require("less") 2 | , through = require('through') 3 | , path = require('path'); 4 | 5 | module.exports = function(file, opts) { 6 | var input = ''; 7 | if (/\.less$/i.test(file) === false) { 8 | return through(); 9 | } 10 | 11 | function write(data) { input += data; } 12 | function end() { 13 | var self = this; 14 | var lessOpts = (opts || {}); 15 | var autoInject = typeof(lessOpts['auto-inject']) == 'undefined' || !!lessOpts['auto-inject']; 16 | 17 | function jsToLoad(css) { 18 | var stringifiedCss = JSON.stringify(css); 19 | if (autoInject) { 20 | return "var css = "+ stringifiedCss +";(require('lessify'))(css); module.exports = css;"; 21 | } else { 22 | return "module.exports = " + stringifiedCss; 23 | } 24 | } 25 | 26 | lessOpts.filename = file; 27 | lessOpts.paths = lessOpts.paths ? lessOpts.paths.concat([path.dirname(file)]) : [path.dirname(file)]; 28 | 29 | less.render(input, lessOpts, function(err, output) { 30 | if (err) { 31 | self.emit('error', new Error(err.message + ': ' + err.filename + '(' + err.line + ')')); 32 | } else { 33 | self.queue(jsToLoad(output.css)); 34 | } 35 | output.imports.forEach(function(f) { 36 | self.emit('file', f); 37 | }); 38 | self.queue(null); 39 | }); 40 | } 41 | 42 | return through(write, end); 43 | } 44 | -------------------------------------------------------------------------------- /test/lessify.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | , lessify = require('../') 3 | , through = require('through'); 4 | 5 | test('skips non-less files', function(t) { 6 | var result = '' 7 | , input = 'test' 8 | , s = lessify('test.js'); 9 | 10 | var ts = through(function(d) { result += d; }, function() { 11 | t.assert(result === input, 'should pass through non-less files'); 12 | t.end(); 13 | }); 14 | 15 | s.pipe(ts); 16 | s.write(input); 17 | s.end(); 18 | }); 19 | 20 | test('should browserify less files', function(t) { 21 | var result = '' 22 | , s = lessify('test.less'); 23 | 24 | var ts = through(function(d) { result += d; }, function() { 25 | t.assert(result.indexOf('width: 2') !== -1, 'should parse less'); 26 | t.end(); 27 | }); 28 | 29 | s.pipe(ts); 30 | s.write('.nav { width: (1 + 1); }'); 31 | s.end(); 32 | }); 33 | 34 | test('should pass less options', function(t) { 35 | var result = '' 36 | , s = lessify('mycss/test.less', {rootpath: 'mycss/'}); 37 | 38 | var ts = through(function(d) { result += d; }, function() { 39 | var urlMatch = /url\(\\"(.*?)\\"\)/.exec(result) 40 | t.ok(urlMatch, 'should have background-image url'); 41 | t.equal(urlMatch[1], 'mycss/images/cat.jpg', 'should honour rootpath'); 42 | t.end(); 43 | }); 44 | 45 | s.pipe(ts); 46 | s.write('body { background-image: url("images/cat.jpg"); }'); 47 | s.end(); 48 | }); 49 | 50 | test('should throw on invalid less', function(t) { 51 | t.plan(1); 52 | var result = '' 53 | , s = lessify('test.less'); 54 | 55 | s.write('.}'); 56 | t.throws(function() { s.end(); }, 'should throw on invalid less'); 57 | }); 58 | 59 | test('should not auto-inject when option set', function (t) { 60 | 61 | var result = '' 62 | , s = lessify('test.less', {"auto-inject": false}); 63 | 64 | var ts = through(function(d) { result += d; }, function() { 65 | t.assert(result.indexOf('require(\'lessify\')') === -1, 'should not require lessify'); 66 | t.end(); 67 | }); 68 | 69 | s.pipe(ts); 70 | s.write('.nav { width: (1 + 1); }'); 71 | s.end(); 72 | 73 | }); 74 | 75 | test('should auto-inject by default', function (t) { 76 | 77 | var result = '' 78 | , s = lessify('test.less'); 79 | 80 | var ts = through(function(d) { result += d; }, function() { 81 | t.assert(result.indexOf('require(\'lessify\')') !== -1, 'should require lessify'); 82 | t.end(); 83 | }); 84 | 85 | s.pipe(ts); 86 | s.write('.nav { width: (1 + 1); }'); 87 | s.end(); 88 | 89 | }); 90 | --------------------------------------------------------------------------------