diff --git a/package.json b/package.json index 3cc7160..35cccd4 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "prepublish": "npm run compile" }, "dependencies": { + "is-color": "^0.2.0", "is-there": "^4.0.0", "lodash": "^3.10.1", "node-sass": "^3.0.0" diff --git a/src/index.js b/src/index.js index 21d9173..569706c 100644 --- a/src/index.js +++ b/src/index.js @@ -2,9 +2,10 @@ import _ from 'lodash'; import {resolve} from 'path'; import isThere from 'is-there'; import sass from 'node-sass'; +import isColor from 'is-color'; export default function(url, prev) { - if (!/\.json$/.test(url)) { + if (!/\.(json|js)$/.test(url)) { return sass.NULL; } @@ -26,11 +27,24 @@ export default function(url, prev) { // https://github.com/Updater/node-sass-json-importer/issues/21 delete require.cache[require.resolve(file)]; + var contents = require(file); + try { + if (/\.js$/.test(url)) { + contents = JSON.parse(JSON.stringify(contents)); + } + } catch(e) { + return sass.NULL; + } + return { - contents: parseJSON(require(file)) + contents: parseJSON(contents) }; } +function isCSSUnit(value) { + return /^\d*\.*\d+(em|ex|ch|rem|vh|vw|vmin|vmax|px|mm|cm|in|pt|pc|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)$/.test(value); +} + function parseJSON(json) { return Object.keys(json) .map(key => `$${key}: ${parseValue(json[key])};`) @@ -43,7 +57,7 @@ function parseValue(value) { } else if (_.isPlainObject(value)) { return parseMap(value); } else { - return value; + return parseEndValue(value); } } @@ -58,3 +72,11 @@ function parseMap(map) { .map(key => `${key}: ${parseValue(map[key])}`) .join(',')})`; } + +function parseEndValue(value) { + if (typeof value === 'string' && !isColor(value) && !isCSSUnit(value)) { + return JSON.stringify(value); + } + + return value; +} \ No newline at end of file diff --git a/test/fixtures/convert-strings/style-js.scss b/test/fixtures/convert-strings/style-js.scss new file mode 100644 index 0000000..eb6551c --- /dev/null +++ b/test/fixtures/convert-strings/style-js.scss @@ -0,0 +1,9 @@ +@import 'variables.js'; + +body { + content: $string; + color: $hex-color, $hsl-color, $rgba-color, $rgb-color, $css-color; + font-size: $em-unit; + margin-top: #{$number}px; + margin-bottom: #{$float}em; +} diff --git a/test/fixtures/convert-strings/style.scss b/test/fixtures/convert-strings/style.scss new file mode 100644 index 0000000..6e90c0b --- /dev/null +++ b/test/fixtures/convert-strings/style.scss @@ -0,0 +1,9 @@ +@import 'variables.json'; + +body { + content: $string; + color: $hex-color, $hsl-color, $rgba-color, $rgb-color, $css-color; + font-size: $em-unit; + margin-top: #{$number}px; + margin-bottom: #{$float}em; +} diff --git a/test/fixtures/convert-strings/variables.js b/test/fixtures/convert-strings/variables.js new file mode 100644 index 0000000..8eec4fa --- /dev/null +++ b/test/fixtures/convert-strings/variables.js @@ -0,0 +1,12 @@ +module.exports = { + "hex-color": "#c33", + "hsl-color": "hsl(100, 100%, 100%)", + "rgba-color": "rgba(0, 0, 0, 1)", + "rgb-color": "rgb(10, 0, 0)", + "css-color": "blue", + "px-unit": "10px", + "em-unit": "2.3em", + "number": 5, + "float": 5.5, + "string": "Lorem ipsum, (\"foo\", bar)" +} diff --git a/test/fixtures/convert-strings/variables.json b/test/fixtures/convert-strings/variables.json new file mode 100644 index 0000000..4ffdb19 --- /dev/null +++ b/test/fixtures/convert-strings/variables.json @@ -0,0 +1,12 @@ +{ + "hex-color": "#c33", + "hsl-color": "hsl(100, 100%, 100%)", + "rgba-color": "rgba(0, 0, 0, 1)", + "rgb-color": "rgb(10, 0, 0)", + "css-color": "blue", + "px-unit": "10px", + "em-unit": "2.3em", + "number": 5, + "float": 5.5, + "string": "Lorem ipsum, (\"foo\", bar)" +} diff --git a/test/fixtures/include-paths/style-js.scss b/test/fixtures/include-paths/style-js.scss new file mode 100644 index 0000000..50dbc94 --- /dev/null +++ b/test/fixtures/include-paths/style-js.scss @@ -0,0 +1,5 @@ +@import 'variables.js'; + +body { + color: $color-red; +} diff --git a/test/fixtures/include-paths/variables/variables.js b/test/fixtures/include-paths/variables/variables.js new file mode 100644 index 0000000..77876c4 --- /dev/null +++ b/test/fixtures/include-paths/variables/variables.js @@ -0,0 +1,4 @@ +module.exports = { + "color-red": "#c33", + "color-blue": "#33c" +} diff --git a/test/fixtures/lists/style-js.scss b/test/fixtures/lists/style-js.scss new file mode 100644 index 0000000..9e752f3 --- /dev/null +++ b/test/fixtures/lists/style-js.scss @@ -0,0 +1,5 @@ +@import 'variables.js'; + +body { + color: nth($colors, 1); +} diff --git a/test/fixtures/lists/variables.js b/test/fixtures/lists/variables.js new file mode 100644 index 0000000..8371e6e --- /dev/null +++ b/test/fixtures/lists/variables.js @@ -0,0 +1,3 @@ +module.exports = { + "colors": ["#c33", "#33c"] +} diff --git a/test/fixtures/maps/style-js.scss b/test/fixtures/maps/style-js.scss new file mode 100644 index 0000000..36e4e70 --- /dev/null +++ b/test/fixtures/maps/style-js.scss @@ -0,0 +1,5 @@ +@import 'variables.js'; + +body { + color: map-get($colors, red); +} diff --git a/test/fixtures/maps/variables.js b/test/fixtures/maps/variables.js new file mode 100644 index 0000000..e3bb9e0 --- /dev/null +++ b/test/fixtures/maps/variables.js @@ -0,0 +1,5 @@ +module.exports = { + "colors": { + "red": "#c33" + } +} diff --git a/test/fixtures/strings/style-js.scss b/test/fixtures/strings/style-js.scss new file mode 100644 index 0000000..50dbc94 --- /dev/null +++ b/test/fixtures/strings/style-js.scss @@ -0,0 +1,5 @@ +@import 'variables.js'; + +body { + color: $color-red; +} diff --git a/test/fixtures/strings/variables.js b/test/fixtures/strings/variables.js new file mode 100644 index 0000000..77876c4 --- /dev/null +++ b/test/fixtures/strings/variables.js @@ -0,0 +1,4 @@ +module.exports = { + "color-red": "#c33", + "color-blue": "#33c" +} diff --git a/test/fixtures/wrong-js-export/style-js.scss b/test/fixtures/wrong-js-export/style-js.scss new file mode 100644 index 0000000..50dbc94 --- /dev/null +++ b/test/fixtures/wrong-js-export/style-js.scss @@ -0,0 +1,5 @@ +@import 'variables.js'; + +body { + color: $color-red; +} diff --git a/test/fixtures/wrong-js-export/variables.js b/test/fixtures/wrong-js-export/variables.js new file mode 100644 index 0000000..a90266f --- /dev/null +++ b/test/fixtures/wrong-js-export/variables.js @@ -0,0 +1,4 @@ +module.exports = { + "color-red": function() {}, + "color-blue": "#33c" +} diff --git a/test/index.js b/test/index.js index fa3c13b..065ab55 100644 --- a/test/index.js +++ b/test/index.js @@ -6,68 +6,95 @@ import {resolve} from 'path'; const EXPECTATION = 'body {\n color: #c33; }\n'; -describe('Import type test', function() { - - it('imports strings', function() { - let result = sass.renderSync({ - file: './test/fixtures/strings/style.scss', - importer: jsonImporter +function sassRenderFile(opts = {}) { + return function(file) { + return sass.renderSync({ + file: file, + importer: jsonImporter, + ...opts }); + } +} +function testExpectedCSS(result) { expect(result.css.toString()).to.eql(EXPECTATION); +} + + +describe('Import type test', function() { + + it('imports strings', function() { + ['./test/fixtures/strings/style.scss', + './test/fixtures/strings/style-js.scss' + ].map(sassRenderFile()).forEach(testExpectedCSS); }); it('imports lists', function() { - let result = sass.renderSync({ - file: './test/fixtures/lists/style.scss', - importer: jsonImporter - }); - - expect(result.css.toString()).to.eql(EXPECTATION); + ['./test/fixtures/lists/style.scss', + './test/fixtures/lists/style-js.scss' + ].map(sassRenderFile()).forEach(testExpectedCSS); }); it('imports maps', function() { - let result = sass.renderSync({ - file: './test/fixtures/maps/style.scss', - importer: jsonImporter - }); - - expect(result.css.toString()).to.eql(EXPECTATION); + ['./test/fixtures/maps/style.scss', + './test/fixtures/maps/style-js.scss' + ].map(sassRenderFile()).forEach(testExpectedCSS); }); it('finds imports via includePaths', function() { - let result = sass.renderSync({ - file: './test/fixtures/include-paths/style.scss', - includePaths: ['./test/fixtures/include-paths/variables'], - importer: jsonImporter - }); - - expect(result.css.toString()).to.eql(EXPECTATION); + ['./test/fixtures/include-paths/style.scss', + './test/fixtures/include-paths/style-js.scss' + ].map(sassRenderFile({ + includePaths: ['./test/fixtures/include-paths/variables'] + })).forEach(testExpectedCSS); }); it('finds imports via multiple includePaths', function() { - let result = sass.renderSync({ - file: './test/fixtures/include-paths/style.scss', - includePaths: ['./test/fixtures/include-paths/variables', './some/other/path/'], - importer: jsonImporter - }); + ['./test/fixtures/include-paths/style.scss', + './test/fixtures/include-paths/style-js.scss' + ].map(sassRenderFile({ + includePaths: ['./test/fixtures/include-paths/variables', './some/other/path/'] + })).forEach(testExpectedCSS); + }); - expect(result.css.toString()).to.eql(EXPECTATION); + it('quotes strings and preserves numbers, floats, colors, and values with units', function() { + ['./test/fixtures/convert-strings/style-js.scss', + './test/fixtures/convert-strings/style.scss' + ].map(sassRenderFile()).forEach(function(result) { + expect(result.css.toString()).to.eql(`body {\n content: 'Lorem ipsum, ("foo", bar)';\n color: #c33, white, black, #0a0000, blue;\n font-size: 2.3em;\n margin-top: 5px;\n margin-bottom: 5.5em; }\n`); + }); }); - it(`throws when an import doesn't exist`, function() { + it(`strips non-valid JSON values from JS exports`, function() { function render() { sass.renderSync({ - file: './test/fixtures/include-paths/style.scss', - includePaths: ['./test/fixtures/include-paths/foo'], + file: './test/fixtures/wrong-js-export/style-js.scss', importer: jsonImporter }); } + expect(render).to.throw(`Undefined variable: "$color-red".`) + }); + + it(`throws when an import doesn't exist`, function() { + function render(file) { + return function() { + sass.renderSync({ + file: file, + includePaths: ['./test/fixtures/include-paths/foo'], + importer: jsonImporter + }); + } + } - expect(render).to.throw( + expect(render('./test/fixtures/include-paths/style.scss')).to.throw( 'Unable to find "variables.json" from the following path(s): ' + `${resolve(process.cwd(), 'test/fixtures/include-paths')}, ./test/fixtures/include-paths/foo. ` + 'Check includePaths.' ); + expect(render('./test/fixtures/include-paths/style-js.scss')).to.throw( + 'Unable to find "variables.js" from the following path(s): ' + + `${resolve(process.cwd(), 'test/fixtures/include-paths')}, ./test/fixtures/include-paths/foo. ` + + 'Check includePaths.' + ); }); });