Merge pull request #254 from dlmanning/2.x

Merge 2.x into Master
This commit is contained in:
Vincent Prouillet 2015-05-06 16:31:19 +01:00
commit ee039186f0
27 changed files with 879 additions and 381 deletions

21
.editorconfig Normal file
View file

@ -0,0 +1,21 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# Change these settings to your own preference
indent_style = space
indent_size = 2
# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

155
.eslintrc Normal file
View file

@ -0,0 +1,155 @@
env:
mocha: true
node: true
# globals:
#########################
## Only add globals if you're absolutely certain they need to be globals
##########################
# console: true
#########################
## set to 0 to allow
## set to 1 to disallow as warning
## set to 2 to disallow as error
#########################
rules:
#########################
## Optional Rules
#########################
# Disallow use of `console`
no-console: 2
# Disallow warning comments
no-warning-comments:
- 1
- terms
- todo
- fixme
location
- anywhere
# Warns when variables are defined but never used
no-unused-vars: 1
# Enforces comma style (first or last)
comma-style:
- 2
- last
# Enforces one true `this` variable
consistent-this:
- 2
- self
# Allows dangling underscores in identifiers
no-underscore-dangle: 2
# Enforces function expressions to have a name
func-names: 0
# Set maximum depth of nested callbacks
max-nested-callbacks:
- 1
- 3
#########################
## Core Rules
##########################
# Enforces camel case names
camelcase: 2
# Prohibit use of == and != in favor of === and !==
eqeqeq: 2
# Suppresses warnings about == null comparisons
no-eq-null: 2
# No mixing tabs and spaces, with 2 spaces only
no-mixed-spaces-and-tabs: 2
# Prohibits use of a variable before it is defined
no-use-before-define: 2
# Requires capitalized names for constructor functions
new-cap: 2
# Prohibits use of explicitly undeclared variables
no-undef: 2
# Enforces Use Strict at the top of function scope
strict:
- 2
- global
# Requires variable declarations to be at the top
vars-on-top: 2
# Enforce curly braces around blocks in loops and conditionals
curly: 2
# Prohibits the use of immediate function invocations w/o wrapping in parentheses
wrap-iife: 2
# Prohibits `argument.caller` and `argument.callee`
no-caller: 2
# Requires all `for in` loops to filter object's items
guard-for-in: 2
# Prohibits comparing a variable against itself
no-self-compare: 2
# Prohibits use of `undefined` variable
no-undefined: 0
# Prohibits nested ternaries
no-nested-ternary: 2
# Enforces a space before blocks
space-before-blocks:
- 2
- always
# Enforces spaces following keywords
space-after-keywords:
- 2
- always
- checkFunctionKeyword: true
# Enforces quoted property names
quote-props:
- 2
- always
# Enforces padded blocks
padded-blocks:
- 1
- never
# Enforce functions as expressions
func-style:
- 2
- expression
# Require brace style
brace-style:
- 2
- stroustrup
# Prohibits Yoda conditions
yoda:
- 2
- never
# Enforce use of single quotation marks for strings.
quotes:
- 2
- single
# Enforces space inside of brackets (except property name)
space-in-brackets:
- 2
- always
- propertyName: false
singleValue: false

View file

@ -1,3 +1,5 @@
language: node_js
node_js:
- "0.10"
- node
- iojs

View file

@ -1,3 +1,18 @@
# Gulp Sass Changelog
## v2.0.0-alpha.1
**March 26, 2015**
* **New** Added `renderSync` option that can be used through `sass.sync()`
### March 24, 2015
* **Change** Updated to `node-sass` 3.0.0-alpha.1
* **New** Added support for `gulp-sourcemaps` including tests
* **New** Added `.editorconfig` for development consistency
* **New** Added linting and test for said linting
* **Change** Updated the README
* **New** `logError` function to make streaming errors possible instead of breaking the stream
### 1.3.3
* updated to node-sass 2.0 (final)

