Skip to content

Commit 12425fc

Browse files
committed
Empty rules are minified away. Passing all YUI tests!
1 parent ccc5d04 commit 12425fc

9 files changed

+109
-15
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# LuaCss
22

3+
![version 1.0](https://img.shields.io/badge/version-1.0-green.svg)
4+
35
CSS tokenizing and minimizing library for Lua 5.1.
46
No external dependencies - only pure Lua.
57
The library tries to closely follow the [CSS3 specification](https://www.w3.org/TR/css-syntax-3/).
@@ -79,6 +81,12 @@ end
7981
Note that all instances of U+0000 (NULL) code points are converted to U+FFFD (replacement character) per the specification.
8082

8183

84+
### _VERSION
85+
`version = _VERSION`
86+
87+
Not a function! Get the version of the library, i.e. `"1.0.2"`.
88+
89+
8290

8391
## Options
8492
`options` is a table that can have any of these fields:

css.lua

+71-9
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ local utf8 = require((path.."utf8"):sub(2))
8686

8787
local F = string.format
8888

89-
local css = {VERSION="0.1.0"}
89+
local css = {_VERSION="1.0.0"}
9090

9191

9292

@@ -1754,15 +1754,15 @@ function css.minimize(tokensIn, options)
17541754
end
17551755

17561756
elseif tokType == "semicolon" then
1757+
cb(token, tokType, i)
1758+
17571759
if isAt"rule" then
17581760
currentProperty = nil
17591761
colonsAfterProp = 0
17601762
end
17611763

17621764
currentAtKeyword = nil -- Possible end of @charset ""; or similar.
17631765

1764-
cb(token, tokType, i)
1765-
17661766
elseif tokType == "atKeyword" then
17671767
token = cb(token, tokType, i) or token
17681768
currentAtKeyword = token.value
@@ -1822,9 +1822,9 @@ function css.minimize(tokensIn, options)
18221822

18231823
local function printPeek(tokens, i, count)
18241824
print("----------------")
1825-
for j = i, math.min(i+count, #tokens) do
1825+
for j = math.min(i+count, i), math.max(i+count, i) do
18261826
if not tokens[j] then break end
1827-
print(j-i, tokens[j].type, tokens[j].value)
1827+
print(j-i, tokens[j].type, tokens[j].value or "")
18281828
end
18291829
print("----------------")
18301830
end
@@ -1998,7 +1998,7 @@ function css.minimize(tokensIn, options)
19981998

19991999
-- Note: It seems CSS4 will add #RRGGBBAA and #RGBA formats, so this code will probably have to be updated.
20002000
if not isAny(#tokOut.value, 3,6) then
2001-
print("Warning: Color value looks incorrect: #"..tokOut.value)
2001+
print("[css] Warning: Color value looks incorrect: #"..tokOut.value)
20022002
end
20032003

20042004
return tokOut
@@ -2012,8 +2012,8 @@ function css.minimize(tokensIn, options)
20122012
local tokNext = getNextNonWsToken(tokensIn, i+1, 1)
20132013

20142014
if
2015-
tokNext and tokNext.type ~= "}" and tokNext.type ~= "semicolon"
2016-
and not (tokPrev and tokPrev.type == "{")
2015+
tokNext and not isAny(tokNext.type, "}","semicolon")
2016+
and not (tokPrev and isAny(tokPrev.type, "{","}"))
20172017
then
20182018
add(tokIn)
20192019
end
@@ -2072,6 +2072,61 @@ function css.minimize(tokensIn, options)
20722072
end
20732073
end)
20742074

2075+
-- Remove empty rules.
2076+
--------------------------------
2077+
local ruleBeginnings = {}
2078+
local ruleDepth = 0
2079+
local lastRuleStart = 0
2080+
local isInSomethingInFileScope = false
2081+
2082+
tokensIn = tokensOut
2083+
tokensOut = {}
2084+
2085+
eachToken(tokensIn, nil, nil, nil, function(tokIn, tokType, i)
2086+
add(tokIn)
2087+
2088+
if not isInSomethingInFileScope and tokType ~= "comment" then
2089+
lastRuleStart = #tokensOut
2090+
isInSomethingInFileScope = true
2091+
2092+
elseif tokType == "{" then
2093+
table.insert(ruleBeginnings, lastRuleStart)
2094+
ruleDepth = ruleDepth+1
2095+
2096+
lastRuleStart = #tokensOut+1
2097+
2098+
elseif tokType == "}" then
2099+
local ruleStart = table.remove(ruleBeginnings)
2100+
ruleDepth = ruleDepth-1
2101+
2102+
if not ruleStart then
2103+
error("[css] Uneven curly brackets.")
2104+
end
2105+
2106+
local tokPrev = getNextToken(tokensOut, #tokensOut-1, -1)
2107+
if tokPrev.type == "{" then
2108+
for j = #tokensOut, ruleStart, -1 do
2109+
table.remove(tokensOut, j)
2110+
end
2111+
end
2112+
2113+
lastRuleStart = #tokensOut+1
2114+
if ruleDepth == 0 then
2115+
isInSomethingInFileScope = false
2116+
end
2117+
2118+
elseif tokType == "semicolon" and currentAtKeyword then
2119+
lastRuleStart = #tokensOut+1
2120+
if ruleDepth == 0 then
2121+
isInSomethingInFileScope = false
2122+
end
2123+
end
2124+
end)
2125+
2126+
if tokensOut[1] and tokensOut[#tokensOut].type == "semicolon" then
2127+
table.remove(tokensOut)
2128+
end
2129+
20752130
-- Minimize specific properties.
20762131
--------------------------------
20772132

@@ -2136,7 +2191,14 @@ function css.minimize(tokensIn, options)
21362191
table.insert(tokensOut, i+1, newTokenWhitespace(" "))
21372192
end
21382193

2139-
nextTokenIndex = i
2194+
-- We also don't need any space before anymore.
2195+
if tokensOut[i-1] and tokensOut[i-1].type == "whitespace" then
2196+
table.remove(tokensOut, i-1)
2197+
nextTokenIndex = i-1
2198+
else
2199+
nextTokenIndex = i
2200+
end
2201+
21402202
return
21412203
end
21422204

tests.lua

+2-2
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ local function runTest(css, printCss)
101101

102102

103103

104-
--[[ Round-trip test.
104+
-- [[ Round-trip test.
105105
local cssAgain = cssLib.minimize(css)
106106

107107
if printCss then
@@ -288,5 +288,5 @@ end
288288

289289
-- runSmallTest()
290290
-- runTestOnFile("test.css")
291-
-- runTestOnFile("tests/yui/pseudo-first.css", true)
291+
-- runTestOnFile("tests/yui/issue205.css", true)
292292
runTestsuite(true)

tests/empty.css

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*! preserved */
2+
emptiness {}
3+
4+
@import "another.css";
5+
/* I'm empty - delete me */
6+
empty { ;}
7+
8+
@media print {
9+
.noprint { display: none; }
10+
}
11+
12+
@media screen {
13+
/* this rule should be removed, not simply minified.*/
14+
.breakme {}
15+
.printonly { display: none; }
16+
}
17+
18+
@import "again.css";
19+
/* Empty @media group should be removed. */
20+
@media (not screen) {
21+
.foo {}
22+
.barf {; }
23+
}

tests/empty.css.min

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/*! preserved */@import"another.css";@media print{.noprint{display:none}}@media screen{.printonly{display:none}}@import"again.css"

tests/keep-percent.css.min

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
.SB-messages .SB-message a{color:rgba(0%,50%,50%,.3);border-bottom:1px dotted #b96375;text-shadow:0 1px 0 hsl(0,0%,0%);flex-basis:0%;flex:0%}@keyframes fadeIn{0%{opacity:0}100%{opacity:.5}}
1+
.SB-messages .SB-message a{color:rgba(0%,50%,50%,.3);border-bottom:1px dotted#b96375;text-shadow:0 1px 0 hsl(0,0%,0%);flex-basis:0%;flex:0%}@keyframes fadeIn{0%{opacity:0}100%{opacity:.5}}

tests/yui/color.css.min

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
.color{me:#7b7b7b;test-overflow:#fff;impressed:#fed;again:#abcdef;andagain:#a6c;background-color:#aa66ccc;filter:chroma(color="#FFFFFF");background:none repeat scroll 0 0 #f00;alpha:rgba(1,2,3,4);border-color:rgba(1,2,3,4);color:#12a}#AABBCC{background-color:#fe1;filter:chroma(color=#FFFFFF);color:#412;foo:#0f1#abc#abc#123344;border-color:#aa66ccc}.foo #AABBCC{background-color:#fe1;color:#412;border-color:#abc;filter:chroma(color=#FFFFFF)}.bar,#AABBCC{background-color:#fe1;border-color:#0f1#abcdef;filter:chroma(color=#11FFFFFF);color:#412}.foo,#AABBCC.foobar{background-color:#fe1;border-color:#0f1#abcdef#abc;color:#412}@media screen{.bar,#AABBCC{background-color:#fe1;color:#412}}
1+
.color{me:#7b7b7b;test-overflow:#fff;impressed:#fed;again:#abcdef;andagain:#a6c;background-color:#aa66ccc;filter:chroma(color="#FFFFFF");background:none repeat scroll 0 0#f00;alpha:rgba(1,2,3,4);border-color:rgba(1,2,3,4);color:#12a}#AABBCC{background-color:#fe1;filter:chroma(color=#FFFFFF);color:#412;foo:#0f1#abc#abc#123344;border-color:#aa66ccc}.foo #AABBCC{background-color:#fe1;color:#412;border-color:#abc;filter:chroma(color=#FFFFFF)}.bar,#AABBCC{background-color:#fe1;border-color:#0f1#abcdef;filter:chroma(color=#11FFFFFF);color:#412}.foo,#AABBCC.foobar{background-color:#fe1;border-color:#0f1#abcdef#abc;color:#412}@media screen{.bar,#AABBCC{background-color:#fe1;color:#412}}

tests/yui/issue205.css.min

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
@charset "utf-8";a[id$=_foo]{abc:abc};
1+
@charset "utf-8";a[id$=_foo]{abc:abc}

tests/yui/media-empty-class.css.min

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
/*! preserved */@import "another.css";@media print{.noprint{display:none}}@media screen{.printonly{display:none}}
1+
/*! preserved */@import"another.css";@media print{.noprint{display:none}}@media screen{.printonly{display:none}}

0 commit comments

Comments
 (0)