From ff1bec16546d4c8ab741fb259c22036b77317332 Mon Sep 17 00:00:00 2001 From: Pierre Lebrun Date: Sat, 2 May 2020 10:55:12 -0400 Subject: [PATCH] feat: add case conversion Closes #77 --- README.md | 45 ++++++++++++++++--- src/index.js | 25 +++++++---- test/fixtures-json5/convert-case/style.scss | 7 +++ .../convert-case/variables.json5 | 5 +++ test/fixtures/convert-case/style.scss | 7 +++ test/fixtures/convert-case/variables.json | 5 +++ test/index.js | 41 +++++++++++++++++ 7 files changed, 121 insertions(+), 14 deletions(-) create mode 100644 test/fixtures-json5/convert-case/style.scss create mode 100644 test/fixtures-json5/convert-case/variables.json5 create mode 100644 test/fixtures/convert-case/style.scss create mode 100644 test/fixtures/convert-case/variables.json diff --git a/README.md b/README.md index e30cf1f..75593ed 100644 --- a/README.md +++ b/README.md @@ -155,11 +155,46 @@ var jsonImporter = require('../dist/node-sass-json-importer'); sass.render({ file: './1.sass', importer: jsonImporter({ - resolver: function(dir, url) { - return url.startsWith('~/') - ? path.resolve(dir, 'json', url.substr(2)) - : path.resolve(dir, url); - }, + resolver: function(dir, url) { + return url.startsWith('~/') + ? path.resolve(dir, 'json', url.substr(2)) + : path.resolve(dir, url); + }, + }), +}, function(err, result) { console.log(err || result.css.toString()) }); +``` + +## camelCase to kebab-case + +If you want to convert standard JavaScript caseCase keys into CSS/SCSS compliant kebab-case keys, for example: + +`variables.json`: + +```JS +{ + "bgBackgroundColor": 'red' +} +``` + +For usage like this: + +`style.scss`: + +```SCSS +@import "variables.json"; + +div { + background: $bg-background-color; +} +``` + +You can pass set the `convertCase` option to true as an argument to `jsonImporter` like so: + +```js +sass.render({ + file: './1.sass', + importer: jsonImporter({ + convertCase: true, }), }, function(err, result) { console.log(err || result.css.toString()) }); ``` diff --git a/src/index.js b/src/index.js index c4078d9..7849cb7 100644 --- a/src/index.js +++ b/src/index.js @@ -35,7 +35,7 @@ export default function(options = {}) { const json = Array.isArray(fileContents) ? { [extensionlessFilename]: fileContents } : fileContents; return { - contents: transformJSONtoSass(json), + contents: transformJSONtoSass(json, options), }; } catch(error) { return new Error(`node-sass-json-importer: Error transforming JSON/JSON5 to SASS. Check if your JSON/JSON5 parses correctly. ${error}`); @@ -47,11 +47,11 @@ export function isJSONfile(url) { return /\.js(on5?)?$/.test(url); } -export function transformJSONtoSass(json) { +export function transformJSONtoSass(json, opts = {}) { return Object.keys(json) .filter(key => isValidKey(key)) .filter(key => json[key] !== '#') - .map(key => `$${key}: ${parseValue(json[key])};`) + .map(key => `$${opts.convertCase ? toKebabCase(key) : key}: ${parseValue(json[key], opts)};`) .join('\n'); } @@ -59,11 +59,18 @@ export function isValidKey(key) { return /^[^$@:].*/.test(key) } -export function parseValue(value) { +export function toKebabCase(key) { + return key + .replace(/([a-z0-9])([A-Z])/g, '$1-$2') + .replace(/([A-Z])([A-Z])(?=[a-z])/g, '$1-$2') + .toLowerCase(); +} + +export function parseValue(value, opts = {}) { if (_.isArray(value)) { - return parseList(value); + return parseList(value, opts); } else if (_.isPlainObject(value)) { - return parseMap(value); + return parseMap(value, opts); } else if (value === '') { return '""'; // Return explicitly an empty string (Sass would otherwise throw an error as the variable is set to nothing) } else { @@ -71,16 +78,16 @@ export function parseValue(value) { } } -export function parseList(list) { +export function parseList(list, opts = {}) { return `(${list .map(value => parseValue(value)) .join(',')})`; } -export function parseMap(map) { +export function parseMap(map, opts = {}) { return `(${Object.keys(map) .filter(key => isValidKey(key)) - .map(key => `${key}: ${parseValue(map[key])}`) + .map(key => `${opts.convertCase ? toKebabCase(key) : key}: ${parseValue(map[key], opts)}`) .join(',')})`; } diff --git a/test/fixtures-json5/convert-case/style.scss b/test/fixtures-json5/convert-case/style.scss new file mode 100644 index 0000000..867c9b2 --- /dev/null +++ b/test/fixtures-json5/convert-case/style.scss @@ -0,0 +1,7 @@ +@import 'variables.json5'; + +body { + color: $color-red; + color: $color-green; + color: $color-blue; +} diff --git a/test/fixtures-json5/convert-case/variables.json5 b/test/fixtures-json5/convert-case/variables.json5 new file mode 100644 index 0000000..2da1319 --- /dev/null +++ b/test/fixtures-json5/convert-case/variables.json5 @@ -0,0 +1,5 @@ +{ + 'colorRed': '#c33', // Red color + 'ColorGreen': '#3c3', // Green color + 'COLORBlue': '#33c', // Blue color +} diff --git a/test/fixtures/convert-case/style.scss b/test/fixtures/convert-case/style.scss new file mode 100644 index 0000000..0660840 --- /dev/null +++ b/test/fixtures/convert-case/style.scss @@ -0,0 +1,7 @@ +@import 'variables.json'; + +body { + color: $color-red; + color: $color-green; + color: $color-blue; +} diff --git a/test/fixtures/convert-case/variables.json b/test/fixtures/convert-case/variables.json new file mode 100644 index 0000000..7129456 --- /dev/null +++ b/test/fixtures/convert-case/variables.json @@ -0,0 +1,5 @@ +{ + "colorRed": "#c33", + "ColorGreen": "#3c3", + "COLORBlue": "#33c" +} diff --git a/test/index.js b/test/index.js index 73cf54f..d896d93 100644 --- a/test/index.js +++ b/test/index.js @@ -2,6 +2,7 @@ import jsonImporter, { isJSONfile, isValidKey, + toKebabCase, parseValue, } from '../src' import sass from 'node-sass'; @@ -149,6 +150,17 @@ describe('Import type test (JSON)', function() { expect(result.css.toString()).to.eql('body {\n color: ""; }\n'); }); + + it('allows case conversion', function() { + let result = sass.renderSync({ + file: './test/fixtures/convert-case/style.scss', + importer: jsonImporter({ + convertCase: true, + }), + }); + + expect(result.css.toString()).to.eql('body {\n color: #c33;\n color: #3c3;\n color: #33c; }\n'); + }); }); describe('Import type test (JSON5)', function() { @@ -272,6 +284,17 @@ describe('Import type test (JSON5)', function() { expect(result.css.toString()).to.eql('body {\n color: ""; }\n'); }); + + it('allows case conversion', function() { + let result = sass.renderSync({ + file: './test/fixtures-json5/convert-case/style.scss', + importer: jsonImporter({ + convertCase: true, + }), + }); + + expect(result.css.toString()).to.eql('body {\n color: #c33;\n color: #3c3;\n color: #33c; }\n'); + }); }); describe('parseValue', function() { @@ -339,3 +362,21 @@ describe('isValidKey', function() { expect(isValidKey('valid')).to.be.true; }); }); + +describe('toKebabCase', function() { + it('can handle camelCase', function() { + expect(toKebabCase('camelCase')).to.eql('camel-case'); + }); + + it('can handle PascalCase', function() { + expect(toKebabCase('PascalCase')).to.eql('pascal-case'); + }); + + it('can handle ALLCAPSCase', function() { + expect(toKebabCase('ALLCAPSCase')).to.eql('allcaps-case'); + }); + + it('can even handle EDGECaseWELLCase', function() { + expect(toKebabCase('EDGECaseWELLCase')).to.eql('edge-case-well-case'); + }); +});