Merge pull request #667 from dlmanning/next

Drop support for non-Active LTS and Current releases
This commit is contained in:
Michael Mifsud 2018-04-05 16:02:00 +10:00 committed by GitHub
commit 44be409bd9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 381 additions and 569 deletions

156
.eslintrc
View file

@ -4,159 +4,9 @@ env:
mocha: true mocha: true
node: 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: rules:
######################### max-len:
## 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 - 2
- last - 120
# Enforces one true `this` variable extends: "airbnb-base"
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
keyword-spacing:
- 2
- after: 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
# Disallow or enforce spaces inside of curly braces in objects.
object-curly-spacing:
- 2
- always
# Disallow or enforce spaces inside of brackets.
array-bracket-spacing:
- 2
- never
# Disallow or enforce spaces inside of computed properties.
computed-property-spacing:
- 2
- never

View file

@ -1,6 +1,8 @@
language: node_js
node_js:
- "0.10"
- node
- iojs
sudo: false sudo: false
language: node_js
node_js:
- lts/boron
- lts/carbon
- current

View file

@ -4,6 +4,12 @@ Sass plugin for [Gulp](https://github.com/gulpjs/gulp).
**_Before filing an issue, please make sure you have [Updated to the latest Gulp Sass](https://github.com/dlmanning/gulp-sass/wiki/Update-to-the-latest-Gulp-Sass) and have gone through our [Common Issues and Their Fixes](https://github.com/dlmanning/gulp-sass/wiki/Common-Issues-and-Their-Fixes) section._** **_Before filing an issue, please make sure you have [Updated to the latest Gulp Sass](https://github.com/dlmanning/gulp-sass/wiki/Update-to-the-latest-Gulp-Sass) and have gone through our [Common Issues and Their Fixes](https://github.com/dlmanning/gulp-sass/wiki/Common-Issues-and-Their-Fixes) section._**
# Support
Only [Active LTS and Current releases][1] are supported.
[1]: https://github.com/nodejs/Release#release-schedule
# Install # Install
``` ```
@ -52,7 +58,7 @@ gulp.task('sass:watch', function () {
## Options ## Options
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`. Except for the `data` option which is used by gulp-sass internally. Using the `file` option is also unsupported and results in undefined behaviour that may change without notice. 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`. Except for the `data` option which is used by gulp-sass internally. Using the `file` option is also unsupported and results in undefined behaviour that may change without notice.
For example: For example:

View file

@ -11,12 +11,9 @@ platform:
environment: environment:
matrix: matrix:
- nodejs_version: 0.10 - nodejs_version: lts/boron
- nodejs_version: 0.12 - nodejs_version: lts/carbon
- nodejs_version: 4 - nodejs_version: current
- nodejs_version: 6
- nodejs_version: 8
- nodejs_version: 9
install: install:
- ps: Install-Product node $env:nodejs_version $env:platform - ps: Install-Product node $env:nodejs_version $env:platform

279
index.js
View file

@ -1,183 +1,158 @@
'use strict'; const chalk = require('chalk');
const PluginError = require('plugin-error');
const replaceExtension = require('replace-ext');
const stripAnsi = require('strip-ansi');
const through = require('through2');
const clonedeep = require('lodash.clonedeep');
const path = require('path');
const applySourceMap = require('vinyl-sourcemaps-apply');
var gutil = require('gulp-util'); const PLUGIN_NAME = 'gulp-sass';
var through = require('through2');
var clonedeep = require('lodash.clonedeep');
var path = require('path');
var applySourceMap = require('vinyl-sourcemaps-apply');
var PLUGIN_NAME = 'gulp-sass';
////////////////////////////// //////////////////////////////
// Main Gulp Sass function // Main Gulp Sass function
////////////////////////////// //////////////////////////////
var gulpSass = function gulpSass(options, sync) { const gulpSass = (options, sync) => through.obj((file, enc, cb) => { // eslint-disable-line consistent-return
return through.obj(function(file, enc, cb) { if (file.isNull()) {
var opts, return cb(null, file);
filePush, }
errorM,
callback,
result;
if (file.isNull()) { if (file.isStream()) {
return cb(null, file); return cb(new PluginError(PLUGIN_NAME, 'Streaming not supported'));
} }
if (file.isStream()) {
return cb(new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported')); if (path.basename(file.path).indexOf('_') === 0) {
} return cb();
if (path.basename(file.path).indexOf('_') === 0) { }
return cb();
} if (!file.contents.length) {
if (!file.contents.length) { file.path = replaceExtension(file.path, '.css'); // eslint-disable-line no-param-reassign
file.path = gutil.replaceExtension(file.path, '.css'); return cb(null, file);
return cb(null, file); }
const opts = clonedeep(options || {});
opts.data = file.contents.toString();
// we set the file path here so that libsass can correctly resolve import paths
opts.file = file.path;
// 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.unshift(path.dirname(file.path));
opts = clonedeep(options || {}); // Generate Source Maps if plugin source-map present
opts.data = file.contents.toString(); if (file.sourceMap) {
opts.sourceMap = file.path;
opts.omitSourceMapUrl = true;
opts.sourceMapContents = true;
}
// we set the file path here so that libsass can correctly resolve import paths //////////////////////////////
opts.file = file.path; // Handles returning the file to the stream
//////////////////////////////
const filePush = (sassObj) => {
let sassMap;
let sassMapFile;
let sassFileSrc;
let sassFileSrcPath;
let sourceFileIndex;
// Ensure `indentedSyntax` is true if a `.sass` file // Build Source Maps!
if (path.extname(file.path) === '.sass') { if (sassObj.map) {
opts.indentedSyntax = true; // Transform map into JSON
} sassMap = JSON.parse(sassObj.map.toString());
// Grab the stdout and transform it into stdin
// Ensure file's parent directory in the include path sassMapFile = sassMap.file.replace(/^stdout$/, 'stdin');
if (opts.includePaths) { // Grab the base file name that's being worked on
if (typeof opts.includePaths === 'string') { sassFileSrc = file.relative;
opts.includePaths = [opts.includePaths]; // Grab the path portion of the file that's being worked on
} sassFileSrcPath = path.dirname(sassFileSrc);
} if (sassFileSrcPath) {
else { // Prepend the path to all files in the sources array except the file that's being worked on
opts.includePaths = []; sourceFileIndex = sassMap.sources.indexOf(sassMapFile);
} sassMap.sources = sassMap.sources.map((source, index) => { // eslint-disable-line arrow-body-style
return index === sourceFileIndex ? source : path.join(sassFileSrcPath, source);
opts.includePaths.unshift(path.dirname(file.path));
// Generate Source Maps if plugin source-map present
if (file.sourceMap) {
opts.sourceMap = file.path;
opts.omitSourceMapUrl = true;
opts.sourceMapContents = true;
}
//////////////////////////////
// Handles returning the file to the stream
//////////////////////////////
filePush = function filePush(sassObj) {
var sassMap,
sassMapFile,
sassFileSrc,
sassFileSrcPath,
sourceFileIndex;
// 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.relative;
// Grab the path portion of the file that's being worked on
sassFileSrcPath = path.dirname(sassFileSrc);
if (sassFileSrcPath) {
// Prepend the path to all files in the sources array except the file that's being worked on
sourceFileIndex = sassMap.sources.indexOf(sassMapFile);
sassMap.sources = sassMap.sources.map(function(source, index) {
return (index === sourceFileIndex) ? source : path.join(sassFileSrcPath, source);
});
}
// Remove 'stdin' from souces and replace with filenames!
sassMap.sources = sassMap.sources.filter(function(src) {
if (src !== 'stdin') {
return src;
}
}); });
// Replace the map file with the original file name (but new extension)
sassMap.file = gutil.replaceExtension(sassFileSrc, '.css');
// Apply the map
applySourceMap(file, sassMap);
} }
file.contents = sassObj.css; // Remove 'stdin' from souces and replace with filenames!
file.path = gutil.replaceExtension(file.path, '.css'); sassMap.sources = sassMap.sources.filter(src => src !== 'stdin' && src);
cb(null, file); // Replace the map file with the original file name (but new extension)
}; sassMap.file = replaceExtension(sassFileSrc, '.css');
// Apply the map
////////////////////////////// applySourceMap(file, sassMap);
// Handles error message
//////////////////////////////
errorM = function errorM(error) {
var relativePath = '',
filePath = error.file === 'stdin' ? file.path : error.file,
message = '';
filePath = filePath ? filePath : file.path;
relativePath = path.relative(process.cwd(), filePath);
message += gutil.colors.underline(relativePath) + '\n';
message += error.formatted;
error.messageFormatted = message;
error.messageOriginal = error.message;
error.message = gutil.colors.stripColor(message);
error.relativePath = relativePath;
return cb(new gutil.PluginError(
PLUGIN_NAME, error
));
};
if (sync !== true) {
//////////////////////////////
// Async Sass render
//////////////////////////////
callback = function(error, obj) {
if (error) {
return errorM(error);
}
filePush(obj);
};
gulpSass.compiler.render(opts, callback);
} }
else {
//////////////////////////////
// Sync Sass render
//////////////////////////////
try {
result = gulpSass.compiler.renderSync(opts);
filePush(result); file.contents = sassObj.css; // eslint-disable-line no-param-reassign
} file.path = replaceExtension(file.path, '.css'); // eslint-disable-line no-param-reassign
catch (error) {
cb(null, file);
};
//////////////////////////////
// Handles error message
//////////////////////////////
const errorM = (error) => {
const filePath = (error.file === 'stdin' ? file.path : error.file) || file.path;
const relativePath = path.relative(process.cwd(), filePath);
const message = [chalk.underline(relativePath), error.formatted].join('\n');
error.messageFormatted = message; // eslint-disable-line no-param-reassign
error.messageOriginal = error.message; // eslint-disable-line no-param-reassign
error.message = stripAnsi(message); // eslint-disable-line no-param-reassign
error.relativePath = relativePath; // eslint-disable-line no-param-reassign
return cb(new PluginError(PLUGIN_NAME, error));
};
if (sync !== true) {
//////////////////////////////
// Async Sass render
//////////////////////////////
const callback = (error, obj) => { // eslint-disable-line consistent-return
if (error) {
return errorM(error); return errorM(error);
} }
filePush(obj);
};
gulpSass.compiler.render(opts, callback);
} else {
//////////////////////////////
// Sync Sass render
//////////////////////////////
try {
filePush(gulpSass.compiler.renderSync(opts));
} catch (error) {
return errorM(error);
} }
}); }
}; });
////////////////////////////// //////////////////////////////
// Sync Sass render // Sync Sass render
////////////////////////////// //////////////////////////////
gulpSass.sync = function sync(options) { gulpSass.sync = options => gulpSass(options, true);
return gulpSass(options, true);
};
////////////////////////////// //////////////////////////////
// Log errors nicely // Log errors nicely
////////////////////////////// //////////////////////////////
gulpSass.logError = function logError(error) { gulpSass.logError = (error) => {
var message = new gutil.PluginError('sass', error.messageFormatted).toString(); const message = new PluginError('sass', error.messageFormatted).toString();
process.stderr.write(message + '\n'); process.stderr.write(`${message}\n`);
this.emit('end'); this.emit('end');
}; };

View file

@ -3,6 +3,9 @@
"version": "3.2.1", "version": "3.2.1",
"description": "Gulp plugin for sass", "description": "Gulp plugin for sass",
"main": "index.js", "main": "index.js",
"engines": {
"node": ">=6"
},
"scripts": { "scripts": {
"test": "./node_modules/.bin/mocha test" "test": "./node_modules/.bin/mocha test"
}, },
@ -21,22 +24,28 @@
"url": "https://github.com/dlmanning/gulp-sass/issues" "url": "https://github.com/dlmanning/gulp-sass/issues"
}, },
"dependencies": { "dependencies": {
"gulp-util": "^3.0", "chalk": "^2.3.0",
"lodash.clonedeep": "^4.3.2", "lodash.clonedeep": "^4.3.2",
"node-sass": "^4.8.3", "node-sass": "^4.8.3",
"plugin-error": "^1.0.1",
"replace-ext": "^1.0.0",
"strip-ansi": "^4.0.0",
"through2": "^2.0.0", "through2": "^2.0.0",
"vinyl-sourcemaps-apply": "^0.2.0" "vinyl-sourcemaps-apply": "^0.2.0"
}, },
"devDependencies": { "devDependencies": {
"autoprefixer-core": "^5.2.1", "autoprefixer": "^8.1.0",
"eslint": "^2.9.0", "eslint": "^4.18.2",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "^2.9.0",
"globule": "^1.0.0", "globule": "^1.0.0",
"gulp": "^3.8.11", "gulp": "^3.8.11",
"gulp-postcss": "^5.1.10", "gulp-postcss": "^7.0.1",
"gulp-sourcemaps": "^1.5.2", "gulp-sourcemaps": "^2.6.4",
"gulp-tap": "^0.1.3", "gulp-tap": "^0.1.3",
"mocha": "^2.2.1", "mocha": "^5.0.4",
"rimraf": "^2.4.3", "rimraf": "^2.4.3",
"should": "^8.3.1" "should": "^13.2.1",
"vinyl": "^2.1.0"
} }
} }