63
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,63 @@
# Contributing to Gulp Sass
Gulp Sass is a very light-weight [Gulp](https://github.com/gulpjs/gulp) wrapper for [`node-sass`](https://github.com/sass/node-sass), which in turn is a Node binding for [`libsass`](https://github.com/sass/libsass), which in turn is a port of [`Sass`](https://github.com/sass/sass).
## Submitting Issues
* Before creating a new issue, perform a [cursory search](https://github.com/issues?utf8=%E2%9C%93&q=repo%3Adlmanning%2Fgulp-sass+repo%3Asass%2Fnode-sass+repo%3Asass%2Flibsass+repo%3Asass%2Fsass+repo%3Asass-eyeglass%2Feyeglass) in the Gulp Sass, Node Sass, Libsass, and main Sass repos to see if a similar issue has already been submitted.
* You can create an issue [here](https://github.com/dlmanning/gulp-sass/issues). Please include as many details as possible in your report.
* Issue titles should be descriptive, explaining at the high level what it is about.
* Please include the version of `gulp-sass` you are using.
* Do not open a [pull request](#pull-requests) to resolve an issue without first receiving feedback from a `collaborator` or `owner` and having them agree on a solution forward.
* Include screenshots and animated GIFs whenever possible; they are immensely helpful.
* Issues that have a number of sub-items that need to be complete should use [task lists](https://github.com/blog/1375%0A-task-lists-in-gfm-issues-pulls-comments) to track the sub-items in the main issue comment.
## Pull Requests
* **DO NOT ISSUE A PULL REQUEST WITHOUT FIRST [SUBMITTING AN ISSUE](#submitting-issues)**
* Pull requests should reference their related issues. If the pull request closes an issue, [please reference its closing in your commit messages](https://help.github.com/articles/closing-issues-via-commit-messages/). Pull requests not referencing any issues will be closed.
* Pull request titles should be descriptive, explaining at the high level what it is doing, and should be written in the same style as [Git commit messages](#git-commit-messages).
* Update the `CHANGELOG` with the changes made by your pull request, making sure to use the proper [Emoji](#emoji-cheatsheet).
* Follow our JavaScript styleguides. Tests will fail if you do not.
* Ensure that you have [EditorConfig](http://editorconfig.org/) installed in your editor of choice and that it is functioning properly.
* Do not squash or rebase your commits when submitting a Pull Request. It makes it much harder to follow your work and make incremental changes.
* Update the [CHANGELOG](#maintaining-thechangelog) with your changes.
* Branches should be made off of the most current `master` branch from `git@github.com:dlmanning/gulp-sass.git`
* Pull requests should be made into our [master](https://github.com/dlmanning/gulp-sass/tree/master) branch.
### Git Commit Messages
* Use the present tense (`"Add feature"` not `"Added Feature"`)
* Use the imperative mood (`"Move cursor to…"` not `"Moves cursor to…"`)
* Limit the first line to 72 characters or less
* Consider including relevant Emoji from our [Emoji cheatsheet](#emoji-cheatsheet)
## Creating a New Version
Versioning is done through [SEMVER](http://semver.org/). When creating a new version, create new release branch off of `master` with the version's name, and create a new tag with `v` prefixed with the version's name from that branch.
For instance, if you are creating version `1.1.0`, you would create a branch `release/1.1.0` from `master` and create a tag `v1.1.0` from branch `release/1.1.0`.
### Maintaining the Changelog
The Changelog should have a list of changes made for each version. They should be organized so additions come first, changes come second, and deletions come third. Version numbers should be 2nd level headers with the `v` in front (like a tag) and the date of the version's most recent update should be underneath in italics.
Changelog messages do not need to cover each individual commit made, but rather should have individual summaries of the changes made. Changelog messages should be written in the same style as [Git commit messages](#git-commit-messages).
## Emoji Cheatsheet
When creating creating commits or updating the CHANGELOG, please **start** the commit message or update with one of the following applicable Emoji. Emoji should not be used at the start of issue or pull request titles.
* :art: `:art:` when improving the format/structure of the code
* :racehorse: `:racehorse:` when improving performance
* :memo: `:memo:` when writing long-form text (documentation, guidelines, principles, etc…)
* :bug: `:bug:` when fixing a bug
* :fire: `:fire:` when removing code or files
* :green_heart: `:green_heart:` when fixing the CI build
* :white_check_mark: `:white_check_mark:` when adding tests
* :lock: `:lock:` when dealing with security
* :arrow_up: `:arrow_up:` when upgrading dependencies
* :arrow_down: `:arrow_down:` when downgrading dependencies
* :shirt: `:shirt:` when removing linter warnings
* :shipit: `:shipit:` when creating a new release

View file

@ -1,21 +1,16 @@
[![Build Status](https://travis-ci.org/dlmanning/gulp-sass.svg?branch=master)](https://travis-ci.org/dlmanning/gulp-sass)
# gulp-sass [![Build Status](https://travis-ci.org/dlmanning/gulp-sass.svg?branch=master)](https://travis-ci.org/dlmanning/gulp-sass) [![Join the chat at https://gitter.im/dlmanning/gulp-sass](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dlmanning/gulp-sass?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
gulp-sass
=========
[![Join the chat at https://gitter.im/dlmanning/gulp-sass](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dlmanning/gulp-sass?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Sass plugin for [gulp](https://github.com/gulpjs/gulp).
Sass plugin for [Gulp](https://github.com/gulpjs/gulp).
# Install
```
npm install gulp-sass
npm install gulp-sass --save-dev
```
# Basic Usage
Something like this:
Something like this will compile your Sass files:
```javascript
var gulp = require('gulp');
@ -23,36 +18,31 @@ var sass = require('gulp-sass');
gulp.task('sass', function () {
gulp.src('./scss/*.scss')
.pipe(sass())
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('./css'));
});
```
Options passed as a hash into `sass()` will be passed along to [`node-sass`](https://github.com/sass/node-sass).
You can also compile synchronously, doing something like this:
If you want to use the indented syntax (`.sass`) as the top level file, use `sass({indentedSyntax: true})`.
```javascript
var gulp = require('gulp');
var sass = require('gulp-sass');
## gulp-sass specific options
gulp.task('sass', function () {
gulp.src('./scss/*.scss')
.pipe(sass.sync().on('error', sass.logError))
.pipe(gulp.dest('./css'));
});
```
#### `errLogToConsole: true`
## Options
If you pass `errLogToConsole: true` into the options hash, sass errors will be logged to the console instead of generating a `gutil.PluginError` object. Use this option with `gulp.watch` to keep gulp from stopping every time you mess up your sass.
#### `onSuccess: callback`
Pass in your own callback to be called upon successful compilation by node-sass. The callback has the form `callback(css)`, and is passed the compiled css as a string. Note: This *does not* prevent gulp-sass's default behavior of writing the output css file.
#### `onError: callback`
Pass in your own callback to be called upon a sass error from node-sass. The callback has the form `callback(err)`, where err is the error string generated by libsass. Note: this *does* prevent an actual `gulpPluginError` object from being created.
#### `sync: true`
If you pass `sync: true` into the options hash, sass.renderSync will be called, instead of sass.render. This should help when memory and/or cpu usage is getting very high when rendering many and/or big files.
Pass in options just like you would for [`node-sass`](https://github.com/sass/node-sass#options); they will be passed along just as if you were using `node-sass`.
## Source Maps
gulp-sass can be used in tandem with [gulp-sourcemaps](https://github.com/floridoo/gulp-sourcemaps) to generate source maps for the SASS to CSS compilation. You will need to initialize [gulp-sourcemaps](https://github.com/floridoo/gulp-sourcemaps) prior to running the gulp-sass compiler and write the source maps after.
`gulp-sass` can be used in tandem with [gulp-sourcemaps](https://github.com/floridoo/gulp-sourcemaps) to generate source maps for the Sass to CSS compilation. You will need to initialize [gulp-sourcemaps](https://github.com/floridoo/gulp-sourcemaps) prior to running `gulp-sass` and write the source maps after.
```javascript
var sourcemaps = require('gulp-sourcemaps');
@ -62,11 +52,9 @@ gulp.src('./scss/*.scss')
.pipe(sass())
.pipe(sourcemaps.write())
.pipe(gulp.dest('./css'));
// will write the source maps inline in the compiled CSS files
```
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.
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 path relative to the `gulp.dest()` destination in the `sourcemaps.write()` function.
```javascript
var sourcemaps = require('gulp-sourcemaps');
@ -76,39 +64,14 @@ gulp.src('./scss/*.scss')
.pipe(sass())
.pipe(sourcemaps.write('./maps'))
.pipe(gulp.dest('./css'));
// will write the source maps to ./dest/css/maps
```
# Imports and Partials
gulp-sass now automatically passes along the directory of every scss file it parses as an include path for node-sass. This means that as long as you specify your includes relative to path of your scss file, everything will just work.
scss/includes/_settings.scss:
```scss
$blue: #3bbfce;
$margin: 16px;
```
scss/style.scss:
```scss
@import "includes/settings";
.content-navigation {
border-color: $blue;
color:
darken($blue, 9%);
}
.border {
padding: $margin / 2;
margin: $margin / 2;
border-color: $blue;
}
```
# Issues
Before submitting an issue, please understand that gulp-sass is only a wrapper for [node-sass](https://github.com/sass/node-sass), which in turn is a node front end for [libsass](https://github.com/sass/libsass). Missing sass features and errors should not be reported here.
`gulp-sass` is a very light-weight wrapper around [`node-sass`](https://github.com/sass/node-sass), which in turn is a Node binding for [`libsass`](https://github.com/sass/libsass), which in turn is a port of [`Sass`](https://github.com/sass/sass). Because of this, the issue you're having likely isn't a `gulp-sass` issue, but an issue with one of those three projects.
If you have a feature request/question how Sass works/concerns on how your Sass gets compiled/errors in your compiling, it's likely a `libsass` or `Sass` issue and you should file your issue with one of those projects.
If you're having problems with the options you're passing in, it's likely a `node-sass` or `libsass` issued and you should file your issue with one of those projects.
We may, in the course of resolving issues, direct you to one of these other projects. If we do so, please follow up by searching that project's issue queue (both open and closed) for your problem and, if it doesn't exist, filing an issue with them.

206
index.js
View file

@ -1,104 +1,156 @@
var fs = require('fs')
, map = require('map-stream')
, nodeSass = require('node-sass')
, path = require('path')
, gutil = require('gulp-util')
, clone = require('clone')
, ext = gutil.replaceExtension
, applySourceMap = require('vinyl-sourcemaps-apply')
;
'use strict';
module.exports = function (options) {
var gutil = require('gulp-util');
var through = require('through2');
var assign = require('object-assign');
var path = require('path');
var applySourceMap = require('vinyl-sourcemaps-apply');
function sass (file, cb) {
var opts = options ? clone(options) : {};
var fileDir = path.dirname(file.path);
var PLUGIN_NAME = 'gulp-sass';
//////////////////////////////
// Main Gulp Sass function
//////////////////////////////
var gulpSass = function gulpSass(options, sync) {
return through.obj(function(file, enc, cb) {
var opts,
filePush,
errorM,
callback,
result;
if (file.isNull()) {
return cb(null, file);
}
if (file.isStream()) {
return cb(new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported'));
}
if (path.basename(file.path).indexOf('_') === 0) {
return cb();
}
opts = assign({}, options);
opts.data = file.contents.toString();
// Ensure `indentedSyntax` is true if a `.sass` file
if (path.extname(file.path) === '.sass') {
opts.indentedSyntax = true;
}
// Ensure file's parent directory in the include path
if (opts.includePaths) {
if (typeof opts.includePaths === 'string') {
opts.includePaths = [opts.includePaths];
}
}
else {
opts.includePaths = [];
}
opts.includePaths.push(path.dirname(file.path));
// Generate Source Maps if plugin source-map present
if (file.sourceMap) {
opts.sourceMap = file.path;
opts.omitSourceMapUrl = true;
}
opts.data = file.contents.toString();
opts.file = file.path;
//////////////////////////////
// Handles returning the file to the stream
//////////////////////////////
filePush = function filePush(sassObj) {
var sassMap,
sassMapFile,
sassFileSrc;
if (opts.includePaths && Array.isArray(opts.includePaths)) {
if (opts.includePaths.indexOf(fileDir) === -1) {
opts.includePaths.push(fileDir);
// Build Source Maps!
if (sassObj.map) {
// Transform map into JSON
sassMap = JSON.parse(sassObj.map.toString());
// Grab the stdout and transform it into stdin
sassMapFile = sassMap.file.replace('stdout', 'stdin');
// Grab the base file name that's being worked on
sassFileSrc = file.path.split('/').pop();
// Replace the stdin with the original file name
sassMap.sources[sassMap.sources.indexOf(sassMapFile)] = sassFileSrc;
// Replace the map file with the original file name
sassMap.file = sassFileSrc;
// Apply the map
applySourceMap(file, sassMap);
}
} else {
opts.includePaths = [fileDir];
}
opts.success = function (obj) {
if (typeof opts.onSuccess === 'function') opts.onSuccess(obj);
file.contents = sassObj.css;
file.path = gutil.replaceExtension(file.path, '.css');
if (obj.map && typeof obj.map === 'string') {
// hack to remove the already added sourceMappingURL from libsass
obj.css = obj.css.replace(/\/\*#\s*sourceMappingURL\=.*\*\//, '');
cb(null, file);
};
// libsass gives us sources' paths relative to file;
// gulp-sourcemaps needs sources' paths relative to file.base;
// so alter the sources' paths to please gulp-sourcemaps.
obj.map = JSON.parse(obj.map);
//////////////////////////////
// Handles error message
//////////////////////////////
errorM = function errorM(error) {
var relativePath = '',
filePath = error.file === 'stdin' ? file.path : error.file,
message = '';
if (obj.map.sources) {
obj.map.sources = obj.map.sources.map(function(source) {
var abs = path.resolve(path.dirname(file.path), source);
return path.relative(file.base, abs);
});
filePath = filePath ? filePath : file.path;
relativePath = path.relative(process.cwd(), filePath);
obj.map = JSON.stringify(obj.map);
applySourceMap(file, obj.map);
message += gutil.colors.underline(relativePath) + '\n';
message += gutil.colors.gray(' ' + error.line + ':' + error.column) + ' ';
message += error.message;
return cb(new gutil.PluginError(
PLUGIN_NAME, message
));
};
if (sync !== true) {
//////////////////////////////
// Async Sass render
//////////////////////////////
callback = function(error, obj) {
if (error) {
return errorM(error);
}
filePush(obj);
};
}
handleOutput(obj, file, cb);
};
opts.error = function (err) {
if (opts.errLogToConsole) {
gutil.log(gutil.colors.red('[gulp-sass]', err.message, 'on line', err.line + 'in', err.file));
return cb();
}
if (typeof opts.onError === 'function') {
opts.onError(err);
return cb();
}
err.lineNumber = err.line;
err.fileName = err.file;
return cb(new gutil.PluginError('gulp-sass', err));
};
if ( opts.sync ) {
try {
var output = nodeSass.renderSync(opts);
opts.success(output);
handleOutput(output, file, cb);
} catch(err) {
opts.error(err);
}
} else {
nodeSass.render(opts);
gulpSass.compiler.render(opts, callback);
}
else {
//////////////////////////////
// Sync Sass render
//////////////////////////////
try {
result = gulpSass.compiler.renderSync(opts);
}
return map(sass);
filePush(result);
}
catch(error) {
return errorM(error);
}
}
});
};
function handleOutput(output, file, cb) {
file.path = ext(file.path, '.css');
file.contents = new Buffer(output.css);
cb(null, file);
}
//////////////////////////////
// Sync Sass render
//////////////////////////////
gulpSass.sync = function sync(options) {
return gulpSass(options, true);
};
//////////////////////////////
// Log errors nicely
//////////////////////////////
gulpSass.logError = function logError(error) {
gutil.log(gutil.colors.red('[' + PLUGIN_NAME + '] ') + error.message);
};
//////////////////////////////
// Store compiler in a prop
//////////////////////////////
gulpSass.compiler = require('node-sass');
module.exports = gulpSass;

View file

@ -1,10 +1,10 @@
{
"name": "gulp-sass",
"version": "1.3.3",
"version": "2.0.0",
"description": "Gulp plugin for sass",
"main": "index.js",
"scripts": {
"test": "node test/test.js"
"test": "./node_modules/.bin/mocha test"
},
"repository": {
"type": "git",
@ -21,17 +21,15 @@
"url": "https://github.com/dlmanning/gulp-sass/issues"
},
"dependencies": {
"clone": "~0.1.18",
"gulp-util": "^3.0",
"map-stream": "~0.1",
"node-sass": "^2.0.1",
"node-sass": "^3.0.0",
"object-assign": "^2.0.0",
"through2": "^0.6.3",
"vinyl-sourcemaps-apply": "~0.1.1"
},
"devDependencies": {
"tape": "~2.3",
"concat-stream": "~1.4"
},
"jshintConfig": {
"laxcomma": true
"eslint": "^0.17.1",
"mocha": "^2.2.1",
"should": "^5.2.0"
}
}

2
test/expected/indent.css Normal file
View file

@ -0,0 +1,2 @@
body .div {
color: blue; }

View file

@ -1,8 +1,14 @@
body {
background: pink; }
footer {
background: red; }
.error, .badError {
border: #f00;
background: #fdd; }
.error.intrusion {
.error.intrusion, .intrusion.badError {
font-size: 1.3em;
font-weight: bold; }

49
test/lint.js Normal file
View file

@ -0,0 +1,49 @@
'use strict';
var eslint = require('eslint');
var should = require('should');
var cli = new eslint.CLIEngine();
var formatter = cli.getFormatter();
var report;
describe('code style guide', function() {
it('index.js should follow our lint style guide', function(done) {
report = cli.executeOnFiles(['index.js']);
if (report.errorCount > 0 || report.warningCount > 0) {
console.log(formatter(report.results));
}
should(report.errorCount).equal(0);
should(report.warningCount).equal(0);
done();
});
it('test/main.js should follow our lint style guide', function(done) {
report = cli.executeOnFiles(['test/main.js']);
if (report.errorCount > 0 || report.warningCount > 0) {
console.log(formatter(report.results));
}
should(report.errorCount).equal(0);
should(report.warningCount).equal(0);
done();
});
it('test/lint.js should follow our lint style guide', function(done) {
cli = new eslint.CLIEngine({
'rules': {
'no-console': 0
}
});
report = cli.executeOnFiles(['test/lint.js']);
if (report.errorCount > 0 || report.warningCount > 0) {
console.log(formatter(report.results));
}
should(report.errorCount).equal(0);
should(report.warningCount).equal(0);
done();
});
});

383
test/main.js Normal file
View file

@ -0,0 +1,383 @@
'use strict';
var should = require('should');
var gutil = require('gulp-util');
var path = require('path');
var fs = require('fs');
var sass = require('../index');
var createVinyl = function createVinyl(filename, contents) {
var base = path.join(__dirname, 'scss');
var filePath = path.join(base, filename);
return new gutil.File({
'cwd': __dirname,
'base': base,
'path': filePath,
'contents': contents || fs.readFileSync(filePath)
});
};
describe('gulp-sass -- async compile', function() {
it('should pass file when it isNull()', function(done) {
var stream = sass();
var emptyFile = {
'isNull': function () {
return true;
}
};
stream.on('data', function(data) {
data.should.equal(emptyFile);
done();
});
stream.write(emptyFile);
});
it('should emit error when file isStream()', function (done) {
var stream = sass();
var streamFile = {
'isNull': function () {
return false;
},
'isStream': function () {
return true;
}
};
stream.on('error', function(err) {
err.message.should.equal('Streaming not supported');
done();
});
stream.write(streamFile);
});
it('should compile a single sass file', function(done) {
var sassFile = createVinyl('mixins.scss');
var stream = sass();
stream.on('data', function(cssFile) {
should.exist(cssFile);
should.exist(cssFile.path);
should.exist(cssFile.relative);
should.exist(cssFile.contents);
String(cssFile.contents).should.equal(
fs.readFileSync(path.join(__dirname, 'expected/mixins.css'), 'utf8')
);
done();
});
stream.write(sassFile);
});
it('should compile multiple sass files', function(done) {
var files = [
createVinyl('mixins.scss'),
createVinyl('variables.scss')
];
var stream = sass();
var mustSee = files.length;
var expectedPath = 'expected/mixins.css';
stream.on('data', function(cssFile) {
should.exist(cssFile);
should.exist(cssFile.path);
should.exist(cssFile.relative);
should.exist(cssFile.contents);
if (cssFile.path.indexOf('variables') !== -1) {
expectedPath = 'expected/variables.css';
}
String(cssFile.contents).should.equal(
fs.readFileSync(path.join(__dirname, expectedPath), 'utf8')
);
mustSee--;
if (mustSee <= 0) {
done();
}
});
files.forEach(function (file) {
stream.write(file);
});
});
it('should compile files with partials in another folder', function(done) {
var sassFile = createVinyl('inheritance.scss');
var stream = sass();
stream.on('data', function(cssFile) {
should.exist(cssFile);
should.exist(cssFile.path);
should.exist(cssFile.relative);
should.exist(cssFile.contents);
String(cssFile.contents).should.equal(
fs.readFileSync(path.join(__dirname, 'expected/inheritance.css'), 'utf8')
);
done();
});
stream.write(sassFile);
});
it('should handle sass errors', function(done) {
var errorFile = createVinyl('error.scss');
var stream = sass();
stream.on('error', function(err) {
err.message.indexOf('property "font" must be followed by a \':\'').should.not.equal(-1);
done();
});
stream.write(errorFile);
});
it('should compile a single sass file if the file name has been changed in the stream', function(done) {
var sassFile = createVinyl('mixins.scss');
var stream;
// Transform file name
sassFile.path = path.join(path.join(__dirname, 'scss'), 'mixin--changed.scss');
stream = sass();
stream.on('data', function(cssFile) {
should.exist(cssFile);
should.exist(cssFile.path);
cssFile.path.split('/').pop().should.equal('mixin--changed.css');
should.exist(cssFile.relative);
should.exist(cssFile.contents);
String(cssFile.contents).should.equal(
fs.readFileSync(path.join(__dirname, 'expected/mixins.css'), 'utf8')
);
done();
});
stream.write(sassFile);
});
it('should preserve changes made in-stream to a Sass file', function(done) {
var sassFile = createVinyl('mixins.scss');
var stream;
// Transform file name
sassFile.contents = new Buffer('/* Added Dynamically */' + sassFile.contents.toString());
stream = sass();
stream.on('data', function(cssFile) {
should.exist(cssFile);
should.exist(cssFile.path);
should.exist(cssFile.relative);
should.exist(cssFile.contents);
String(cssFile.contents).should.equal('/* Added Dynamically */\n' +
fs.readFileSync(path.join(__dirname, 'expected/mixins.css'), 'utf8')
);
done();
});
stream.write(sassFile);
});
it('should work with gulp-sourcemaps', function(done) {
var sassFile = createVinyl('inheritance.scss');
// Expected sources are relative to file.base
var expectedSources = [
'includes/_cats.scss',
'includes/_dogs.sass',
'inheritance.scss'
];
var stream;
sassFile.sourceMap = '{' +
'"version": 3,' +
'"file": "scss/subdir/multilevelimport.scss",' +
'"names": [],' +
'"mappings": "",' +
'"sources": [ "scss/subdir/multilevelimport.scss" ],' +
'"sourcesContent": [ "@import ../inheritance;" ]' +
'}';
stream = sass();
stream.on('data', function(cssFile) {
should.exist(cssFile.sourceMap);
cssFile.sourceMap.sources.should.eql(expectedSources);
done();
});
stream.write(sassFile);
});
it('should compile a single indented sass file', function(done) {
var sassFile = createVinyl('indent.sass');
var stream = sass();
stream.on('data', function(cssFile) {
should.exist(cssFile);
should.exist(cssFile.path);
should.exist(cssFile.relative);
should.exist(cssFile.contents);
String(cssFile.contents).should.equal(
fs.readFileSync(path.join(__dirname, 'expected/indent.css'), 'utf8')
);
done();
});
stream.write(sassFile);
});
it('should parse files in sass and scss', function(done) {
var files = [
createVinyl('mixins.scss'),
createVinyl('indent.sass')
];
var stream = sass();
var mustSee = files.length;
var expectedPath = 'expected/mixins.css';
stream.on('data', function(cssFile) {
should.exist(cssFile);
should.exist(cssFile.path);
should.exist(cssFile.relative);
should.exist(cssFile.contents);
if (cssFile.path.indexOf('indent') !== -1) {
expectedPath = 'expected/indent.css';
}
String(cssFile.contents).should.equal(
fs.readFileSync(path.join(__dirname, expectedPath), 'utf8')
);
mustSee--;
if (mustSee <= 0) {
done();
}
});
files.forEach(function (file) {
stream.write(file);
});
});
});
describe('gulp-sass -- sync compile', function() {
it('should pass file when it isNull()', function(done) {
var stream = sass.sync();
var emptyFile = {
'isNull': function () {
return true;
}
};
stream.on('data', function(data) {
data.should.equal(emptyFile);
done();
});
stream.write(emptyFile);
});
it('should emit error when file isStream()', function (done) {
var stream = sass.sync();
var streamFile = {
'isNull': function () {
return false;
},
'isStream': function () {
return true;
}
};
stream.on('error', function(err) {
err.message.should.equal('Streaming not supported');
done();
});
stream.write(streamFile);
});
it('should compile a single sass file', function(done) {
var sassFile = createVinyl('mixins.scss');
var stream = sass.sync();
stream.on('data', function(cssFile) {
should.exist(cssFile);
should.exist(cssFile.path);
should.exist(cssFile.relative);
should.exist(cssFile.contents);
String(cssFile.contents).should.equal(
fs.readFileSync(path.join(__dirname, 'expected/mixins.css'), 'utf8')
);
done();
});
stream.write(sassFile);
});
it('should compile multiple sass files', function(done) {
var files = [
createVinyl('mixins.scss'),
createVinyl('variables.scss')
];
var stream = sass.sync();
var mustSee = files.length;
var expectedPath = 'expected/mixins.css';
stream.on('data', function(cssFile) {
should.exist(cssFile);
should.exist(cssFile.path);
should.exist(cssFile.relative);
should.exist(cssFile.contents);
if (cssFile.path.indexOf('variables') !== -1) {
expectedPath = 'expected/variables.css';
}
String(cssFile.contents).should.equal(
fs.readFileSync(path.join(__dirname, expectedPath), 'utf8')
);
mustSee--;
if (mustSee <= 0) {
done();
}
});
files.forEach(function (file) {
stream.write(file);
});
});
it('should compile files with partials in another folder', function(done) {
var sassFile = createVinyl('inheritance.scss');
var stream = sass.sync();
stream.on('data', function(cssFile) {
should.exist(cssFile);
should.exist(cssFile.path);
should.exist(cssFile.relative);
should.exist(cssFile.contents);
String(cssFile.contents).should.equal(
fs.readFileSync(path.join(__dirname, 'expected/inheritance.css'), 'utf8')
);
done();
});
stream.write(sassFile);
});
it('should handle sass errors', function(done) {
var errorFile = createVinyl('error.scss');
var stream = sass.sync();
stream.on('error', function(err) {
err.message.indexOf('property "font" must be followed by a \':\'').should.not.equal(-1);
done();
});
stream.write(errorFile);
});
it('should work with gulp-sourcemaps', function(done) {
var sassFile = createVinyl('inheritance.scss');
// Expected sources are relative to file.base
var expectedSources = [
'includes/_cats.scss',
'includes/_dogs.sass',
'inheritance.scss'
];
var stream;
sassFile.sourceMap = '{' +
'"version": 3,' +
'"file": "scss/subdir/multilevelimport.scss",' +
'"names": [],' +
'"mappings": "",' +
'"sources": [ "scss/subdir/multilevelimport.scss" ],' +
'"sourcesContent": [ "@import ../inheritance;" ]' +
'}';
stream = sass.sync();
stream.on('data', function(cssFile) {
should.exist(cssFile.sourceMap);
cssFile.sourceMap.sources.should.eql(expectedSources);
done();
});
stream.write(sassFile);
});
});

View file

@ -1,9 +0,0 @@
table.hl {
margin: 2em 0; }
table.hl td.ln {
text-align: right; }
li {
font-family: serif;
font-weight: bold;
font-size: 1.2em; }

3
test/scss/_partial.scss Normal file
View file

@ -0,0 +1,3 @@
body {
background: red;
}

3
test/scss/error.scss Normal file
View file

@ -0,0 +1,3 @@
body {
font 'Comic Sans';
}

View file

@ -1 +0,0 @@
@import "cats";

View file

@ -1,2 +0,0 @@
@import "cats";

View file

@ -2,5 +2,5 @@ $blue: #3bbfce;
$margin: 16px;
body {
background: pink;
background: pink;
}

View file

@ -0,0 +1,5 @@
$blue: #3bbfce;
$margin: 16px;
footer
background: red;

4
test/scss/indent.sass Normal file
View file

@ -0,0 +1,4 @@
$color: blue
body .div
color: $color

View file

@ -1,4 +1,5 @@
@import "includes/cats";
@import "includes/dogs";
.error {
border: #f00;
@ -13,4 +14,4 @@
.badError {
@extend .error;
border-width: 3px;
}
}

View file

@ -1,14 +0,0 @@
table.hl {
margin: 2em 0;
td.ln {
text-align: right;
}
}
li {
font: {
family: serif;
weight: bold;
size: 1.2em;
}
}

View file

@ -1 +0,0 @@
@import "../inheritance";

View file

@ -11,4 +11,4 @@ $margin: 16px;
padding: $margin / 2;
margin: $margin / 2;
border-color: $blue;
}
}

View file

@ -1,200 +0,0 @@
var assert = require('assert');
var gsass = require('../');
var gutil = require('gulp-util');
var fs = require('fs');
var path = require('path');
var test = require('tape');
function createVinyl(sassFileName, contents, base) {
base = base || path.join(__dirname, 'scss');
var filePath = path.join(base, sassFileName);
return new gutil.File({
cwd: __dirname,
base: base,
path: filePath,
contents: contents || fs.readFileSync(filePath)
});
}
test('pass file when isNull()', function (t) {
var stream = gsass();
var emptyFile = {
isNull: function () { return true; }
};
stream.on('data', function (data) {
t.equal(data, emptyFile);
t.end();
});
stream.write(emptyFile);
});
// test('emit error when file isStream()', function (t) {
// var stream = gsass();
// var streamFile = {
// isNull: function () { return false; },
// isStream: function () { return true; }
// };
// stream.on()
// });
test('compile a single sass file', function (t) {
var sassFile = createVinyl('mixins.scss');
var stream = gsass();
stream.on('data', function (cssFile) {
t.ok(cssFile, 'cssFile should exist');
t.ok(cssFile.path, 'cssFile.path should exist');
t.ok(cssFile.relative, 'cssFile.relative should exist');
t.ok(cssFile.contents, 'cssFile.contents should exist');
t.equal(cssFile.path, path.join(__dirname, 'scss', 'mixins.css'));
t.equal(
fs.readFileSync(path.join(__dirname, 'ref/mixins.css'), 'utf8'),
cssFile.contents.toString(),
'file compiles correctly to css'
);
t.end();
})
stream.write(sassFile);
});
test('compile a single sass file synchronously', function (t) {
var sassFile = createVinyl('mixins.scss');
var stream = gsass({sync: true});
stream.on('data', function (cssFile) {
t.ok(cssFile, 'cssFile should exist');
t.ok(cssFile.path, 'cssFile.path should exist');
t.ok(cssFile.relative, 'cssFile.relative should exist');
t.ok(cssFile.contents, 'cssFile.contents should exist');
t.equal(cssFile.path, path.join(__dirname, 'scss', 'mixins.css'));
t.equal(
fs.readFileSync(path.join(__dirname, 'ref/mixins.css'), 'utf8'),
cssFile.contents.toString(),
'file compiles correctly to css'
);
t.end();
})
stream.write(sassFile);
});
test('compile multiple sass files', function (t) {
var files = [
createVinyl('inheritance.scss'),
createVinyl('mixins.scss'),
createVinyl('nesting.scss'),
createVinyl('variables.scss')
];
t.plan(files.length * 4);
var stream = gsass();
stream.on('data', function (cssFile) {
t.ok(cssFile, 'cssFile exists');
t.ok(cssFile.path, 'cssFile.path exists');
t.ok(cssFile.relative, 'cssFile.relative exists');
t.ok(cssFile.contents, 'cssFile.contents exists');
});
files.forEach(function (file) {
stream.write(file);
});
});
test('compile multiple sass files with includePaths', function (t) {
var files = [
createVinyl('file1.scss', null, path.join(__dirname, 'scss', 'include-path-tests')),
createVinyl('file2.scss', null, path.join(__dirname, 'scss', 'include-path-tests'))
];
var options = {
includePaths: [path.resolve(__dirname, 'scss', 'includes')]
};
t.plan(files.length * 4);
var stream = gsass(options);
stream.on('data', function (cssFile) {
t.ok(cssFile, 'cssFile exists');
t.ok(cssFile.path, 'cssFile.path exists');
t.ok(cssFile.relative, 'cssFile.relative exists');
t.ok(cssFile.contents, 'cssFile.contents exists');
});
files.forEach(function (file) {
stream.write(file);
});
});
test('emit error on sass errors', function (t) {
var stream = gsass();
var errorFile = createVinyl('somefile.sass',
new Buffer('body { font \'Comic Sans\'; }'));
stream.on('error', function (err) {
t.equal(err.message,
'property "font" must be followed by a \':\''
);
t.end();
});
stream.write(errorFile);
});
test('emit error on sass errors when using sync true', function (t) {
var stream = gsass({sync: true});
var errorFile = createVinyl('somefile.sass',
new Buffer('body { font \'Comic Sans\'; }'));
stream.on('error', function (err) {
t.equal(err.message,
'property "font" must be followed by a \':\''
);
t.end();
});
stream.write(errorFile);
});
test('call custom error callback when opts.onError is given', function (t) {
var stream = gsass({ onError: function (err) {
t.equal(err.message,
'property "font" must be followed by a \':\''
);
t.end();
}});
var errorFile = createVinyl('somefile.sass',
new Buffer('body { font \'Comic Sans\'; }'));
stream.write(errorFile);
});
test('sourcemaps', function (t) {
var sassFile = createVinyl('subdir/multilevelimport.scss');
// Pretend sourcemap.init() happened by mimicking
// the object it would create.
sassFile.sourceMap = '{' +
'"version": 3,' +
'"file": "scss/subdir/multilevelimport.scss",' +
'"names": [],' +
'"mappings": "",' +
'"sources": [ "scss/subdir/multilevelimport.scss" ],' +
'"sourcesContent": [ "@import ../inheritance;" ]' +
'}';
// Expected sources are relative to file.base
var expectedSources = [
'includes/_cats.scss',
'inheritance.scss'
];
var stream = gsass();
stream.on('data', function (cssFile) {
t.deepEqual(
cssFile.sourceMap.sources,
expectedSources,
'sourcemap paths are relative to file.base'
);
t.end();
});
stream.write(sassFile);
});