diff --git a/camel-case.js b/camel-case.js index a055191..4ee6b8c 100644 --- a/camel-case.js +++ b/camel-case.js @@ -1,9 +1,18 @@ "use strict"; function camelCase (str) { - if (str.startsWith("--")) { - return str; - } - return str.replace(/(^|\s|\W)-(ms-)/g, "$1$2").replace(/-+(\w)/g, (s, char) => s.length > 2 ? s : char.toUpperCase()); + return str.replace(/[\w-]+/g, (s) => ( + /^-?[a-z]+(?:-[a-z]+)+$/.test(s) + ? s.replace(/^-/, "") + .replace( + /-(\w)/g, + (s, char, index) => ( + index + ? char.toUpperCase() + : char + ) + ) + : s + )); } module.exports = camelCase; diff --git a/object-parser.js b/object-parser.js index 2313b8f..62e637e 100644 --- a/object-parser.js +++ b/object-parser.js @@ -2,6 +2,7 @@ const getTemplate = require("./get-template"); const ObjectLiteral = require("./object"); const camelCase = require("./camel-case"); +const unCamelCase = require("./un-camel-case"); const Literal = require("./literal"); const postcss = require("postcss"); @@ -9,10 +10,6 @@ function forEach (arr, callback) { arr && arr.forEach(callback); } -function unCamelCase (str) { - return str.replace(/[A-Z]/g, (char) => "-" + char.toLowerCase()).replace(/(^|\b)ms-/, "$1-ms-"); -} - const replaceProp = (fn) => (value) => ( value.replace(/(\(\s*)(.*?)(\s*:)/g, (s, prefix, prop, suffix) => ( prefix + fn(prop) + suffix diff --git a/object.js b/object.js index 4ad6eeb..858b488 100644 --- a/object.js +++ b/object.js @@ -16,7 +16,7 @@ class ObjectLiteral extends Container { constructor (defaults) { super(defaults); this.type = "object"; - if (!this.nodes) this.nodes = []; + this.nodes = []; } } diff --git a/package.json b/package.json index 4123e31..64b1dbd 100644 --- a/package.json +++ b/package.json @@ -52,13 +52,13 @@ "postcss-syntax": ">=0.31.0" }, "devDependencies": { - "autoprefixer": "^9.0.0", + "autoprefixer": "^9.0.1", "chai": "^4.1.2", "codecov": "^3.0.4", "json5": "^1.0.1", "mocha": "^5.2.0", "nyc": "^12.0.2", - "postcss": "^7.0.0", + "postcss": "^7.0.1", "postcss-parser-tests": "^6.3.0", "postcss-safe-parser": "^4.0.1", "postcss-syntax": ">=0.31.0" diff --git a/test/camel-case.js b/test/camel-case.js new file mode 100644 index 0000000..da2621c --- /dev/null +++ b/test/camel-case.js @@ -0,0 +1,71 @@ +"use strict"; + +const expect = require("chai").expect; +const camelCase = require("../camel-case"); +const unCamelCase = require("../un-camel-case"); + +const data = { + xwebkitAnimation: "-xwebkit-animation", + webkitAnimation: "-webkit-animation", + epubAnimation: "-epub-animation", + mozAnimation: "-moz-animation", + msAnimation: "-ms-animation", + oAnimation: "-o-animation", + xAnimation: "-x-animation", + webkitApp: "-webkit-app", + borderTopLeftRadius: "border-top-left-radius", + backgroundImage: "background-image", + "::selection": "::selection", + "::mozSelection": "::-moz-selection", + "::mozSelection,::selection": "::-moz-selection,::selection", + "--margin-top": "--margin-top", + "margin--top": "margin--top", + "height: webkitCalc(2vh-20px);": "height: -webkit-calc(2vh-20px);", + "calc(2vh-20px)": "calc(2vh-20px)", + "calc(2vh--20px)": "calc(2vh--20px)", +}; + +const testCases = Object.keys(data).map(prop => { + return { + camel: prop, + unCamel: data[prop], + }; +}); + +const symbols = Array.from("@*:;\n,(){} "); + +describe("camelCase", () => { + testCases.forEach(testCase => { + it(`${testCase.unCamel} => ${testCase.camel}`, () => { + expect(camelCase(testCase.unCamel)).to.equal(testCase.camel); + }); + }); + describe("symbols", () => { + symbols.forEach(symbol => { + it(JSON.stringify(symbol), () => { + expect(camelCase(testCases.map(testCase => testCase.unCamel).join(symbol))).to.equal(testCases.map(testCase => testCase.camel).join(symbol)); + }); + }); + }); +}); + +describe("unCamelCase", () => { + it("onChange => on-change", () => { + expect(unCamelCase("onChange")).to.equal("on-change"); + }); + it("OnChange => -on-change", () => { + expect(unCamelCase("OnChange")).to.equal("-on-change"); + }); + testCases.forEach(testCase => { + it(`${testCase.camel} => ${testCase.unCamel}`, () => { + expect(unCamelCase(testCase.camel)).to.equal(testCase.unCamel); + }); + }); + describe("symbols", () => { + symbols.forEach(symbol => { + it(JSON.stringify(symbol), () => { + expect(unCamelCase(testCases.map(testCase => testCase.camel).join(symbol))).to.equal(testCases.map(testCase => testCase.unCamel).join(symbol)); + }); + }); + }); +}); diff --git a/test/css-in-js.js b/test/css-in-js.js index 47ab68f..df37e12 100644 --- a/test/css-in-js.js +++ b/test/css-in-js.js @@ -137,6 +137,7 @@ describe("CSS in JS", () => { }); `); }); + describe("objectify for css", () => { cases.each((name, css) => { if (name === "bom.css") return; diff --git a/un-camel-case.js b/un-camel-case.js new file mode 100644 index 0000000..ba322c1 --- /dev/null +++ b/un-camel-case.js @@ -0,0 +1,16 @@ +"use strict"; +function unCamelCase (str) { + return str.replace(/[\w-]+/g, (s) => ( + /^[a-z]*(?:[A-Z][a-z]+)+$/.test(s) + ? s.replace( + /[A-Z]/g, + s => "-" + s.toLowerCase() + ).replace( + /^(\w|ms|moz|khtml|epub|\w*webkit)-/, + "-$1-" + ) + : s + )); +} + +module.exports = unCamelCase;