packages feed

hasmin 0.3.2.4 → 0.3.3

raw patch · 19 files changed

+234/−177 lines, 19 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Hasmin.Types.Stylesheet: instance GHC.Show.Show Hasmin.Types.Stylesheet.GeneralEnclosed
- Hasmin.Utils: isLeft :: Either a b -> Bool
- Hasmin.Utils: isRight :: Either a b -> Bool
+ Hasmin.Parser.Internal: rule :: Parser Rule
+ Hasmin.Types.Stylesheet: instance GHC.Classes.Eq Hasmin.Types.Stylesheet.Rule
+ Hasmin.Types.Stylesheet: instance GHC.Classes.Eq Hasmin.Types.Stylesheet.SupportsCondInParens
+ Hasmin.Types.Stylesheet: instance GHC.Classes.Eq Hasmin.Types.Stylesheet.SupportsCondition
+ Hasmin.Types.Stylesheet: minifyRules :: [Rule] -> Reader Config [Rule]
- Hasmin.Types.FilterFunction: minifyPseudoShadow :: (Minifiable b, Minifiable t1, Minifiable t2, Traversable t) => (t2 -> t1 -> Maybe Distance -> t b -> b1) -> t2 -> t1 -> Maybe Distance -> t b -> ReaderT * Config Identity b1
+ Hasmin.Types.FilterFunction: minifyPseudoShadow :: (Minifiable b, Minifiable t1, Minifiable t2, Traversable t) => (t2 -> t1 -> Maybe Distance -> t b -> b1) -> t2 -> t1 -> Maybe Distance -> t b -> Reader Config b1

Files

CHANGELOG.md view
@@ -1,5 +1,23 @@ # Changelog +## 0.3.3+Added a simple merging of adjacent media queries (`@media` rules), e.g.:+```css+@media all and (min-width: 24rem) {+  a { font-size: 1.2rem; }+}+@media all and (min-width: 24rem) {+  b { padding-left: .25rem; padding-right: .25rem; }+}+```+Gets merged into into:+```css+@media all and (min-width: 24rem) {+  a { font-size: 1.2rem; }+  b { padding-left: .25rem; padding-right: .25rem; }+}+```+ ## 0.3.2.4 * Relaxed doctest upper bound once more, see [stackage issue 2663](https://github.com/fpco/stackage/issues/2663#issuecomment-319880160). 
Main.hs view
@@ -11,10 +11,9 @@ module Main where  import Codec.Compression.Hopfli-import Control.Monad.Reader+import Control.Applicative (liftA2) import Data.Monoid ((<>))-import Data.Attoparsec.Text (parseOnly)-import Data.Text.Lazy.Builder (toLazyText)+import Data.Text (Text)  import Options.Applicative hiding (command) import Data.Version (showVersion) import Development.GitRev (gitHash)@@ -22,19 +21,15 @@ import qualified Data.ByteString as B import qualified Data.Text.Encoding as TE import qualified Data.Text.IO as TIO-import qualified Data.Text.Lazy as TL import System.Exit (die) import Hasmin.Config-import Hasmin.Parser.Internal-import Hasmin.Types.Class-import Hasmin.Types.Stylesheet+import Hasmin  command :: Parser Commands-command = Commands <$> switch (long "beautify"-                          <> short 'b' <> help "Beautify output")-                  <*> switch (long "zopfli"-                          <> short 'z' <> help "Compress result using zopfli")-                  <*> argument str (metavar "FILE")+command = Commands +    <$> switch (long "beautify" <> short 'b' <> help "Beautify output")+    <*> switch (long "zopfli" <> short 'z' <> help "Compress result using zopfli")+    <*> argument str (metavar "FILE")  config :: Parser Config config = Config@@ -45,8 +40,7 @@                             <> short 'd'                             <> help  "Enable normalization of absolute dimensions")   <*> flag GradientMinOn GradientMinOff (long "-no-gradient-min"-                    <> short 'g'-                    <> help "Disable <gradient> minification")+                    <> short 'g' <> help "Disable <gradient> minification")   <*> flag True False (long "no-property-traits"                     <> short 't'                     <> help "Disable use of property traits for declaration minification")@@ -92,7 +86,7 @@                            <> help "Disable sorting properties lexicographically")  instructions :: ParserInfo Instructions-instructions = info (helper <*> versionOption <*> ((,) <$> command <*> config))+instructions = info (helper <*> versionOption <*> liftA2 (,) command config)     (fullDesc <> header "Hasmin - A Haskell CSS Minifier")   where versionOption = infoOption (showVersion version <> " " <> $(gitHash))                                    (long "version" <> help "Show version and commit hash")@@ -101,17 +95,11 @@ main = do     (comm, conf) <- execParser instructions     text         <- TIO.readFile (file comm)-    case parseOnly stylesheet text of-      Right r -> process r comm conf-      Left e  -> die e--process :: [Rule] -> Commands -> Config -> IO ()-process r comm conf-    | shouldBeautify comm = error "Currently unsupported"-    | shouldCompress comm = B.writeFile "output.gz" . compressWith defaultCompressOptions GZIP . TE.encodeUtf8 $ output-    | otherwise           = TIO.putStr output-  where sheet  = let ruleList = fmap (\x -> runReader (minifyWith x) conf) r-                 in if shouldRemoveEmptyBlocks conf-                       then filter (not . isEmpty) ruleList-                       else ruleList-        output = TL.toStrict . toLazyText $ mconcat (fmap toBuilder sheet)+    case minifyCSSWith conf text of+      Right rs -> process rs comm+      Left e   -> die e+  where process :: Text -> Commands -> IO ()+        process ts comm+          | shouldBeautify comm = error "Currently unsupported"+          | shouldCompress comm = B.writeFile "output.gz" . compressWith defaultCompressOptions GZIP . TE.encodeUtf8 $ ts+          | otherwise           = TIO.putStr ts
hasmin.cabal view
@@ -1,5 +1,5 @@ name:                hasmin
-version:             0.3.2.4
+version:             0.3.3
 license:             BSD3
 license-file:        LICENSE
 author:              (c) 2017 Cristian Adrián Ontivero <cristianontivero@gmail.com>
