diff --git a/css.js b/css.js index b50e0a5..4c15579 100644 --- a/css.js +++ b/css.js @@ -5,13 +5,13 @@ exports.CSSStyleDeclaration = CSSStyleDeclaration exports.CSSStyleSheet = CSSStyleSheet var path = require("path") -, clearFn = (_, q, str) => q ? (q == "\"" && str.indexOf("'") == -1 ? "'" + str + "'" : _) : +, clearFn = (_, q, str) => q ? (q = str.indexOf("'") == -1 ? "'" : "\"", q + str.replace(q === "'" ? /\\(")/g : /\\(')/g, "$1")) + q : _.replace(/[\t\n]+/g, " ") .replace(/ *([,;{}>~+\/]) */g, "$1") .replace(/;(?=})/g, "") .replace(/: +/g, ":") .replace(/([ :,])0\.([0-9])/g, "$1.$2") -, clear = s => s.replace(/(["'])((?:\\\1|.)*?)\1|[^"']+/g, clearFn).replace(/url\(("|')([^'"()\s]+)\1\)/g, "url($2)") +, clear = s => s.replace(/("|')((?:\\\1|[^\1])*?)\1|[^"']+/g, clearFn).replace(/url\(("|')([^'"()\s]+)\1\)/g, "url($2)") , styleHandler = { get(style, prop) { if (prop === "cssText") { @@ -110,7 +110,7 @@ var path = require("path") function CSSRule(text, parentStyleSheet, atType, parentRule = null) { // Clear comments and trim - text = text.replace(/^\s+|\/\*[^*]*\*+([^/*][^*]*\*+)*\/|\s+$/g, "") + text = text.replace(/\/\*(?:[^*]|\*(?!\/))*\*\//g, "").trim() var type = text[0] === "@" && text.slice(1, text.indexOf(" ")) || "style" , rule = Object.create(ruleTypes[type] || ruleTypes[type === "page" || type === "font-face" || type === "counter-style" ? "style" : atType]) rule.cssText = text @@ -149,29 +149,35 @@ CSSStyleSheet.prototype = { this.rules.splice(idx > -1 ? idx : this.rules.length, 0, CSSRule(rule, this)) }, replaceSync(text) { - var m - , sheet = this - , re = /((?:("|')(?:\\.|(?!\2)[^\\])*?\2|[^"'}{;\/]|\/(?!\*))+)|(\/\*[\s\S]*?\*\/)|./g - , block = 0 - , pos = 0 - , arr = [] + var qpos, sheet = this sheet.rules = sheet.cssRules = [] sheet.warnings = [] - for (; (m = re.exec(text)); ) { - if (m[0] === "{") { - block++ - } else if (m[3] && block === 0) { - text = text.slice(0, m.index) + text.slice(re.lastIndex) - re.lastIndex = m.index - } else if (m[0] === ";" && block === 0 || m[0] === "}" && --block === 0) { - arr.push(pos, pos = re.lastIndex, m[0]) - } else if (block < 0) { - throw "Invalid css" + for (var char, inQuote, depth = 0, start = 0, pos = 0, len = text.length; pos < len; pos++) { + char = text[pos] + if (char === "\\" && inQuote !== "/") { + pos++ + } else if (inQuote) { + if (char === inQuote) { + if (char !== "/" || text[pos - 1] === "*") { + if (char === "/" && depth < 1) { + // Remove root level comments + text = text.slice(0, qpos) + text.slice(pos + 1) + pos = qpos - 1 + len = text.length + } + inQuote = "" + } + } + } else if (char === "'" || char === "\"" || char === "/" && text[pos+1] === "*") { + inQuote = char + qpos = pos + } else if (char === "{") { + depth++ + } else if (char === "}" && --depth < 1 || char === ";" && depth < 1) { + if (depth < 0) throw "Invalid css" + sheet.rules.push(CSSRule(text.slice(start, start = pos + 1), sheet, char)) } } - for (m = 0; m < arr.length; m += 3) { - sheet.rules.push(CSSRule(text.slice(arr[m], arr[m + 1]), sheet, arr[m+2])) - } }, toString(min) { if (min) this.min = min diff --git a/test/css.js b/test/css.js index b1343b9..ef16cd5 100644 --- a/test/css.js +++ b/test/css.js @@ -49,14 +49,18 @@ describe("css.js {0}", describe.env === "browser" ? [["mock", exports], ["native assert.end() }) - test("parse {i}", [ + test("parse '{0}'", [ [" ", ""], [" html {} body{ } ", ""], + ["a{a:1}b{b:2}c{c:3}", "a{a:1}\nb{b:2}\nc{c:3}"], + ["/*A*/a/*B\\*/{b:c}/**/d/**/{e:f}", "a{b:c}\nd{e:f}"], + ["a\\,b{c:d}", "a\\,b{c:d}"], [" * {margin:0} body { font-size: 1.4em; } p { color: red; }", "*{margin:0}\nbody{font-size:1.4em}\np{color:red}"], ["div {\n background: #00a400;\n background: linear-gradient(to bottom, rgb(214, 122, 127) 0%, hsla(237deg 74% 33% / 61%) 100%);}", "div{background:#00a400;background:linear-gradient(to bottom,rgb(214,122,127) 0%,hsla(237deg 74% 33%/61%) 100%)}"], [" @import url('a.css') screen; @import url(\"b.css\") screen; * { margin: 0; }", "@import 'a.css' screen;\n@import 'b.css' screen;\n*{margin:0}"], ["@media (min-width: 500px) {\n body {\n color: blue;\n }\n}\n", "@media (min-width:500px){body{color:blue}}"], ["@media (min-width: 500px) {\n\n}\n", ""], + [".a { b: url('a\\'b') }", ".a{b:url(\"a'b\")}"], //[":root{--my-test-variable:123px}.p{ width: var(--my-test-variable); }", ".p{width:123px}"], ], (text, expected, assert) => { sheet.replaceSync(text)