diff --git a/lib/groot.js b/lib/groot.js index 986ce8b7..1d2ee529 100644 --- a/lib/groot.js +++ b/lib/groot.js @@ -3,14 +3,15 @@ ////////////////////////////// 'use strict'; -var gonzales = require('gonzales-pe'), - fm = require('front-matter'); +var gonzales = require('gonzales-pe'); +var fm = require('front-matter'); +var helpers = require('./helpers'); module.exports = function (text, syntax, filename) { var tree; // Run `.toString()` to allow Buffers to be passed in - text = text.toString(); + text = helpers.stripBom(text.toString()); // if we're skipping front matter do it here, fall back to just our text in case it fails if (fm.test(text)) { diff --git a/lib/helpers.js b/lib/helpers.js index d2cb3306..0b9af3ce 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -394,4 +394,24 @@ helpers.isPartialStringMatch = function (needle, haystack) { return false; }; +/** + * A copy of the the stripBom module from https://github.com/sindresorhus/strip-bom/blob/master/index.js + * The module requires node > 4 whereas we support earlier versions. + * This function strips the BOM marker from the beginning of a file + * + * @param {string} str - The string we wish to strip the BOM marker from + * @returns {string} The string without a BOM marker + */ +helpers.stripBom = function (str) { + if (typeof str !== 'string') { + throw new TypeError('Expected a string, got ' + typeof str); + } + + if (str.charCodeAt(0) === 0xFEFF) { + return str.slice(1); + } + + return str; +}; + module.exports = helpers; diff --git a/tests/bom-utf8/starts-with-mixin-utf8-bom.scss b/tests/bom-utf8/starts-with-mixin-utf8-bom.scss new file mode 100644 index 00000000..4f12e183 --- /dev/null +++ b/tests/bom-utf8/starts-with-mixin-utf8-bom.scss @@ -0,0 +1,5 @@ +@mixin foo( + $param: 'def' +) { + color: red; +} diff --git a/tests/bom-utf8/var-utf8-bom.scss b/tests/bom-utf8/var-utf8-bom.scss new file mode 100644 index 00000000..bc5580da --- /dev/null +++ b/tests/bom-utf8/var-utf8-bom.scss @@ -0,0 +1,5 @@ +$my-color: red; + +body { + color: $my-color; +} \ No newline at end of file diff --git a/tests/cli.js b/tests/cli.js index 8df2996e..df7c7fef 100644 --- a/tests/cli.js +++ b/tests/cli.js @@ -472,3 +472,43 @@ describe('cli', function () { }); }); }); + +// ============================================================================== +// UTF-8 BOM +// ============================================================================== + +describe('Reading files with UTF-8 BOM', function () { + before(function () { + var testText = fs.readFileSync('tests/bom-utf8/starts-with-mixin-utf8-bom.scss').toString(); + if (testText.charCodeAt(0) !== 0xFEFF) { + throw new Error('BOM test files have no BOM markers'); + } + }); + + it('should not throw a parse error from file containing a BOM', function (done) { + var command = 'node bin/sass-lint -v tests/bom-utf8/starts-with-mixin-utf8-bom.scss --format json'; + + exec(command, function (err, stdout) { // eslint-disable-line handle-callback-err + var result = JSON.parse(stdout)[0]; + + // Files with BOM markers that start with a mixin throw a fatal error + // https://github.com/sasstools/sass-lint/issues/880 + assert.equal(result.errorCount, 0); + done(); + }); + }); + + it('should return the correct amount of warnings from a file containing BOM markers', function (done) { + var command = 'node bin/sass-lint -v tests/bom-utf8/var-utf8-bom.scss --format json'; + + exec(command, function (err, stdout) { // eslint-disable-line handle-callback-err + var result = JSON.parse(stdout)[0]; + + // Files starting with variables threw extra errors + // see https://github.com/sasstools/sass-lint/issues/880 + assert.equal(result.errorCount, 0); + assert.equal(result.warningCount, 2); + done(); + }); + }); +}); diff --git a/tests/helpers/stripBom.js b/tests/helpers/stripBom.js new file mode 100644 index 00000000..523e0266 --- /dev/null +++ b/tests/helpers/stripBom.js @@ -0,0 +1,28 @@ +'use strict'; + +var assert = require('assert'), + helpers = require('../../lib/helpers'), + fs = require('fs'); + +describe('helpers - stripBom', function () { + + ////////////////////////////// + // Strip BOM + ////////////////////////////// + + it('should remove the BOM marker', function (done) { + var file = fs.readFileSync('tests/bom-utf8/starts-with-mixin-utf8-bom.scss').toString(); + assert.equal(file.charCodeAt(0), 0xFEFF); + assert.notEqual(helpers.stripBom(file).charCodeAt(0), 0xFEFF); + done(); + }); + + it('should throw an error if not passed a string', function (done) { + assert.throws( + function () { + helpers.stripBom(8); + }, Error + ); + done(); + }); +});