@@ -30,7 +30,7 @@ 
 executable hasmin
   default-language:    Haskell2010
-  ghc-options:         -O2 -Wall -fwarn-tabs -fwarn-unused-do-bind -Wincomplete-uni-patterns -Wincomplete-record-updates -Wmissing-import-lists -Wcompat -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances -Wredundant-constraints
+  ghc-options:         -O2 -Wall -fwarn-tabs -fwarn-unused-do-bind -Wincomplete-uni-patterns -Wincomplete-record-updates  -Wcompat -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances -Wredundant-constraints
   main-is:             Main.hs
   other-modules:       Paths_hasmin
   build-depends:       base                 >=4.9        && <5.1
@@ -50,7 +50,7 @@ library
   default-language:    Haskell2010
   hs-source-dirs:      src
-  ghc-options:         -O2 -Wall -fwarn-tabs -fwarn-unused-do-bind -Wincomplete-uni-patterns -Wincomplete-record-updates -Wmissing-import-lists -Wcompat -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances -Wredundant-constraints
+  ghc-options:         -O2 -Wall -fwarn-tabs -fwarn-unused-do-bind -Wincomplete-uni-patterns -Wincomplete-record-updates -Wcompat -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances -Wredundant-constraints
   exposed-modules:     Hasmin
                      , Hasmin.Config
                      , Hasmin.Types.Class