View file

@ -1,16 +1,12 @@
'use strict'; const eslint = require('eslint');
const should = require('should');
var eslint = require('eslint'); describe('code style guide', () => {
var should = require('should'); it('index.js should follow our lint style guide', (done) => {
const cli = new eslint.CLIEngine({ rules: { 'spaced-comment': 0 } });
const formatter = cli.getFormatter();
const report = cli.executeOnFiles(['index.js']);
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) { if (report.errorCount > 0 || report.warningCount > 0) {
console.log(formatter(report.results)); console.log(formatter(report.results));
} }
@ -20,8 +16,11 @@ describe('code style guide', function() {
done(); done();
}); });
it('test/main.js should follow our lint style guide', function(done) { it('test/main.js should follow our lint style guide', (done) => {
report = cli.executeOnFiles(['test/main.js']); const cli = new eslint.CLIEngine();
const formatter = cli.getFormatter();
const report = cli.executeOnFiles(['test/main.js']);
if (report.errorCount > 0 || report.warningCount > 0) { if (report.errorCount > 0 || report.warningCount > 0) {
console.log(formatter(report.results)); console.log(formatter(report.results));
} }
@ -31,13 +30,11 @@ describe('code style guide', function() {
done(); done();
}); });
it('test/lint.js should follow our lint style guide', function(done) { it('test/lint.js should follow our lint style guide', (done) => {
cli = new eslint.CLIEngine({ const cli = new eslint.CLIEngine({ rules: { 'no-console': 0 } });
'rules': { const formatter = cli.getFormatter();
'no-console': 0 const report = cli.executeOnFiles(['test/lint.js']);
}
});
report = cli.executeOnFiles(['test/lint.js']);
if (report.errorCount > 0 || report.warningCount > 0) { if (report.errorCount > 0 || report.warningCount > 0) {
console.log(formatter(report.results)); console.log(formatter(report.results));
} }

View file

@ -1,121 +1,107 @@
'use strict'; const should = require('should');
const Vinyl = require('vinyl');
const path = require('path');
const fs = require('fs');
const sass = require('../index');
const rimraf = require('rimraf');
const gulp = require('gulp');
const sourcemaps = require('gulp-sourcemaps');
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const tap = require('gulp-tap');
const globule = require('globule');
var should = require('should'); const createVinyl = (filename, contents) => {
var gutil = require('gulp-util'); const base = path.join(__dirname, 'scss');
var path = require('path'); const filePath = path.join(base, filename);
var fs = require('fs');
var sass = require('../index');
var rimraf = require('rimraf');
var gulp = require('gulp');
var sourcemaps = require('gulp-sourcemaps');
var postcss = require('gulp-postcss');
var autoprefixer = require('autoprefixer-core');
var tap = require('gulp-tap');
var globule = require('globule');
var createVinyl = function createVinyl(filename, contents) { return new Vinyl({
var base = path.join(__dirname, 'scss'); cwd: __dirname,
var filePath = path.join(base, filename); base,
path: filePath,
return new gutil.File({ contents: contents || fs.readFileSync(filePath),
'cwd': __dirname,
'base': base,
'path': filePath,
'contents': contents || fs.readFileSync(filePath)
}); });
}; };
var normaliseEOL = function(str) { const normaliseEOL = str => str.toString('utf8').replace(/\r\n/g, '\n');
if (typeof(str) === 'object') {
str = str.toString('utf8');
}
return str.replace(/\r\n/g, '\n'); describe('test helpers', () => {
} it('should normalise EOL', (done) => {
describe('test helpers', function() {
it('should normalise EOL', function(done) {
should.equal(normaliseEOL('foo\r\nbar'), 'foo\nbar'); should.equal(normaliseEOL('foo\r\nbar'), 'foo\nbar');
should.equal(normaliseEOL('foo\nbar'), 'foo\nbar'); should.equal(normaliseEOL('foo\nbar'), 'foo\nbar');
done(); done();
}); });
}); });
describe('gulp-sass -- async compile', function() { describe('gulp-sass -- async compile', () => {
it('should pass file when it isNull()', function(done) { it('should pass file when it isNull()', (done) => {
var stream = sass(); const stream = sass();
var emptyFile = { const emptyFile = {
'isNull': function () { isNull: () => true,
return true;
}
}; };
stream.on('data', function(data) { stream.on('data', (data) => {
data.should.equal(emptyFile); data.should.equal(emptyFile);
done(); done();
}); });
stream.write(emptyFile); stream.write(emptyFile);
}); });
it('should emit error when file isStream()', function (done) { it('should emit error when file isStream()', (done) => {
var stream = sass(); const stream = sass();
var streamFile = { const streamFile = {
'isNull': function () { isNull: () => false,
return false; isStream: () => true,
},
'isStream': function () {
return true;
}
}; };
stream.on('error', function(err) { stream.on('error', (err) => {
err.message.should.equal('Streaming not supported'); err.message.should.equal('Streaming not supported');
done(); done();
}); });
stream.write(streamFile); stream.write(streamFile);
}); });
it('should compile an empty sass file', function(done) { it('should compile an empty sass file', (done) => {
var sassFile = createVinyl('empty.scss'); const sassFile = createVinyl('empty.scss');
var stream = sass(); const stream = sass();
stream.on('data', function(cssFile) { stream.on('data', (cssFile) => {
should.exist(cssFile); should.exist(cssFile);
should.exist(cssFile.path); should.exist(cssFile.path);
should.exist(cssFile.relative); should.exist(cssFile.relative);
should.exist(cssFile.contents); should.exist(cssFile.contents);
should.equal(path.basename(cssFile.path), 'empty.css'); should.equal(path.basename(cssFile.path), 'empty.css');
String(normaliseEOL(cssFile.contents)).should.equal(
normaliseEOL(fs.readFileSync(path.join(__dirname, 'expected', 'empty.css'), 'utf8')) const actual = fs.readFileSync(path.join(__dirname, 'expected', 'empty.css'), 'utf8');
); String(normaliseEOL(cssFile.contents)).should.equal(normaliseEOL(actual));
done(); done();
}); });
stream.write(sassFile); stream.write(sassFile);
}); });
it('should compile a single sass file', function(done) { it('should compile a single sass file', (done) => {
var sassFile = createVinyl('mixins.scss'); const sassFile = createVinyl('mixins.scss');
var stream = sass(); const stream = sass();
stream.on('data', function(cssFile) { stream.on('data', (cssFile) => {
should.exist(cssFile); should.exist(cssFile);
should.exist(cssFile.path); should.exist(cssFile.path);
should.exist(cssFile.relative); should.exist(cssFile.relative);
should.exist(cssFile.contents); should.exist(cssFile.contents);
String(normaliseEOL(cssFile.contents)).should.equal(
normaliseEOL(fs.readFileSync(path.join(__dirname, 'expected', 'mixins.css'), 'utf8')) const actual = fs.readFileSync(path.join(__dirname, 'expected', 'mixins.css'), 'utf8');
); String(normaliseEOL(cssFile.contents)).should.equal(normaliseEOL(actual));
done(); done();
}); });
stream.write(sassFile); stream.write(sassFile);
}); });
it('should compile multiple sass files', function(done) { it('should compile multiple sass files', (done) => {
var files = [ const files = [
createVinyl('mixins.scss'), createVinyl('mixins.scss'),
createVinyl('variables.scss') createVinyl('variables.scss'),
]; ];
var stream = sass(); const stream = sass();
var mustSee = files.length; let mustSee = files.length;
var expectedPath = path.join('expected', 'mixins.css'); let expectedPath = path.join('expected', 'mixins.css');
stream.on('data', function(cssFile) { stream.on('data', (cssFile) => {
should.exist(cssFile); should.exist(cssFile);
should.exist(cssFile.path); should.exist(cssFile.path);
should.exist(cssFile.relative); should.exist(cssFile.relative);
@ -123,41 +109,42 @@ describe('gulp-sass -- async compile', function() {
if (cssFile.path.indexOf('variables') !== -1) { if (cssFile.path.indexOf('variables') !== -1) {
expectedPath = path.join('expected', 'variables.css'); expectedPath = path.join('expected', 'variables.css');
} }
String(normaliseEOL(cssFile.contents)).should.equal(
normaliseEOL(fs.readFileSync(path.join(__dirname, expectedPath), 'utf8')) const actual = fs.readFileSync(path.join(__dirname, expectedPath), 'utf8');
); String(normaliseEOL(cssFile.contents)).should.equal(normaliseEOL(actual));
mustSee--;
mustSee -= 1;
if (mustSee <= 0) { if (mustSee <= 0) {
done(); done();
} }
}); });
files.forEach(function (file) { files.forEach((file) => {
stream.write(file); stream.write(file);
}); });
}); });
it('should compile files with partials in another folder', function(done) { it('should compile files with partials in another folder', (done) => {
var sassFile = createVinyl('inheritance.scss'); const sassFile = createVinyl('inheritance.scss');
var stream = sass(); const stream = sass();
stream.on('data', function(cssFile) { stream.on('data', (cssFile) => {
should.exist(cssFile); should.exist(cssFile);
should.exist(cssFile.path); should.exist(cssFile.path);
should.exist(cssFile.relative); should.exist(cssFile.relative);
should.exist(cssFile.contents); should.exist(cssFile.contents);
String(normaliseEOL(cssFile.contents)).should.equal(
normaliseEOL(fs.readFileSync(path.join(__dirname, 'expected', 'inheritance.css'), 'utf8')) const actual = fs.readFileSync(path.join(__dirname, 'expected', 'inheritance.css'), 'utf8');
); String(normaliseEOL(cssFile.contents)).should.equal(normaliseEOL(actual));
done(); done();
}); });
stream.write(sassFile); stream.write(sassFile);
}); });
it('should handle sass errors', function(done) { it('should handle sass errors', (done) => {
var errorFile = createVinyl('error.scss'); const errorFile = createVinyl('error.scss');
var stream = sass(); const stream = sass();
stream.on('error', function(err) { stream.on('error', (err) => {
// Error must include message body // Error must include message body
err.message.indexOf('property "font" must be followed by a \':\'').should.not.equal(-1); err.message.indexOf('property "font" must be followed by a \':\'').should.not.equal(-1);
// Error must include file error occurs in // Error must include file error occurs in
@ -171,11 +158,11 @@ describe('gulp-sass -- async compile', function() {
stream.write(errorFile); stream.write(errorFile);
}); });
it('should preserve the original sass error message', function(done) { it('should preserve the original sass error message', (done) => {
var errorFile = createVinyl('error.scss'); const errorFile = createVinyl('error.scss');
var stream = sass(); const stream = sass();
stream.on('error', function(err) { stream.on('error', (err) => {
// Error must include original error message // Error must include original error message
err.messageOriginal.indexOf('property "font" must be followed by a \':\'').should.not.equal(-1); err.messageOriginal.indexOf('property "font" must be followed by a \':\'').should.not.equal(-1);
// Error must not format or change the original error message // Error must not format or change the original error message
@ -185,60 +172,48 @@ describe('gulp-sass -- async compile', function() {
stream.write(errorFile); stream.write(errorFile);
}); });
it('should compile a single sass file if the file name has been changed in the stream', function(done) { it('should compile a single sass file if the file name has been changed in the stream', (done) => {
var sassFile = createVinyl('mixins.scss'); const sassFile = createVinyl('mixins.scss');
var stream;
// Transform file name // Transform file name
sassFile.path = path.join(path.join(__dirname, 'scss'), 'mixin--changed.scss'); sassFile.path = path.join(path.join(__dirname, 'scss'), 'mixin--changed.scss');
stream = sass(); const stream = sass();
stream.on('data', function(cssFile) { stream.on('data', (cssFile) => {
should.exist(cssFile); should.exist(cssFile);
should.exist(cssFile.path); should.exist(cssFile.path);
cssFile.path.split(path.sep).pop().should.equal('mixin--changed.css'); cssFile.path.split(path.sep).pop().should.equal('mixin--changed.css');
should.exist(cssFile.relative); should.exist(cssFile.relative);
should.exist(cssFile.contents); should.exist(cssFile.contents);
String(normaliseEOL(cssFile.contents)).should.equal(
normaliseEOL(fs.readFileSync(path.join(__dirname, 'expected', 'mixins.css'), 'utf8')) const actual = fs.readFileSync(path.join(__dirname, 'expected', 'mixins.css'), 'utf8');
); String(normaliseEOL(cssFile.contents)).should.equal(normaliseEOL(actual));
done(); done();
}); });
stream.write(sassFile); stream.write(sassFile);
}); });
it('should preserve changes made in-stream to a Sass file', function(done) { it('should preserve changes made in-stream to a Sass file', (done) => {
var sassFile = createVinyl('mixins.scss'); const sassFile = createVinyl('mixins.scss');
var stream;
// Transform file name // Transform file name
sassFile.contents = new Buffer('/* Added Dynamically */' + sassFile.contents.toString()); sassFile.contents = Buffer.from(`/* Added Dynamically */${sassFile.contents.toString()}`);
stream = sass(); const stream = sass();
stream.on('data', function(cssFile) { stream.on('data', (cssFile) => {
should.exist(cssFile); should.exist(cssFile);
should.exist(cssFile.path); should.exist(cssFile.path);
should.exist(cssFile.relative); should.exist(cssFile.relative);
should.exist(cssFile.contents); should.exist(cssFile.contents);
String(normaliseEOL(cssFile.contents)).should.equal('/* Added Dynamically */\n' +
normaliseEOL(fs.readFileSync(path.join(__dirname, 'expected', 'mixins.css'), 'utf8')) const actual = fs.readFileSync(path.join(__dirname, 'expected', 'mixins.css'), 'utf8');
); String(normaliseEOL(cssFile.contents))
.should.equal(`/* Added Dynamically */\n${normaliseEOL(actual)}`);
done(); done();
}); });
stream.write(sassFile); stream.write(sassFile);
}); });
it('should work with gulp-sourcemaps', function(done) { it('should work with gulp-sourcemaps', (done) => {
var sassFile = createVinyl('inheritance.scss'); const sassFile = createVinyl('inheritance.scss');
// Expected sources are relative to file.base
var expectedSources = [
'inheritance.scss',
'includes/_cats.scss',
'includes/_dogs.sass',
];
var stream;
sassFile.sourceMap = '{' + sassFile.sourceMap = '{' +
'"version": 3,' + '"version": 3,' +
@ -249,8 +224,15 @@ describe('gulp-sass -- async compile', function() {
'"sourcesContent": [ "@import ../inheritance;" ]' + '"sourcesContent": [ "@import ../inheritance;" ]' +
'}'; '}';
stream = sass(); // Expected sources are relative to file.base
stream.on('data', function(cssFile) { const expectedSources = [
'inheritance.scss',
'includes/_cats.scss',
'includes/_dogs.sass',
];
const stream = sass();
stream.on('data', (cssFile) => {
should.exist(cssFile.sourceMap); should.exist(cssFile.sourceMap);
cssFile.sourceMap.sources.should.eql(expectedSources); cssFile.sourceMap.sources.should.eql(expectedSources);
done(); done();
@ -258,32 +240,32 @@ describe('gulp-sass -- async compile', function() {
stream.write(sassFile); stream.write(sassFile);
}); });
it('should compile a single indented sass file', function(done) { it('should compile a single indented sass file', (done) => {
var sassFile = createVinyl('indent.sass'); const sassFile = createVinyl('indent.sass');
var stream = sass(); const stream = sass();
stream.on('data', function(cssFile) { stream.on('data', (cssFile) => {
should.exist(cssFile); should.exist(cssFile);
should.exist(cssFile.path); should.exist(cssFile.path);
should.exist(cssFile.relative); should.exist(cssFile.relative);
should.exist(cssFile.contents); should.exist(cssFile.contents);
String(normaliseEOL(cssFile.contents)).should.equal(
normaliseEOL(fs.readFileSync(path.join(__dirname, 'expected', 'indent.css'), 'utf8')) const actual = fs.readFileSync(path.join(__dirname, 'expected', 'indent.css'), 'utf8');
); String(normaliseEOL(cssFile.contents)).should.equal(normaliseEOL(actual));
done(); done();
}); });
stream.write(sassFile); stream.write(sassFile);
}); });
it('should parse files in sass and scss', function(done) { it('should parse files in sass and scss', (done) => {
var files = [ const files = [
createVinyl('mixins.scss'), createVinyl('mixins.scss'),
createVinyl('indent.sass') createVinyl('indent.sass'),
]; ];
var stream = sass(); const stream = sass();
var mustSee = files.length; let mustSee = files.length;
var expectedPath = path.join('expected', 'mixins.css'); let expectedPath = path.join('expected', 'mixins.css');
stream.on('data', function(cssFile) { stream.on('data', (cssFile) => {
should.exist(cssFile); should.exist(cssFile);
should.exist(cssFile.path); should.exist(cssFile.path);
should.exist(cssFile.relative); should.exist(cssFile.relative);
@ -291,125 +273,123 @@ describe('gulp-sass -- async compile', function() {
if (cssFile.path.indexOf('indent') !== -1) { if (cssFile.path.indexOf('indent') !== -1) {
expectedPath = path.join('expected', 'indent.css'); expectedPath = path.join('expected', 'indent.css');
} }
String(normaliseEOL(cssFile.contents)).should.equal(
normaliseEOL(fs.readFileSync(path.join(__dirname, expectedPath), 'utf8')) const actual = fs.readFileSync(path.join(__dirname, expectedPath), 'utf8');
); String(normaliseEOL(cssFile.contents)).should.equal(normaliseEOL(actual));
mustSee--;
mustSee -= 1;
if (mustSee <= 0) { if (mustSee <= 0) {
done(); done();
} }
}); });
files.forEach(function (file) { files.forEach((file) => {
stream.write(file); stream.write(file);
}); });
}); });
}); });
describe('gulp-sass -- sync compile', function() { describe('gulp-sass -- sync compile', () => {
beforeEach(function(done) { beforeEach((done) => {
rimraf(path.join(__dirname, 'results'), done); rimraf(path.join(__dirname, 'results'), done);
}); });
it('should pass file when it isNull()', function(done) { it('should pass file when it isNull()', (done) => {
var stream = sass.sync(); const stream = sass.sync();
var emptyFile = { const emptyFile = {
'isNull': function () { isNull: () => true,
return true;
}
}; };
stream.on('data', function(data) { stream.on('data', (data) => {
data.should.equal(emptyFile); data.should.equal(emptyFile);
done(); done();
}); });
stream.write(emptyFile); stream.write(emptyFile);
}); });
it('should emit error when file isStream()', function (done) { it('should emit error when file isStream()', (done) => {
var stream = sass.sync(); const stream = sass.sync();
var streamFile = { const streamFile = {
'isNull': function () { isNull: () => false,
return false; isStream: () => true,
},
'isStream': function () {
return true;
}
}; };
stream.on('error', function(err) { stream.on('error', (err) => {
err.message.should.equal('Streaming not supported'); err.message.should.equal('Streaming not supported');
done(); done();
}); });
stream.write(streamFile); stream.write(streamFile);
}); });
it('should compile a single sass file', function(done) { it('should compile a single sass file', (done) => {
var sassFile = createVinyl('mixins.scss'); const sassFile = createVinyl('mixins.scss');
var stream = sass.sync(); const stream = sass.sync();
stream.on('data', function(cssFile) { stream.on('data', (cssFile) => {
should.exist(cssFile); should.exist(cssFile);
should.exist(cssFile.path); should.exist(cssFile.path);
should.exist(cssFile.relative); should.exist(cssFile.relative);
should.exist(cssFile.contents); should.exist(cssFile.contents);
String(normaliseEOL(cssFile.contents)).should.equal(
normaliseEOL(fs.readFileSync(path.join(__dirname, 'expected', 'mixins.css'), 'utf8')) const actual = fs.readFileSync(path.join(__dirname, 'expected', 'mixins.css'), 'utf8');
); String(normaliseEOL(cssFile.contents)).should.equal(normaliseEOL(actual));
done(); done();
}); });
stream.write(sassFile); stream.write(sassFile);
}); });
it('should compile multiple sass files', function(done) { it('should compile multiple sass files', (done) => {
var files = [ const files = [
createVinyl('mixins.scss'), createVinyl('mixins.scss'),
createVinyl('variables.scss') createVinyl('variables.scss'),
]; ];
var stream = sass.sync(); const stream = sass.sync();
var mustSee = files.length; let mustSee = files.length;
var expectedPath = path.join('expected', 'mixins.css'); let expectedPath = path.join('expected', 'mixins.css');
stream.on('data', function(cssFile) { stream.on('data', (cssFile) => {
should.exist(cssFile); should.exist(cssFile);
should.exist(cssFile.path); should.exist(cssFile.path);
should.exist(cssFile.relative); should.exist(cssFile.relative);
should.exist(cssFile.contents); should.exist(cssFile.contents);
if (cssFile.path.indexOf('variables') !== -1) { if (cssFile.path.indexOf('variables') !== -1) {
expectedPath = path.join('expected', 'variables.css'); expectedPath = path.join('expected', 'variables.css');
} }
String(normaliseEOL(cssFile.contents)).should.equal(
normaliseEOL(fs.readFileSync(path.join(__dirname, expectedPath), 'utf8')) const actual = normaliseEOL(fs.readFileSync(path.join(__dirname, expectedPath), 'utf8'));
); String(normaliseEOL(cssFile.contents)).should.equal(actual);
mustSee--;
mustSee -= 1;
if (mustSee <= 0) { if (mustSee <= 0) {
done(); done();
} }
}); });
files.forEach(function (file) { files.forEach((file) => {
stream.write(file); stream.write(file);
}); });
}); });
it('should compile files with partials in another folder', function(done) { it('should compile files with partials in another folder', (done) => {
var sassFile = createVinyl('inheritance.scss'); const sassFile = createVinyl('inheritance.scss');
var stream = sass.sync(); const stream = sass.sync();
stream.on('data', function(cssFile) {
stream.on('data', (cssFile) => {
should.exist(cssFile); should.exist(cssFile);
should.exist(cssFile.path); should.exist(cssFile.path);
should.exist(cssFile.relative); should.exist(cssFile.relative);
should.exist(cssFile.contents); should.exist(cssFile.contents);
String(normaliseEOL(cssFile.contents)).should.equal(
normaliseEOL(fs.readFileSync(path.join(__dirname, 'expected', 'inheritance.css'), 'utf8')) const actual = fs.readFileSync(path.join(__dirname, 'expected', 'inheritance.css'), 'utf8');
); String(normaliseEOL(cssFile.contents)).should.equal(normaliseEOL(actual));
done(); done();
}); });
stream.write(sassFile); stream.write(sassFile);
}); });
it('should handle sass errors', function(done) { it('should handle sass errors', (done) => {
var errorFile = createVinyl('error.scss'); const errorFile = createVinyl('error.scss');
var stream = sass.sync(); const stream = sass.sync();
stream.on('error', function(err) { stream.on('error', (err) => {
err.message.indexOf('property "font" must be followed by a \':\'').should.not.equal(-1); err.message.indexOf('property "font" must be followed by a \':\'').should.not.equal(-1);
err.relativePath.should.equal(path.join('test', 'scss', 'error.scss')); err.relativePath.should.equal(path.join('test', 'scss', 'error.scss'));
done(); done();
@ -417,18 +397,16 @@ describe('gulp-sass -- sync compile', function() {
stream.write(errorFile); stream.write(errorFile);
}); });
it('should work with gulp-sourcemaps', function(done) { it('should work with gulp-sourcemaps', (done) => {
var sassFile = createVinyl('inheritance.scss'); const sassFile = createVinyl('inheritance.scss');
// Expected sources are relative to file.base // Expected sources are relative to file.base
var expectedSources = [ const expectedSources = [
'inheritance.scss', 'inheritance.scss',
'includes/_cats.scss', 'includes/_cats.scss',
'includes/_dogs.sass', 'includes/_dogs.sass',
]; ];
var stream;
sassFile.sourceMap = '{' + sassFile.sourceMap = '{' +
'"version": 3,' + '"version": 3,' +
'"file": "scss/subdir/multilevelimport.scss",' + '"file": "scss/subdir/multilevelimport.scss",' +
@ -438,8 +416,8 @@ describe('gulp-sass -- sync compile', function() {
'"sourcesContent": [ "@import ../inheritance;" ]' + '"sourcesContent": [ "@import ../inheritance;" ]' +
'}'; '}';
stream = sass.sync(); const stream = sass.sync();
stream.on('data', function(cssFile) { stream.on('data', (cssFile) => {
should.exist(cssFile.sourceMap); should.exist(cssFile.sourceMap);
cssFile.sourceMap.sources.should.eql(expectedSources); cssFile.sourceMap.sources.should.eql(expectedSources);
done(); done();
@ -447,14 +425,14 @@ describe('gulp-sass -- sync compile', function() {
stream.write(sassFile); stream.write(sassFile);
}); });
it('should work with gulp-sourcemaps and autoprefixer', function(done) { it('should work with gulp-sourcemaps and autoprefixer', (done) => {
var expectedSourcesBefore = [ const expectedSourcesBefore = [
'inheritance.scss', 'inheritance.scss',
'includes/_cats.scss', 'includes/_cats.scss',
'includes/_dogs.sass', 'includes/_dogs.sass',
]; ];
var expectedSourcesAfter = [ const expectedSourcesAfter = [
'includes/_cats.scss', 'includes/_cats.scss',
'includes/_dogs.sass', 'includes/_dogs.sass',
'inheritance.scss', 'inheritance.scss',
@ -463,80 +441,78 @@ describe('gulp-sass -- sync compile', function() {
gulp.src(path.join(__dirname, 'scss', 'inheritance.scss')) gulp.src(path.join(__dirname, 'scss', 'inheritance.scss'))
.pipe(sourcemaps.init()) .pipe(sourcemaps.init())
.pipe(sass.sync()) .pipe(sass.sync())
.pipe(tap(function(file) { .pipe(tap((file) => {
should.exist(file.sourceMap); should.exist(file.sourceMap);
file.sourceMap.sources.should.eql(expectedSourcesBefore); file.sourceMap.sources.should.eql(expectedSourcesBefore);
})) }))
.pipe(postcss([autoprefixer()])) .pipe(postcss([autoprefixer()]))
.pipe(sourcemaps.write()) .pipe(sourcemaps.write())
.pipe(gulp.dest(path.join(__dirname, 'results'))) .pipe(gulp.dest(path.join(__dirname, 'results')))
.pipe(tap(function(file) { .pipe(tap((file) => {
should.exist(file.sourceMap); should.exist(file.sourceMap);
file.sourceMap.sources.should.eql(expectedSourcesAfter); file.sourceMap.sources.should.eql(expectedSourcesAfter);
})) }))
.on('end', done); .on('end', done);
}); });
it('should work with gulp-sourcemaps and a globbed source', function(done) { it('should work with gulp-sourcemaps and a globbed source', (done) => {
var files, filesContent, actualContent, expectedContent, globPath; const globPath = path.join(__dirname, 'scss', 'globbed');
globPath = path.join(__dirname, 'scss', 'globbed'); const files = globule.find(path.join(__dirname, 'scss', 'globbed', '**', '*.scss'));
files = globule.find(path.join(__dirname, 'scss', 'globbed', '**', '*.scss')); const filesContent = {};
filesContent = {};
files.forEach(function(file) { files.forEach((file) => {
var source = path.normalize(path.relative(globPath, file)); const source = path.normalize(path.relative(globPath, file));
filesContent[source] = fs.readFileSync(file, 'utf8'); filesContent[source] = fs.readFileSync(file, 'utf8');
}); });
gulp.src(path.join(__dirname, 'scss', 'globbed', '**', '*.scss')) gulp.src(path.join(__dirname, 'scss', 'globbed', '**', '*.scss'))
.pipe(sourcemaps.init()) .pipe(sourcemaps.init())
.pipe(sass.sync()) .pipe(sass.sync())
.pipe(tap(function(file) { .pipe(tap((file) => {
should.exist(file.sourceMap); should.exist(file.sourceMap);
actualContent = normaliseEOL(file.sourceMap.sourcesContent[0]); const actual = normaliseEOL(file.sourceMap.sourcesContent[0]);
expectedContent = normaliseEOL(filesContent[path.normalize(file.sourceMap.sources[0])]); const expected = normaliseEOL(filesContent[path.normalize(file.sourceMap.sources[0])]);
actualContent.should.eql(expectedContent); actual.should.eql(expected);
})) }))
.on('end', done); .on('end', done);
}); });
it('should work with gulp-sourcemaps and autoprefixer with different file.base', function(done) { it('should work with gulp-sourcemaps and autoprefixer with different file.base', (done) => {
var expectedSourcesBefore = [ const expectedSourcesBefore = [
'scss/inheritance.scss', 'scss/inheritance.scss',
'scss/includes/_cats.scss', 'scss/includes/_cats.scss',
'scss/includes/_dogs.sass' 'scss/includes/_dogs.sass',
]; ];
var expectedSourcesAfter = [ const expectedSourcesAfter = [
'scss/includes/_cats.scss', 'scss/includes/_cats.scss',
'scss/includes/_dogs.sass', 'scss/includes/_dogs.sass',
'scss/inheritance.scss' 'scss/inheritance.scss',
]; ];
gulp.src(path.join(__dirname, 'scss', 'inheritance.scss'), { 'base': 'test' }) gulp.src(path.join(__dirname, 'scss', 'inheritance.scss'), { base: 'test' })
.pipe(sourcemaps.init()) .pipe(sourcemaps.init())
.pipe(sass.sync()) .pipe(sass.sync())
.pipe(tap(function(file) { .pipe(tap((file) => {
should.exist(file.sourceMap); should.exist(file.sourceMap);
file.sourceMap.sources.should.eql(expectedSourcesBefore); file.sourceMap.sources.should.eql(expectedSourcesBefore);
})) }))
.pipe(postcss([autoprefixer()])) .pipe(postcss([autoprefixer()]))
.pipe(tap(function(file) { .pipe(tap((file) => {
should.exist(file.sourceMap); should.exist(file.sourceMap);
file.sourceMap.sources.should.eql(expectedSourcesAfter); file.sourceMap.sources.should.eql(expectedSourcesAfter);
})) }))
.on('end', done); .on('end', done);
}); });
it('should work with empty files', function(done) { it('should work with empty files', (done) => {
gulp.src(path.join(__dirname, 'scss', 'empty.scss')) gulp.src(path.join(__dirname, 'scss', 'empty.scss'))
.pipe(sass.sync()) .pipe(sass.sync())
.pipe(gulp.dest(path.join(__dirname, 'results'))) .pipe(gulp.dest(path.join(__dirname, 'results')))
.pipe(tap(function() { .pipe(tap(() => {
try { try {
fs.statSync(path.join(__dirname, 'results', 'empty.css')); fs.statSync(path.join(__dirname, 'results', 'empty.css'));
} } catch (e) {
catch (e) {
should.fail(false, true, 'Empty file was produced'); should.fail(false, true, 'Empty file was produced');
} }
})) }))