├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── package.json └── techs └── css-sass.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | .DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Marat Dulin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [enb](https://github.com/enb-make/enb)-sass [![npm version](https://img.shields.io/badge/npm-v1.0.1-blue.svg)](https://www.npmjs.com/package/enb-sass) [![node-sass supports version](https://img.shields.io/badge/node--sass-3.2.0-orange.svg)](https://github.com/sass/node-sass/tree/v2) [![Build Status](https://travis-ci.org/enb-make/enb-sass.svg?branch=master)](https://travis-ci.org/enb-make/enb-sass) [![Dependency Status](https://david-dm.org/enb-make/enb-sass.svg)](https://david-dm.org/enb-make/enb-sass) 2 | 3 | Provides the `node-sass` features for project-builder `enb` (https://github.com/enb-make/enb). 4 | 5 | 6 | ## Installing 7 | 8 | ``` 9 | npm install enb-sass --save 10 | ``` 11 | 12 | 13 | ## Options 14 | 15 | * *String* **target** contains target file name. Default: `?.css` 16 | * *String* **filesTarget** contains file masks, according to which a list of source files is created. Default: `?.files`. 17 | * *Array* **sourceSuffixes** Files suffixes that will be used. Default: `css` 18 | * *Object* **sass** `node-sass` options. Read more: https://github.com/sass/node-sass#options. Default: default `node-sass` options. 19 | 20 | 21 | ## Usage 22 | 23 | #### Default use 24 | 25 | ```javascript 26 | nodeConfig.addTech([ 27 | require('enb-sass') 28 | ]); 29 | ``` 30 | 31 | #### Collecting only scss files 32 | 33 | ```javascript 34 | nodeConfig.addTech([ 35 | require('enb-sass'), { 36 | target: '?.css', 37 | sourceSuffixes: ['scss'] 38 | } 39 | ]); 40 | ``` 41 | 42 | #### Use `node-sass` [compression](https://github.com/sass/node-sass#outputstyle) and [debug mode](https://github.com/sass/node-sass#sourcecomments) 43 | 44 | ```javascript 45 | nodeConfig.addTech([ 46 | require('enb-sass'), 47 | { 48 | target: '?.css', 49 | sourceSuffixes: ['scss'], 50 | sass: { 51 | outputStyle: 'compressed', 52 | sourceComments: true 53 | } 54 | } 55 | ]); 56 | ``` 57 | 58 | #### Collecting ie and ie8 css/scss files with `node-sass` [compression](https://github.com/sass/node-sass#outputstyle) and [debug mode](https://github.com/sass/node-sass#sourcecomments) 59 | 60 | ```javascript 61 | nodeConfig.addTech([ 62 | require('enb-sass'), 63 | { 64 | target: '?.css', 65 | sourceSuffixes: ['css', 'scss', 'ie.css', 'ie.scss', 'ie8.css', 'ie8.scss'], 66 | sass: { 67 | outputStyle: 'compressed', 68 | sourceComments: true 69 | } 70 | } 71 | ]); 72 | ``` 73 | 74 | 75 | ## Used in 76 | * Yandex TV https://tv.yandex.ru/ 77 | * Kinopoisk https://kinopoisk.ru/ 78 | 79 | 80 | ## Thanks 81 | 82 | * Abramov Andrew ([@blond](https://github.com/blond)). For the support and correct answers. 83 | * Filatov Dmitry ([@dfilatov](https://github.com/dfilatov)). For `vow`, `vow-fs`, `inherit`. 84 | * Georgy Krasulya ([@gkrasulya](https://github.com/gkrasulya)). For `rich error reporting`. 85 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "enb-sass", 3 | "version": "1.3.3", 4 | "description": "enb sass tech", 5 | "keywords": [ 6 | "enb", 7 | "sass", 8 | "scss", 9 | "css" 10 | ], 11 | "author": "Viacheslav Glushko ", 12 | "licenses": [ 13 | { 14 | "type": "MIT", 15 | "url": "https://github.com/enb-make/enb-sass/blob/master/LICENSE" 16 | } 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "git@github.com:enb-make/enb-sass.git" 21 | }, 22 | "homepage": "https://github.com/enb-make/enb-sass", 23 | "bugs": "https://github.com/enb-make/enb-sass/issues", 24 | "maintainers": [ 25 | { 26 | "name": "Viacheslav Glushko", 27 | "email": "mail@ixax.me", 28 | "github-username": "ixax" 29 | } 30 | ], 31 | "dependencies" : { 32 | "enb": "0.15.0", 33 | "node-sass": "3.3.3", 34 | "vow": "0.4.9", 35 | "inherit": "2.2.2" 36 | }, 37 | "engines": { 38 | "node": ">=0.10" 39 | }, 40 | "main": "techs/css-sass.js" 41 | } 42 | -------------------------------------------------------------------------------- /techs/css-sass.js: -------------------------------------------------------------------------------- 1 | var sass = require('node-sass'); 2 | var Vow = require('vow'); 3 | var inherit = require('inherit'); 4 | var CssPreprocessor = require('enb/lib/preprocess/css-preprocessor'); 5 | var vowFs = require('enb/lib/fs/async-fs'); 6 | var path = require('path'); 7 | var util = require('util'); 8 | var Logger = require('enb/lib/logger'); 9 | 10 | Logger = new Logger(); 11 | 12 | module.exports = require('enb/lib/build-flow').create() 13 | .name('enb-sass') 14 | .target('target', '?.css') 15 | .defineOption('sass', {}) // https://github.com/sass/node-sass#options 16 | .useFileList(['css', 'scss']) 17 | .builder(function (sourceFiles) { 18 | var _this = this; 19 | var deferred = Vow.defer(); 20 | var sassSettings = inherit({ 21 | includePaths: [], 22 | data: '' 23 | }, this._sass); 24 | var errorLogging = { 25 | enabled: true, 26 | offsetLines: 5 27 | }; 28 | 29 | // Prevent user mistakes 30 | sassSettings.includePaths = sassSettings.includePaths instanceof Array ? sassSettings.includePaths : []; 31 | sassSettings.data = sassSettings.data instanceof String ? sassSettings.data : ''; 32 | 33 | var promises = sourceFiles.map(function (file) { 34 | var filename = file.fullname; 35 | var fileDir = path.dirname(filename); 36 | 37 | if (sassSettings.includePaths.indexOf(fileDir) === -1) { 38 | sassSettings.includePaths.push(fileDir); 39 | } 40 | 41 | return vowFs.read(filename, 'utf8') 42 | .then(function (data) { 43 | data = _this._processUrls(data, filename); 44 | data = _this._processIncludes(data, filename); 45 | 46 | return data; 47 | }); 48 | }); 49 | 50 | Vow.all(promises) 51 | .then(function () { 52 | // Collect the contents of all files into one big string 53 | sassSettings.data += Array.prototype.slice.call(arguments[0], 0).join("\n"); 54 | 55 | var successCb = sassSettings.success instanceof Function ? sassSettings.success : function () {}; 56 | 57 | if (sassSettings.data.length == 0) { 58 | Logger.log('[enb-sass]: empty string given.'); 59 | deferred.resolve(''); 60 | } 61 | 62 | // In some cases `renderSync` does not give the data in the handler, so we use a try...catch 63 | try { 64 | var cssResult = sass.renderSync(sassSettings).css; 65 | successCb(cssResult); 66 | deferred.resolve(cssResult); 67 | } catch (ex) { 68 | ex = ex instanceof Error ? ex : JSON.parse(ex); 69 | 70 | var lines = sassSettings.data.split('\n'); 71 | var errorCtx = lines.slice(ex.line - errorLogging.offsetLines, ex.line).concat( 72 | '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^', 73 | lines.slice(ex.line + 1, ex.line + errorLogging.offsetLines + 1) 74 | ).join('\n'); 75 | var formattedError; 76 | 77 | // Finding filename 78 | var i = ex.line; 79 | var filename; 80 | var searchString = ': begin */'; 81 | while (!filename) { 82 | if (lines[i].indexOf(searchString, lines[i] - searchString.length) !== -1) { 83 | filename = lines[i].match(/^\/\*\s+(.*):\s+begin\s+\*\/$/); 84 | } 85 | i--; 86 | } 87 | 88 | formattedError = util.format( 89 | 'File:%s\nMessage: %s\nContext:\n%s', 90 | filename[1] + ':' + (ex.line - i - 2), 91 | ex.message, 92 | errorCtx 93 | ); 94 | 95 | if (errorLogging.enabled) { 96 | Logger.log(formattedError); 97 | } 98 | 99 | deferred.reject(formattedError); 100 | } 101 | }.bind(this)) 102 | .fail(function (ex) { 103 | ex = ex instanceof Error ? ex : JSON.parse(ex); 104 | deferred.reject(ex); 105 | }); 106 | 107 | return deferred.promise(); 108 | }) 109 | .methods({ 110 | _processUrls: function (data, filename) { 111 | return this._getCssPreprocessor()._processUrls(data, filename); 112 | }, 113 | 114 | _processIncludes: function (data, filename) { 115 | var fileRelativeUrl = this._resolveCssUrl(filename, filename); 116 | 117 | return this._getCssPreprocessor()._processIncludes(data, filename).then(function (data) { 118 | return '/* ' + fileRelativeUrl + ': begin */' + 119 | '\n' + data + '\n' + 120 | '/* ' + fileRelativeUrl + ': end */\n'; 121 | }); 122 | }, 123 | 124 | _resolveCssUrl: function (url, filename) { 125 | return this._getCssPreprocessor()._resolveCssUrl(url, filename); 126 | }, 127 | 128 | _getCssPreprocessor: function () { 129 | var _this = this; 130 | var preprocessCss = new CssPreprocessor(); 131 | 132 | // In css-preprocessor used `hash-url` so `url(#{$var})` won't work correctly 133 | // Will return current css/scss file url 134 | preprocessCss._resolveCssUrl = function (url, filename) { 135 | if (url.substr(0, 5) === 'data:' || 136 | url.substr(0, 2) === '//' || 137 | ~url.indexOf('http://') || 138 | ~url.indexOf('https://') 139 | ) { 140 | return url; 141 | } else { 142 | return this._buildCssRelativeUrl(url, filename); 143 | } 144 | }; 145 | 146 | preprocessCss.setCssRelativeUrlBuilder(function (url, filename) { 147 | var urlFilename = path.resolve(path.dirname(filename), url); 148 | return _this.node.relativePath(urlFilename); 149 | }); 150 | 151 | return preprocessCss; 152 | } 153 | }) 154 | .createTech(); 155 | --------------------------------------------------------------------------------