src/Hasmin.hs view
@@ -26,6 +26,7 @@  import Hasmin.Parser.Internal import Hasmin.Types.Class+import Hasmin.Types.Stylesheet import Hasmin.Config  -- | Minify Text, based on a 'Config'. To just use a default set of@@ -33,8 +34,8 @@ minifyCSSWith :: Config -> Text -> Either String Text minifyCSSWith cfg t = do     sheet <- parseOnly stylesheet t-    pure . TL.toStrict . toLazyText . mconcat $ map f sheet-  where f x = toBuilder $ runReader (minifyWith x) cfg+    let rs = runReader (minifyRules sheet) cfg+    pure . TL.toStrict . toLazyText . mconcat $ map toBuilder rs  -- | Minify Text CSS, using a default set of configurations (with most -- minification techniques enabled).
src/Hasmin/Parser/Internal.hs view
@@ -11,6 +11,7 @@ module Hasmin.Parser.Internal (       stylesheet     , atRule+    , rule     , declaration     , declarations     , selector
src/Hasmin/Parser/Value.hs view
@@ -295,54 +295,54 @@ position = pos4 <|> pos2 <|> pos1  pos1 :: Parser Position-pos1 =  (asciiCI "left" $> (f $ Just PosLeft))-    <|> (asciiCI "center" $> (f $ Just PosCenter))-    <|> (asciiCI "right" $> (f $ Just PosRight))-    <|> (asciiCI "top" $> (f $ Just PosTop))-    <|> (asciiCI "bottom" $> (f $ Just PosBottom))+pos1 =  (asciiCI "left" $> f (Just PosLeft))+    <|> (asciiCI "center" $> f (Just PosCenter))+    <|> (asciiCI "right" $> f (Just PosRight))+    <|> (asciiCI "top" $> f (Just PosTop))+    <|> (asciiCI "bottom" $> f (Just PosBottom))     <|> ((\a -> Position Nothing a Nothing Nothing) <$> (Just <$> percentageLength))   where f x = Position x Nothing Nothing Nothing  pos2 :: Parser Position pos2 = firstx <|> firsty   where firstx = do-            a <- (asciiCI "left" $> (Position (Just PosLeft) Nothing))-                 <|> (asciiCI "center" $> (Position (Just PosCenter) Nothing))-                 <|> (asciiCI "right" $> (Position (Just PosRight) Nothing))+            a <- (asciiCI "left" $> Position (Just PosLeft) Nothing)+                 <|> (asciiCI "center" $> Position (Just PosCenter) Nothing)+                 <|> (asciiCI "right" $> Position (Just PosRight) Nothing)                  <|> ((Position Nothing . Just) <$> percentageLength)-            skipComments *> ((asciiCI "top" $> (a (Just PosTop) Nothing))-                 <|> (asciiCI "center" $> (a (Just PosCenter) Nothing))-                 <|> (asciiCI "bottom" $> (a (Just PosBottom) Nothing))+            skipComments *> ((asciiCI "top" $> a (Just PosTop) Nothing)+                 <|> (asciiCI "center" $> a (Just PosCenter) Nothing)+                 <|> (asciiCI "bottom" $> a (Just PosBottom) Nothing)                  <|> ((a Nothing . Just) <$> percentageLength))         firsty = do-            a <- (asciiCI "top" $> (Position (Just PosTop) Nothing))-                 <|> (asciiCI "center" $> (Position (Just PosCenter) Nothing))-                 <|> (asciiCI "bottom" $> (Position (Just PosBottom) Nothing))+            a <- (asciiCI "top" $> Position (Just PosTop) Nothing)+                 <|> (asciiCI "center" $> Position (Just PosCenter) Nothing)+                 <|> (asciiCI "bottom" $> Position (Just PosBottom) Nothing)                  <|> ((Position Nothing . Just) <$> percentageLength)-            skipComments *> ((asciiCI "left" $> (a (Just PosLeft) Nothing))-                 <|> (asciiCI "center" $> (a (Just PosCenter) Nothing))-                 <|> (asciiCI "right" $> (a (Just PosRight) Nothing))+            skipComments *> ((asciiCI "left" $> a (Just PosLeft) Nothing)+                 <|> (asciiCI "center" $> a (Just PosCenter) Nothing)+                 <|> (asciiCI "right" $> a (Just PosRight) Nothing)                  <|> ((a Nothing . Just) <$> percentageLength))  pos4 :: Parser Position pos4 = firstx <|> firsty-  where posTop    = asciiCI "top" $> (Position (Just PosTop))-        posRight  = asciiCI "right" $> (Position (Just PosRight))-        posBottom = asciiCI "bottom" $> (Position (Just PosBottom))-        posLeft   = asciiCI "left" $> (Position (Just PosLeft))+  where posTop    = asciiCI "top" $> Position (Just PosTop)+        posRight  = asciiCI "right" $> Position (Just PosRight)+        posBottom = asciiCI "bottom" $> Position (Just PosBottom)+        posLeft   = asciiCI "left" $> Position (Just PosLeft)         firstx    = do-            x <- (asciiCI "center" $> (Position (Just PosCenter) Nothing))+            x <- (asciiCI "center" $> Position (Just PosCenter) Nothing)                  <|> ((posLeft <|> posRight) <*> (skipComments *> option Nothing (Just <$> percentageLength)))             _ <- skipComments-            (asciiCI "center" $> (x (Just PosCenter) Nothing))-                <|> (((asciiCI "top" $> (x $ Just PosTop)) <|> (asciiCI "bottom" $> (x (Just PosBottom))))+            (asciiCI "center" $> x (Just PosCenter) Nothing)+                <|> (((asciiCI "top" $> x (Just PosTop)) <|> (asciiCI "bottom" $> x (Just PosBottom)))                     <*> (skipComments *> option Nothing (Just <$> percentageLength)))         firsty = do-            x <- (asciiCI "center" $> (Position (Just PosCenter) Nothing))+            x <- (asciiCI "center" $> Position (Just PosCenter) Nothing)                  <|> ((posTop <|> posBottom) <*> (skipComments *> option Nothing (Just <$> percentageLength)))             _ <- skipComments-            (asciiCI "center" $> (x (Just PosCenter) Nothing))-                <|> (((asciiCI "left" $> (x $ Just PosLeft)) <|> (asciiCI "right" $> (x (Just PosRight))))+            (asciiCI "center" $> x (Just PosCenter) Nothing)+                <|> (((asciiCI "left" $> x (Just PosLeft)) <|> (asciiCI "right" $> x (Just PosRight)))                     <*> (skipComments *> option Nothing (Just <$> percentageLength)))  {-
src/Hasmin/Properties.hs view
@@ -15,7 +15,7 @@     ) where  import Data.Attoparsec.Text (parseOnly)-import Data.Text (Text, unpack)+import Data.Text (Text) import Control.Applicative ((<|>)) import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map
src/Hasmin/Types/Declaration.hs view
@@ -18,9 +18,11 @@ import Control.Monad ((>=>)) import Data.Map.Strict (Map) import Data.Monoid ((<>))+import Data.Foldable (toList) import Data.Maybe (fromMaybe)+import Data.Sequence (Seq, (|>)) import Data.List (find, delete, minimumBy, (\\))-import Data.Text (Text) +import Data.Text (Text) import Data.Text.Lazy.Builder (singleton, fromText) import qualified Data.Map.Strict as Map import qualified Data.Text as T@@ -37,15 +39,15 @@ import Hasmin.Types.Value import Hasmin.Utils -data Declaration = Declaration { propertyName :: Text +data Declaration = Declaration { propertyName :: Text                                , valueList :: Values                                , isImportant :: Bool  -- ends with !important                                , hasIEhack :: Bool    -- ends with \9                                } deriving (Eq, Show) instance ToText Declaration where-  toBuilder (Declaration p vs i h) = fromText p <> singleton ':' +  toBuilder (Declaration p vs i h) = fromText p <> singleton ':'       <> toBuilder vs <> imp <> (if h then " \\9" else mempty)-    where imp | i         = "!important" +    where imp | i         = "!important"               | otherwise = mempty  instance Minifiable Declaration where@@ -125,7 +127,7 @@   ]  -- Generic function to map some optimization to a property's values.-optimizeValues :: (Value -> Reader Config Value) +optimizeValues :: (Value -> Reader Config Value)                -> Declaration -> Reader Config Declaration optimizeValues f d@(Declaration _ vs _ _) = do     newV <- mapValues f vs@@ -134,7 +136,7 @@ -- converts 0% into 0 (of type <length>) -- Do NOT use it with height and max-height, since 0% /= 0 nullPercentageToLength :: Declaration -> Reader Config Declaration-nullPercentageToLength d = do +nullPercentageToLength d = do     conf <- ask     if shouldConvertNullPercentages conf        then optimizeValues f d@@ -142,7 +144,7 @@   where f :: Value -> Reader Config Value         f (PositionV p@(Position _ a _ b)) = pure . PositionV $             let stripPercentage Nothing  = Nothing-                stripPercentage (Just x) = if isZero x +                stripPercentage (Just x) = if isZero x                                               then l0                                               else Just x             in p { offset1 = stripPercentage a, offset2 = stripPercentage b }@@ -152,8 +154,8 @@                 zeroPercentageToLength x = PercentageV x         f (BgSizeV (BgSize x y)) = pure . BgSizeV $ BgSize (zeroPerToLength x) (fmap zeroPerToLength y)           where zeroPerToLength (Left (Left 0)) = Left $ Right (Distance 0 Q)-                zeroPerToLength z = z -        f x = pure x +                zeroPerToLength z = z+        f x = pure x  -- For word-spacing, normal computes to 0. -- For vertical-align, baseline is the same as 0.@@ -163,7 +165,7 @@ replaceWithZero :: Text -> Declaration -> Reader Config Declaration replaceWithZero s d@(Declaration p (Values v vs) _ _)     | not (null vs) = pure d -- Some error occured, since there should be only one value-    | otherwise     = +    | otherwise     =         case Map.lookup (T.toCaseFold p) propertiesTraits of           Just (iv, inhs) -> if f iv inhs == mkOther s                                 then pure $ d { valueList = Values (DistanceV (Distance 0 Q)) [] }@@ -171,19 +173,19 @@           Nothing         -> pure d   where f (Just (Values x _)) inh           | v == Initial || v == Unset && not inh = x-          | otherwise                             = v +          | otherwise                             = v         f _ _ = v  -- Converts the keywords "normal" and "bold" to 400 and 700, respectively. fontWeightOptimizer :: Declaration -> Reader Config Declaration-fontWeightOptimizer = optimizeValues f +fontWeightOptimizer = optimizeValues f   where f :: Value -> Reader Config Value         f x@(Other t) = do           conf <- ask           pure $ case fontweightSettings conf of                    FontWeightMinOn  -> replaceForSynonym t                    FontWeightMinOff -> x-        f x = pure x +        f x = pure x          replaceForSynonym :: TextV -> Value         replaceForSynonym t@@ -259,7 +261,7 @@  transformOrigin3 :: Value -> Value -> Value -> [Value] transformOrigin3 x y z-    | x == Other "top" || x == Other "bottom" +    | x == Other "top" || x == Other "bottom"       || y == Other "left" || y == Other "right" = fmap replaceKeywords [y, x, z]     | otherwise = fmap replaceKeywords [x, y, z]   where replaceKeywords :: Value -> Value@@ -268,7 +270,7 @@  -- transform-origin keyword meanings. transformOriginKeywords :: Map Text Value-transformOriginKeywords = Map.fromList +transformOriginKeywords = Map.fromList     [("top", DistanceV (Distance 0 Q))     ,("right", PercentageV (Percentage 100))     ,("bottom", PercentageV (Percentage 100))@@ -281,7 +283,7 @@ -- configurations. minifyDec :: Declaration -> Maybe Values -> Bool -> Declaration minifyDec d@(Declaration p vs _ _) mv inherits =-    case mv of +    case mv of       -- Use the found initial values to try to reduce the declaration       Just vals ->           case Map.lookup (T.toCaseFold p) declarationExceptions of@@ -290,7 +292,7 @@             Just f  -> f d vals inherits             Nothing -> reduceDeclaration d vals inherits       -- Property with no defined initial values. Try to reduce css-wide keywords-      Nothing   -> +      Nothing   ->           if not inherits && vs == initial || inherits && vs == inherit              then d { valueList = unset }              else d@@ -319,13 +321,17 @@  combineTransformFunctions :: Declaration -> Reader Config Declaration combineTransformFunctions d@(Declaration _ vs _ _) = do-    combinedFuncs <- combine $ fmap (\(TransformV x) -> x) tfuncs -    let newVals = fmap TransformV combinedFuncs ++ (decValues \\ tfuncs)+    combinedFuncs <- combine (toList tfValues)+    let newVals = fmap TransformV combinedFuncs ++ toList otherValues     pure $ d { valueList = mkValues newVals}-  where decValues     = valuesToList vs-        tfuncs = filter isTransformFunction decValues-        isTransformFunction (TransformV _) = True-        isTransformFunction _              = False+  where decValues               = valuesToList vs+        (tfValues, otherValues) = splitValues decValues+        splitValues = splitValues' (mempty, mempty)+          where splitValues' :: (Seq TransformFunction, Seq Value) -> [Value]+                             -> (Seq TransformFunction, Seq Value)+                splitValues' (ts, os) (TransformV x:xs) = splitValues' (ts |> x, os) xs+                splitValues' (ts, os) (x:xs)            = splitValues' (ts, os |> x) xs+                splitValues  (ts, os) []                = (ts, os)  backgroundSizeReduce :: Declaration -> Values -> Bool -> Declaration backgroundSizeReduce d@(Declaration _ vs _ _) initVals inherits =@@ -349,7 +355,7 @@ --    property that qualifies is border-bottom, and one that doesn't is --    font-synthesis (because it isn't a shorthand). reduceDeclaration :: Declaration -> Values -> Bool -> Declaration-reduceDeclaration d@(Declaration _ vs _ _) initVals inherits = +reduceDeclaration d@(Declaration _ vs _ _) initVals inherits =     case analyzeValueDifference vs initVals of       Just v  -> d {valueList = shortestEquiv v shortestInitialValue inherits}       Nothing -> d {valueList = minVal inherits shortestInitialValue}@@ -373,13 +379,13 @@     | textualLength globalKeyword <= textualLength vs = globalKeyword     | otherwise                                       = vs   where globalKeyword = mkValues [if not inherits then Unset else Initial]-    + -- Substract declaration values to the property's initial value list. -- If nothing remains (i.e. every value declared was an initial one and may be -- left implicit), then just replace it with whatever initial value is the -- shortest, otherwise whatever remains is the shortest equivalent declaration. analyzeValueDifference :: Values -> Values -> Maybe Values-analyzeValueDifference vs initVals = +analyzeValueDifference vs initVals =     case valuesDifference of       [] -> Nothing -- every value was an initial one       _  -> Just $ mkValues valuesDifference -- At least a value wasn't an initial one, or it was a css-wide keyword@@ -402,7 +408,7 @@ -- longhand later in the list, merges them. If it is a longhand overwritten by -- a shorthand, it deletes it. Otherwise, it keeps the value. In any case, it -- returns the new list to analyze and (if any) the value to keep.-solveClashes :: [Declaration] -> Declaration +solveClashes :: [Declaration] -> Declaration              -> PropertyInfo -> (Maybe Declaration, [Declaration]) solveClashes ds = solveClashes' ds ds @@ -413,7 +419,7 @@ solveClashes' newDs (laterDec:ds) dec pinfo     -- Do not remove vendor-prefixed values, which are probably fallbacks.     | hasVendorPrefix dec      = (Just dec, newDs)-    | hasVendorPrefix laterDec || hasIEhack dec /= hasIEhack laterDec = +    | hasVendorPrefix laterDec || hasIEhack dec /= hasIEhack laterDec =         solveClashes' newDs ds dec pinfo     | propertyName laterDec `elem` subproperties pinfo =         attemptMerge newDs ds dec laterDec pinfo@@ -442,7 +448,7 @@ isVendorPrefixedValue _                 = False  attemptMerge :: [Declaration] -> [Declaration] -> Declaration-             -> Declaration -> PropertyInfo +             -> Declaration -> PropertyInfo              -> (Maybe Declaration, [Declaration]) attemptMerge newDs ds dec laterDec pinfo =     case merge dec laterDec of@@ -464,19 +470,19 @@                                        ,("border-color", mergeIntoTRBL)                                        ,("border-width", mergeIntoTRBL)                                        ,("border-style", mergeIntoTRBL)-                                       --,("animation", +                                       --,("animation",                                        --,("background",-                                       --,("background-position", +                                       --,("background-position",                                        --,("border",-                                       --,("border-bottom", +                                       --,("border-bottom",                                        --,("border-image",-                                       --,("border-left", +                                       --,("border-left",                                        --,("border-radius"                                        --,("border-right",                                        --,("border-top"                                        --,("column-rule",                                        --,("columns",-                                       --,("flex",  +                                       --,("flex",                                        --,("flex-flow",                                        --,("font",                                        --,("grid",@@ -486,10 +492,10 @@                                        --,("grid-row",                                        --,("grid-template",                                        --,("list-style",-                                       --,("mask", +                                       --,("mask",                                        --,("outline",                                        --,("padding",-                                       --,("text-decoration", +                                       --,("text-decoration",                                        --,("text-emphasis"                                        --,("transition",                                        ]@@ -534,7 +540,7 @@       [t,r,b,l] -> reduce4 t r b l       [t,r,b]   -> reduce3 t r b       [t,r]     -> reduce2 t r-      _         -> d +      _         -> d   where reduce4 tv rv bv lv             | lv == rv  = reduce3 tv rv bv             | otherwise = d
src/Hasmin/Types/FilterFunction.hs view
@@ -86,6 +86,9 @@          then minifyPseudoShadow DropShadow a b c d          else pure s +minifyPseudoShadow :: (Minifiable b, Minifiable t1, Minifiable t2, Traversable t)+                   => (t2 -> t1 -> Maybe Distance -> t b -> b1)+                   -> t2 -> t1 -> Maybe Distance -> t b -> Reader Config b1 minifyPseudoShadow constr a b c d = do               x  <- minifyWith a               y  <- minifyWith b
src/Hasmin/Types/Gradient.hs view
@@ -8,14 +8,19 @@ -- Portability : non-portable
 --
 -----------------------------------------------------------------------------
-module Hasmin.Types.Gradient (
-    Gradient(..), Side(..), ColorStop(..), Size(..), Shape(..)
+module Hasmin.Types.Gradient
+    ( Gradient(..)
+    , Side(..)
+    , ColorStop(..)
+    , Size(..)
+    , Shape(..)
     ) where
 
 import Control.Monad.Reader (Reader, ask)
 import Data.Monoid ((<>))
 import Data.Text.Lazy.Builder (singleton)
 import Data.Maybe (catMaybes, fromJust, isNothing, isJust)
+import Data.Either (isLeft)
 import Hasmin.Config
 import Hasmin.Types.Class
 import Hasmin.Types.Color
@@ -295,6 +300,7 @@           handleEither (Left a) (Right s)  = angleSideEq a s
           handleEither (Right s) (Left a)  = angleSideEq a s
           handleEither s1 s2               = s1 == s2
+  _ == _ = error "Non supported gradient comparison"
 
 angleSideEq :: Angle -> SideOrCorner -> Bool
 angleSideEq (Angle 0 Deg) (TopSide, Nothing)      = True
src/Hasmin/Types/PercentageLength.hs view
@@ -8,8 +8,10 @@ -- Portability : non-portable -- ------------------------------------------------------------------------------module Hasmin.Types.PercentageLength (-    PercentageLength, isZero, isNonZeroPercentage+module Hasmin.Types.PercentageLength+    ( PercentageLength+    , isZero+    , isNonZeroPercentage     ) where  import Hasmin.Types.Dimension
src/Hasmin/Types/Position.hs view
@@ -25,9 +25,9 @@ import Hasmin.Types.PercentageLength import Hasmin.Utils -data PosKeyword = PosCenter +data PosKeyword = PosCenter                 | PosLeft-                | PosRight +                | PosRight                 | PosTop                 | PosBottom   deriving (Eq, Show)@@ -92,7 +92,7 @@                           then Position Nothing l0 Nothing l0                           else case b of                                  Left 50  -> Position Nothing l0 Nothing Nothing-                                 Left 100 -> Position Nothing l0 Nothing p100 +                                 Left 100 -> Position Nothing l0 Nothing p100                                  _        -> p { offset1 = l0 }           | isZero b = case a of                          Left 50  -> Position (Just PosTop) Nothing Nothing Nothing@@ -100,7 +100,7 @@                          _        -> p { offset2 = l0 }           | b == Left 50 = Position Nothing (Just a) Nothing Nothing           | otherwise    = p-minifyPosition (Position (Just x) (Just y) Nothing Nothing) = +minifyPosition (Position (Just x) (Just y) Nothing Nothing) =   uncurry (\a b -> Position a b Nothing Nothing) (minAxis x y) -- 2 keywords minifyPosition p@(Position (Just x) Nothing (Just y) Nothing) = f x y@@ -118,7 +118,7 @@         f PosCenter PosTop    = p { origin1 = Just PosTop, origin2 = Nothing }         -- 'right top' and 'top right' == '100% 0%'.         f PosRight PosTop     = Position Nothing p100 Nothing l0-        f PosTop PosRight     = Position Nothing p100 Nothing l0 +        f PosTop PosRight     = Position Nothing p100 Nothing l0         -- 'right', 'right center', 'center right' == '100% 50%'.         f PosRight PosCenter  = Position Nothing p100 Nothing Nothing         f PosCenter PosRight  = Position Nothing p100 Nothing Nothing@@ -135,40 +135,40 @@ -- keyword pl keyword syntax minifyPosition p@(Position (Just x) (Just y) (Just z) Nothing)     | x == PosTop || x == PosBottom || z == PosLeft || z == PosRight =-        minifyPosition $ Position (Just z) Nothing (Just x) (Just y) +        minifyPosition $ Position (Just z) Nothing (Just x) (Just y)     | otherwise = minifyPos3 x y z   where minifyPos3 PosLeft b PosBottom-          | isZero b     = Position Nothing l0 Nothing p100 -          | b == Left 50 = Position (Just PosBottom) Nothing Nothing Nothing -          | otherwise    = Position Nothing (Just b) Nothing p100 +          | isZero b     = Position Nothing l0 Nothing p100+          | b == Left 50 = Position (Just PosBottom) Nothing Nothing Nothing+          | otherwise    = Position Nothing (Just b) Nothing p100         minifyPos3 PosLeft b PosTop-          | isZero b     = Position Nothing l0 Nothing l0 -          | b == Left 50 = Position (Just PosTop) Nothing Nothing Nothing -          | otherwise    = Position Nothing (Just b) Nothing l0 +          | isZero b     = Position Nothing l0 Nothing l0+          | b == Left 50 = Position (Just PosTop) Nothing Nothing Nothing+          | otherwise    = Position Nothing (Just b) Nothing l0         minifyPos3 PosLeft b PosCenter-          | isZero b     = Position Nothing l0 Nothing Nothing -          | b == Left 50 = Position Nothing p50 Nothing Nothing -          | otherwise    = Position Nothing (Just b) Nothing p50 +          | isZero b     = Position Nothing l0 Nothing Nothing+          | b == Left 50 = Position Nothing p50 Nothing Nothing+          | otherwise    = Position Nothing (Just b) Nothing p50         minifyPos3 PosRight b PosTop-          | isZero b     = Position Nothing p100 Nothing l0 -          | otherwise    = Position (Just PosRight) (Just b) Nothing l0 +          | isZero b     = Position Nothing p100 Nothing l0+          | otherwise    = Position (Just PosRight) (Just b) Nothing l0         minifyPos3 PosRight b PosBottom-          | isZero b     = Position Nothing p100 Nothing p100 +          | isZero b     = Position Nothing p100 Nothing p100           | otherwise    = Position (Just PosRight) (Just b) Nothing p100         minifyPos3 PosRight b PosCenter-          | isZero b     = Position Nothing p100 Nothing Nothing -          | otherwise    = Position (Just PosRight) (Just b) Nothing p50 +          | isZero b     = Position Nothing p100 Nothing Nothing+          | otherwise    = Position (Just PosRight) (Just b) Nothing p50         minifyPos3 _ _ _ = p minifyPosition p@(Position (Just c) Nothing (Just a) (Just b)) = f $ minAxis a b-  where f (x, y) +  where f (x, y)           | c == PosLeft && x == Just PosTop && isJust y = minifyPosition $ Position Nothing l0 Nothing y           | otherwise = if Just a == x && Just b == y                            then p                            else minifyPosition $ p {origin2 = x, offset2 = y } -- 4 value syntax-minifyPosition p@(Position (Just PosLeft) (Just _) (Just PosTop) (Just _)) = +minifyPosition p@(Position (Just PosLeft) (Just _) (Just PosTop) (Just _)) =     minifyPosition p { origin1 = Nothing, origin2 = Nothing }-minifyPosition (Position (Just a) (Just b) (Just c) (Just d)) = +minifyPosition (Position (Just a) (Just b) (Just c) (Just d)) =     minifyPos4 a b c d minifyPosition p = p @@ -192,10 +192,10 @@         minifyPos4' a b c d = Position (Just a) (Just b) (Just c) (Just d)  minAxis :: PosKeyword -> PercentageLength -> (Maybe PosKeyword, Maybe PercentageLength)-minAxis PosTop x = +minAxis PosTop x =     case x of       Left 50 -> (Just PosCenter, Nothing)-      b       -> if isZero b +      b       -> if isZero b                     then (Just PosTop, Nothing)                     else (Just PosTop, Just x) minAxis PosLeft x =@@ -213,7 +213,7 @@ minAxis PosCenter x = (Just PosCenter, Just x)  instance Eq Position where-  a == b = minify a `equals` minify b+  x == y = minify x `equals` minify y     where equals (Position a b c d) (Position e f g h) =             a == e && b == f && c == g && d == h 
src/Hasmin/Types/String.hs view
@@ -18,10 +18,10 @@  import Control.Applicative (liftA2) import Control.Monad.Reader (ask, Reader)-import Data.Attoparsec.Text (Parser, parse, IResult(..), maybeResult, feed)+import Data.Attoparsec.Text (Parser, parse, IResult(Done, Partial, Fail), maybeResult, feed) import Data.Monoid ((<>)) import Data.Text (Text)-import Data.Text.Lazy.Builder as LB+import Data.Text.Lazy.Builder (singleton, fromText, toLazyText) import qualified Data.Attoparsec.Text as A import qualified Data.Char as C import qualified Data.Text as T
src/Hasmin/Types/Stylesheet.hs view
@@ -17,17 +17,19 @@     , SupportsCondition(..)     , SupportsCondInParens(..)     , isEmpty+    , minifyRules     ) where -import Control.Monad.Reader (Reader, ask) import Control.Applicative (liftA2)+import Control.Monad ((>=>))+import Control.Monad.Reader (Reader, ask) import Data.Monoid ((<>)) import Data.Text (Text) import Data.Text.Lazy.Builder (singleton, fromText, Builder) import Data.List (sortBy, (\\)) import Data.Map.Strict (Map)-import Data.List.NonEmpty (NonEmpty((:|)))-import qualified Data.List.NonEmpty as NE+import Data.List.NonEmpty (NonEmpty)+import Data.Foldable (toList) import qualified Data.Map.Strict as Map import qualified Data.Set as S import qualified Data.Text as T@@ -39,7 +41,6 @@ import Hasmin.Types.Declaration import Hasmin.Types.String import Hasmin.Types.Numeric-import Hasmin.Types.Dimension import Hasmin.Utils  -- | Data type for media queries. For the syntax, see@@ -114,7 +115,7 @@           | AtBlockWithRules Text [Rule]           | AtBlockWithDec Text [Declaration]           | StyleRule [Selector] [Declaration]- deriving (Show)+ deriving (Eq, Show) instance ToText Rule where   toBuilder (AtMedia mqs rs) = "@media " <> mconcatIntersperse toBuilder (singleton ',') mqs       <> singleton '{' <> mconcat (fmap toBuilder rs) <> singleton '}'@@ -257,7 +258,7 @@                        | And SupportsCondInParens (NonEmpty SupportsCondInParens)                        | Or SupportsCondInParens (NonEmpty SupportsCondInParens)                        | Parens SupportsCondInParens-  deriving (Show)+  deriving (Eq, Show) instance ToText SupportsCondition where   toBuilder (Not x)    = "not " <> toBuilder x   toBuilder (And x y)  = appendWith " and " x y@@ -279,7 +280,7 @@       ParensDec y        -> (Not . ParensDec) <$> pure y  appendWith :: Builder -> SupportsCondInParens -> NonEmpty SupportsCondInParens -> Builder-appendWith s x y = toBuilder x <> s <> mconcatIntersperse toBuilder s (NE.toList y)+appendWith s x y = toBuilder x <> s <> mconcatIntersperse toBuilder s (toList y)  -- Note that "general_enclosed" is not included, because, per the spec: --@@ -291,7 +292,7 @@ -- parenthesized expressions that can evaluate to true. data SupportsCondInParens = ParensCond SupportsCondition                           | ParensDec Declaration-  deriving (Show)+  deriving (Eq, Show) instance ToText SupportsCondInParens where   toBuilder (ParensDec x)  = "(" <> toBuilder x <> ")"   toBuilder (ParensCond x) = "(" <> toBuilder x <> ")"@@ -299,8 +300,28 @@   minifyWith (ParensDec x) = ParensDec <$> minifyWith x   minifyWith  (ParensCond x)  = ParensCond <$> minifyWith x -data GeneralEnclosed = GEFunction-  deriving (Show)+combineAdjacentMediaQueries :: [Rule] -> [Rule]+combineAdjacentMediaQueries (a@(AtMedia mqs es) : b@(AtMedia mqs2 es2) : xs)+    | mqs == mqs2 = combineAdjacentMediaQueries (AtMedia mqs (es ++ es2) : xs)+    | otherwise   = a : combineAdjacentMediaQueries (b:xs)+combineAdjacentMediaQueries (x:xs) = x : combineAdjacentMediaQueries xs+combineAdjacentMediaQueries [] = []++-- Set of functions to minify rules+minifyRules :: [Rule] -> Reader Config [Rule]+minifyRules = handleAdjacentMediaQueries+          >=> handleEmptyBlocks+          >=> traverse minifyWith -- minify rules individually+  where handleEmptyBlocks :: [Rule] -> Reader Config [Rule]+        handleEmptyBlocks rs = do+          conf <- ask+          pure $ if shouldRemoveEmptyBlocks conf+                    then filter (not . isEmpty) rs+                    else rs+        handleAdjacentMediaQueries :: [Rule] -> Reader Config [Rule]+        handleAdjacentMediaQueries rs = do+          conf <- ask+          pure $ combineAdjacentMediaQueries rs  {- supports_rule
src/Hasmin/Types/TransformFunction.hs view
@@ -20,6 +20,7 @@ import Control.Monad.Reader (mapReader, Reader, ask, local)
 import Control.Applicative (liftA2)
 import Data.Monoid ((<>))
+import Data.Either (isRight)
 import qualified Data.Text as T
 import Data.Number.FixedFunctions (sin, cos, acos, tan, atan)
 import Prelude hiding (sin, cos, acos, tan, atan)
src/Hasmin/Types/Value.hs view
@@ -192,13 +192,8 @@   minifyWith (ShadowV s)      = ShadowV <$> minifyWith s   minifyWith (ShadowText l1 l2 ml mc) = minifyPseudoShadow ShadowText l1 l2 ml mc   minifyWith (BgLayer img pos sz rst att b1 b2) = do-      conf <- ask-      i <- handleImage img-      s <- handleBgSize sz-      p <- let cannotRemovePos = isJust s -- can only be removed if there is no later <bg-size>-           in handlePosition cannotRemovePos pos-      r <- handleRepeatStyle rst-      a <- handleAttachment att+      -- conf <- ask+      (i,s,p,r,a) <- minifyBgLayer img pos sz rst att       (bgOrigin, bgClip) <- handleBoxes b1 b2       pure $ if isNothing i && isNothing p && isNothing s && isNothing r                 && isNothing a && isNothing bgOrigin && isNothing bgClip@@ -207,13 +202,8 @@                 then BgLayer (Just $ mkOther "none") p s r a bgOrigin bgClip                 else BgLayer i p s r a bgOrigin bgClip   minifyWith (FinalBgLayer img pos sz rst att b1 b2 col) = do-      conf <- ask-      i <- handleImage img-      s <- handleBgSize sz-      p <- let cannotRemovePos = isJust s -- can only be removed if there is no later <bg-size>-           in handlePosition cannotRemovePos pos-      r <- handleRepeatStyle rst-      a <- handleAttachment att+      -- conf <- ask+      (i,s,p,r,a) <- minifyBgLayer img pos sz rst att       c <- handleColor col       (bgOrigin, bgClip) <- handleBoxes b1 b2       pure $ if isNothing i && isNothing p && isNothing s && isNothing r@@ -348,16 +338,16 @@  handlePosition :: Bool -> Maybe Position -> Reader Config (Maybe Position) handlePosition _ Nothing = pure Nothing-handlePosition cannotRemovePos (Just x)-    | cannotRemovePos = Just <$> minifyWith x+handlePosition cannotRemovePos (Just p)+    | cannotRemovePos = Just <$> minifyWith p     | otherwise    = do         conf <- ask-        mx   <- minifyWith x-        pure $ if mx == Position Nothing l0 Nothing l0+        mp   <- minifyWith p+        pure $ if mp == Position Nothing l0 Nothing l0                   then Nothing                   else Just $ if True {- shouldMinifyPosition conf -}-                                 then mx-                                 else x+                                 then mp+                                 else p  data Values = Values Value [(Separator, Value)]   deriving (Show, Eq)@@ -439,3 +429,14 @@               else StringV ffamily optimizeFontFamily x = pure x +minifyBgLayer :: Maybe Value -> Maybe Position -> Maybe BgSize+              -> Maybe RepeatStyle -> Maybe TextV+              -> Reader Config (Maybe Value, Maybe BgSize, Maybe Position, Maybe RepeatStyle, Maybe TextV)+minifyBgLayer img pos sz rst att = do+      i <- handleImage img+      s <- handleBgSize sz+      p <- let cannotRemovePos = isJust s -- can only be removed if there is no later <bg-size>+           in handlePosition cannotRemovePos pos+      r <- handleRepeatStyle rst+      a <- handleAttachment att+      pure (i,s,p,r,a)
src/Hasmin/Utils.hs view
@@ -9,9 +9,7 @@ -- ----------------------------------------------------------------------------- module Hasmin.Utils-    ( isRight-    , isLeft-    , epsilon+    ( epsilon     , eps     , fromLeft'     , fromRight'@@ -40,8 +38,9 @@              | otherwise = y <> mconcatIntersperse toMonoid y xs  replaceAt :: Int -> a -> [a] -> [a]-replaceAt i v ls = let (a, _:vs) = splitAt i ls-                   in a ++ (v:vs)+replaceAt i v ls = case splitAt i ls of+                    (xs, _:vs) -> xs ++ (v:vs)+                    (xs, [])   -> xs  fromRight' :: Either a b -> b fromRight' (Right x) = x@@ -50,14 +49,6 @@ fromLeft' :: Either a b -> a fromLeft' (Left x) = x fromLeft' _        = error "fromLeft'"--isRight :: Either a b -> Bool-isRight Right{} = True-isRight _       = False--isLeft :: Either a b -> Bool-isLeft Left{} = True-isLeft _      = False  -- TODO: Find out how precise is enough. -- Can we use double and round when printing?
tests/Hasmin/TestUtils.hs view
@@ -4,6 +4,7 @@       module Hasmin.TestUtils     , module Test.QuickCheck     , module Test.Hspec+    , module Test.Hspec.Attoparsec     ) where  import Test.Hspec@@ -39,7 +40,8 @@  matchSpec :: ToText a => Parser a -> (Text, Text) -> Spec matchSpec parser (textToParse, expectedResult) =-  it (unpack textToParse) $ (toText <$> (textToParse ~> parser)) `parseSatisfies` (== expectedResult)+  it (unpack textToParse) $ +    (toText <$> (textToParse ~> parser)) `parseSatisfies` (== expectedResult)  mkGen :: [a] -> Gen a mkGen xs = (xs !!) <$> choose (0, length xs - 1)
tests/Hasmin/Types/StylesheetSpec.hs view
@@ -2,12 +2,26 @@  module Hasmin.Types.StylesheetSpec where -import Data.Text (Text)+import Data.Text (Text, unpack)  import Hasmin.Parser.Internal import Hasmin.TestUtils import Hasmin.Types.Class+import Hasmin.Utils+import Hasmin +combineAdjacentMediaQueriesTests :: Spec+combineAdjacentMediaQueriesTests =+     describe "Combines adjacent @media rules" $+      mapM_ f combineAdjacentMediaQueriesTestsInfo+  where f (t1, t2) = it (unpack t1) $ minifyCSS t1 `parseSatisfies` (== t2)++combineAdjacentMediaQueriesTestsInfo :: [(Text, Text)]+combineAdjacentMediaQueriesTestsInfo =+  [("@media all and (min-width:24rem){.Fz\\(s2\\)\\@xs{font-size:1.2rem;}}@media all and (min-width: 24rem){.Px\\(s04\\)\\@xs{padding-left:.25rem; padding-right:.25rem;}}",+    "@media all and (min-width:24rem){.Fz\\(s2\\)\\@xs{font-size:1.2rem}.Px\\(s04\\)\\@xs{padding-left:.25rem;padding-right:.25rem}}")+  ]+ atRuleTests :: Spec atRuleTests = do     describe "at rules parsing and printing" $@@ -75,7 +89,9 @@   ]  spec :: Spec-spec = atRuleTests+spec = do atRuleTests+          combineAdjacentMediaQueriesTests+  main :: IO () main = hspec spec