diff options
author | Florian Dold <florian.dold@gmail.com> | 2017-05-03 15:35:00 +0200 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2017-05-03 15:35:00 +0200 |
commit | de98e0b232509d5f40c135d540a70e415272ff85 (patch) | |
tree | a79222a5b58484ab3b80d18efcaaa7ccc4769b33 /node_modules/clean-css | |
parent | e0c9d480a73fa629c1e4a47d3e721f1d2d345406 (diff) | |
download | wallet-core-de98e0b232509d5f40c135d540a70e415272ff85.tar.xz |
node_modules
Diffstat (limited to 'node_modules/clean-css')
96 files changed, 11242 insertions, 0 deletions
diff --git a/node_modules/clean-css/History.md b/node_modules/clean-css/History.md new file mode 100644 index 000000000..3cf6682ef --- /dev/null +++ b/node_modules/clean-css/History.md @@ -0,0 +1,1240 @@ +[4.0.12 / 2017-04-12](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.11...v4.0.12) +================== + +* Fixed issue [#930](https://github.com/jakubpawlowicz/clean-css/issues/930) - regression in tidying selectors. + +[4.0.11 / 2017-04-04](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.10...v4.0.11) +================== + +* Fixed issue [#924](https://github.com/jakubpawlowicz/clean-css/issues/924) - `hsl` zero percent eager optimization. + +[4.0.10 / 2017-03-22](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.9...v4.0.10) +================== + +* Fixed issue [#917](https://github.com/jakubpawlowicz/clean-css/issues/917) - prevents grid area unquoting. +* Backported [#916](https://github.com/jakubpawlowicz/clean-css/issues/916) - maximum number of merged selectors. +* Refixed issue [#556](https://github.com/jakubpawlowicz/clean-css/issues/917) - IE backslash hacks. + +[4.0.9 / 2017-03-15](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.8...v4.0.9) +================== + +* Fixed issue [#902](https://github.com/jakubpawlowicz/clean-css/issues/902) - case insensitive attribute matchers. +* Fixed issue [#903](https://github.com/jakubpawlowicz/clean-css/issues/903) - web UI and source maps. +* Fixed issue [#907](https://github.com/jakubpawlowicz/clean-css/issues/907) - space after closing brace in `@supports`. +* Fixed issue [#910](https://github.com/jakubpawlowicz/clean-css/issues/910) - too aggressive precision optimizations. + +[4.0.8 / 2017-02-22](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.7...v4.0.8) +================== + +* Fixes edge case in remote stylesheet fetching. +* Fixed issue [#899](https://github.com/jakubpawlowicz/clean-css/issues/899) - regression in optimizing pseudo class arguments. + +[4.0.7 / 2017-02-14](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.6...v4.0.7) +================== + +* Fixed issue [#891](https://github.com/jakubpawlowicz/clean-css/issues/891) - merging vendor-prefixed pseudo-classes. + +[4.0.6 / 2017-02-10](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.5...v4.0.6) +================== + +* Fixed issue [#885](https://github.com/jakubpawlowicz/clean-css/issues/885) - unquoting `font-feature-settings`. + +[4.0.5 / 2017-02-07](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.4...v4.0.5) +================== + +* Fixed issue [#884](https://github.com/jakubpawlowicz/clean-css/issues/884) - handling absolute paths on Windows. +* Fixed issue [#881](https://github.com/jakubpawlowicz/clean-css/issues/881) - incorrect `require` arity. +* Fixed issue [#880](https://github.com/jakubpawlowicz/clean-css/issues/880) - incorrect token type identification. + +[4.0.4 / 2017-02-04](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.3...v4.0.4) +================== + +* Fixed issue [#879](https://github.com/jakubpawlowicz/clean-css/issues/879) - incorrect handling of spaces in paths. +* Fixed issue [#878](https://github.com/jakubpawlowicz/clean-css/issues/878) - invalid double backslash tokenization. + +[4.0.3 / 2017-01-30](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.2...v4.0.3) +================== + +* Fixed issue [#875](https://github.com/jakubpawlowicz/clean-css/issues/875) - invalid traversing in semanting merging. + +[4.0.2 / 2017-01-26](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.1...v4.0.2) +================== + +* Fixed issue [#874](https://github.com/jakubpawlowicz/clean-css/issues/874) - regression in at-rule tokenization. + +[4.0.1 / 2017-01-25](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.0...v4.0.1) +================== + +* Fixed issue [#866](https://github.com/jakubpawlowicz/clean-css/issues/866) - edge case in `inline` option. +* Fixed issue [#867](https://github.com/jakubpawlowicz/clean-css/issues/867) - skip optimizing variable values. +* Fixed issue [#868](https://github.com/jakubpawlowicz/clean-css/issues/868) - accept absolute paths in input hash. +* Fixed issue [#872](https://github.com/jakubpawlowicz/clean-css/issues/872) - edge case in CSS tokenization. + +[4.0.0 / 2017-01-23](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.24...v4.0.0) +================== + +* Adds more detailed error & warning messages on top of the new tokenizer. +* Disables restructuring optimizations by default until optimized in #533. +* Fixes a bug ignoring incorrect properties in complex restructuring. +* Requires Node.js 4.0+ to run. +* Removes `benchmark` API option as total time is always reported under `stats` property. +* Removes `debug` API switch as stats are always gathered and available under `stats` property. +* Replaces the old tokenizer with a new one which doesn't use any escaping. +* Replaces the old `@import` inlining with one on top of the new tokenizer. +* Re-enables `background-(clip|origin|size)` merging with `background` shorthand. +* Simplifies URL rebasing with a single `rebaseTo` option in API or inferred from `--output` in CLI. +* Splits `inliner` option into `inlineRequest` and `inlineTimeout`. +* Fixed issue [#209](https://github.com/jakubpawlowicz/clean-css/issues/209) - adds output formatting via `format` flag. +* Fixed issue [#290](https://github.com/jakubpawlowicz/clean-css/issues/290) - removes aggressive merging. +* Fixed issue [#432](https://github.com/jakubpawlowicz/clean-css/issues/432) - adds URLs normalization. +* Fixed issue [#460](https://github.com/jakubpawlowicz/clean-css/issues/460) - unescaped semicolon in selector. +* Fixed issue [#657](https://github.com/jakubpawlowicz/clean-css/issues/657) - adds property name validation. +* Fixed issue [#685](https://github.com/jakubpawlowicz/clean-css/issues/685) - adds lowercasing hex colors optimization. +* Fixed issue [#686](https://github.com/jakubpawlowicz/clean-css/issues/686) - adds rounding precision for all units. +* Fixed issue [#703](https://github.com/jakubpawlowicz/clean-css/issues/703) - changes default IE compatibility to 10+. +* Fixed issue [#731](https://github.com/jakubpawlowicz/clean-css/issues/731) - adds granular control over level 2 optimizations. +* Fixed issue [#739](https://github.com/jakubpawlowicz/clean-css/issues/739) - error when a closing brace is missing. +* Fixed issue [#750](https://github.com/jakubpawlowicz/clean-css/issues/750) - allows `width` overriding. +* Fixed issue [#756](https://github.com/jakubpawlowicz/clean-css/issues/756) - adds disabling font-weight optimizations. +* Fixed issue [#758](https://github.com/jakubpawlowicz/clean-css/issues/758) - ignores rules with empty selector. +* Fixed issue [#767](https://github.com/jakubpawlowicz/clean-css/issues/767) - disables remote `@import` inlining by default. +* Fixed issue [#773](https://github.com/jakubpawlowicz/clean-css/issues/773) - adds reordering based on selector specificity. +* Fixed issue [#785](https://github.com/jakubpawlowicz/clean-css/issues/785) - adds `@font-face` de-duplication. +* Fixed issue [#791](https://github.com/jakubpawlowicz/clean-css/issues/791) - resolves imports in-memory if possible. +* Fixed issue [#796](https://github.com/jakubpawlowicz/clean-css/issues/796) - semantic merging for `@media` blocks. +* Fixed issue [#801](https://github.com/jakubpawlowicz/clean-css/issues/801) - smarter `@import` inlining. +* Fixed issue [#806](https://github.com/jakubpawlowicz/clean-css/issues/806) - skip optimizing variable properties. +* Fixed issue [#817](https://github.com/jakubpawlowicz/clean-css/issues/817) - makes `off` disable rounding. +* Fixed issue [#818](https://github.com/jakubpawlowicz/clean-css/issues/818) - disables `px` rounding by default. +* Fixed issue [#828](https://github.com/jakubpawlowicz/clean-css/issues/828) - `-chrome-` hack support. +* Fixed issue [#829](https://github.com/jakubpawlowicz/clean-css/issues/829) - adds more strict selector merging rules. +* Fixed issue [#834](https://github.com/jakubpawlowicz/clean-css/issues/834) - adds extra line break in nested blocks. +* Fixed issue [#836](https://github.com/jakubpawlowicz/clean-css/issues/836) - enables level `0` optimizations. +* Fixed issue [#839](https://github.com/jakubpawlowicz/clean-css/issues/839) - allows URIs in import inlining rules. +* Fixed issue [#840](https://github.com/jakubpawlowicz/clean-css/issues/840) - allows input source map as map object. +* Fixed issue [#843](https://github.com/jakubpawlowicz/clean-css/issues/843) - regression in selector handling. +* Fixed issue [#845](https://github.com/jakubpawlowicz/clean-css/issues/845) - web compatibility of 4.0 branch. +* Fixed issue [#847](https://github.com/jakubpawlowicz/clean-css/issues/847) - regression in handling invalid selectors. +* Fixed issue [#849](https://github.com/jakubpawlowicz/clean-css/issues/849) - disables inlining protocol-less resources. +* Fixed issue [#856](https://github.com/jakubpawlowicz/clean-css/issues/856) - allows `minify` to return a promise. +* Fixed issue [#857](https://github.com/jakubpawlowicz/clean-css/issues/857) - normalizes CleanCSS API interface. +* Fixed issue [#863](https://github.com/jakubpawlowicz/clean-css/issues/863) - adds `transform` callback for custom optimizations. + +[3.4.24 / 2017-01-20](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.23...v3.4.24) +================== + +* Fixed issue [#859](https://github.com/jakubpawlowicz/clean-css/issues/859) - avoid `-webkit-border-radius` optimizations. + +[3.4.23 / 2016-12-17](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.22...v3.4.23) +================== + +* Fixed issue [#844](https://github.com/jakubpawlowicz/clean-css/issues/844) - regression in property values extraction. + +[3.4.22 / 2016-12-12](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.21...v3.4.22) +================== + +* Fixed issue [#841](https://github.com/jakubpawlowicz/clean-css/issues/841) - disabled importing and files passed as array. +* Ignores `@import` at-rules if appearing after any non-`@import` rules. + +[3.4.21 / 2016-11-16](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.20...v3.4.21) +================== + +* Fixed issue [#821](https://github.com/jakubpawlowicz/clean-css/issues/821) - reducing non-adjacent rules. +* Fixed issue [#830](https://github.com/jakubpawlowicz/clean-css/issues/830) - reordering border-* properties. +* Fixed issue [#833](https://github.com/jakubpawlowicz/clean-css/issues/833) - moving `@media` queries. + +[3.4.20 / 2016-09-26](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.19...v3.4.20) +================== + +* Fixed issue [#814](https://github.com/jakubpawlowicz/clean-css/issues/814) - `:selection` rule merging. + +[3.4.19 / 2016-07-25](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.18...v3.4.19) +================== + +* Fixed issue [#795](https://github.com/jakubpawlowicz/clean-css/issues/795) - `!important` and override compacting. + +[3.4.18 / 2016-06-15](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.17...v3.4.18) +================== + +* Fixed issue [#787](https://github.com/jakubpawlowicz/clean-css/issues/787) - regression in processing data URIs. + +[3.4.17 / 2016-06-04](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.16...v3.4.17) +================== + +* Fixed issue [#783](https://github.com/jakubpawlowicz/clean-css/issues/783) - regression in processing data URIs. + +[3.4.16 / 2016-06-02](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.15...v3.4.16) +================== + +* Fixed issue [#781](https://github.com/jakubpawlowicz/clean-css/issues/781) - regression in override compacting. +* Fixed issue [#782](https://github.com/jakubpawlowicz/clean-css/issues/782) - regression in processing data URIs. + +[3.4.15 / 2016-06-01](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.14...v3.4.15) +================== + +* Fixed issue [#776](https://github.com/jakubpawlowicz/clean-css/issues/776) - edge case in quoted data URIs. +* Fixed issue [#779](https://github.com/jakubpawlowicz/clean-css/issues/779) - merging `background-(position|size)`. +* Fixed issue [#780](https://github.com/jakubpawlowicz/clean-css/issues/780) - space after inlined variables. + +[3.4.14 / 2016-05-31](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.13...v3.4.14) +================== + +* Fixed issue [#751](https://github.com/jakubpawlowicz/clean-css/issues/751) - stringifying CSS variables. +* Fixed issue [#763](https://github.com/jakubpawlowicz/clean-css/issues/763) - data URI SVG and quoting. +* Fixed issue [#765](https://github.com/jakubpawlowicz/clean-css/issues/765) - two values of border-radius. +* Fixed issue [#768](https://github.com/jakubpawlowicz/clean-css/issues/768) - invalid border-radius property. + +[3.4.13 / 2016-05-23](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.12...v3.4.13) +================== + +* Fixed issue [#734](https://github.com/jakubpawlowicz/clean-css/issues/769) - Node.js 6.x support. + +[3.4.12 / 2016-04-09](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.11...v3.4.12) +================== + +* Fixed issue [#734](https://github.com/jakubpawlowicz/clean-css/issues/734) - `--root` option edge case. +* Fixed issue [#758](https://github.com/jakubpawlowicz/clean-css/issues/758) - treats empty rule as unmergeable. + +[3.4.11 / 2016-04-01](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.10...v3.4.11) +================== + +* Fixed issue [#738](https://github.com/jakubpawlowicz/clean-css/issues/738) - edge case in comment processing. +* Fixed issue [#741](https://github.com/jakubpawlowicz/clean-css/issues/741) - HTTP proxy with HTTPS inlining. +* Fixed issue [#743](https://github.com/jakubpawlowicz/clean-css/issues/743) - background shorthand and source maps. +* Fixed issue [#745](https://github.com/jakubpawlowicz/clean-css/issues/745) - matching mixed case `!important`. + +[3.4.10 / 2016-02-29](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.9...v3.4.10) +================== + +* Fixed issue [#735](https://github.com/jakubpawlowicz/clean-css/issues/735) - whitespace removal with escaped chars. + +[3.4.9 / 2016-01-03](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.8...v3.4.9) +================== + +* Sped up merging by body advanced optimization. +* Fixed issue [#693](https://github.com/jakubpawlowicz/clean-css/issues/693) - restructuring edge case. +* Fixed issue [#711](https://github.com/jakubpawlowicz/clean-css/issues/711) - border fuzzy matching. +* Fixed issue [#714](https://github.com/jakubpawlowicz/clean-css/issues/714) - stringifying property level at rules. +* Fixed issue [#715](https://github.com/jakubpawlowicz/clean-css/issues/715) - stack too deep in comment scan. + +[3.4.8 / 2015-11-13](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.7...v3.4.8) +================== + +* Fixed issue [#676](https://github.com/jakubpawlowicz/clean-css/issues/676) - fuzzy matching unqoted data URIs. + +[3.4.7 / 2015-11-10](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.6...v3.4.7) +================== + +* Fixed issue [#692](https://github.com/jakubpawlowicz/clean-css/issues/692) - edge case in URL quoting. +* Fixed issue [#695](https://github.com/jakubpawlowicz/clean-css/issues/695) - shorthand overriding edge case. +* Fixed issue [#699](https://github.com/jakubpawlowicz/clean-css/issues/699) - IE9 transparent hack. +* Fixed issue [#701](https://github.com/jakubpawlowicz/clean-css/issues/701) - `url` quoting with hash arguments. + +[3.4.6 / 2015-10-14](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.5...v3.4.6) +================== + +* Fixed issue [#679](https://github.com/jakubpawlowicz/clean-css/issues/679) - wrong rebasing of remote URLs. + +[3.4.5 / 2015-09-28](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.4...v3.4.5) +================== + +* Fixed issue [#681](https://github.com/jakubpawlowicz/clean-css/issues/681) - property inheritance & restructuring. +* Fixed issue [#675](https://github.com/jakubpawlowicz/clean-css/issues/675) - overriding with `!important`. + +[3.4.4 / 2015-09-21](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.3...v3.4.4) +================== + +* Fixed issue [#626](https://github.com/jakubpawlowicz/clean-css/issues/626) - edge case in import rebasing. +* Fixed issue [#674](https://github.com/jakubpawlowicz/clean-css/issues/674) - adjacent merging order. + +[3.4.3 / 2015-09-15](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.2...v3.4.3) +================== + +* Fixed issue [#668](https://github.com/jakubpawlowicz/clean-css/issues/668) - node v4 path.join. +* Fixed issue [#669](https://github.com/jakubpawlowicz/clean-css/issues/669) - adjacent overriding with `!important`. + +[3.4.2 / 2015-09-14](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.1...v3.4.2) +================== + +* Fixed issue [#598](https://github.com/jakubpawlowicz/clean-css/issues/598) - restructuring border properties. +* Fixed issue [#654](https://github.com/jakubpawlowicz/clean-css/issues/654) - disables length optimizations. +* Fixed issue [#655](https://github.com/jakubpawlowicz/clean-css/issues/655) - shorthands override merging. +* Fixed issue [#660](https://github.com/jakubpawlowicz/clean-css/issues/660) - !important token overriding. +* Fixed issue [#662](https://github.com/jakubpawlowicz/clean-css/issues/662) - !important selector reducing. +* Fixed issue [#667](https://github.com/jakubpawlowicz/clean-css/issues/667) - rebasing remote URLs. + +[3.4.1 / 2015-08-27](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.0...v3.4.1) +================== + +* Fixed issue [#652](https://github.com/jakubpawlowicz/clean-css/issues/652) - order of restoring and removing tokens. + +[3.4.0 / 2015-08-27](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.10...v3.4.0) +================== + +* Adds an option for a fine-grained `@import` control. +* Adds unit compatibility switches to disable length optimizations. +* Adds inferring proxy settings from HTTP_PROXY environment variable. +* Adds support for Polymer / Web Components special selectors. +* Adds support for Polymer mixins. +* Adds testing source maps in batch mode. +* Unifies wrappers for simple & advanced optimizations. +* Fixed issue [#596](https://github.com/jakubpawlowicz/clean-css/issues/596) - support for !ie IE<8 hack. +* Fixed issue [#599](https://github.com/jakubpawlowicz/clean-css/issues/599) - support for inlined source maps. +* Fixed issue [#607](https://github.com/jakubpawlowicz/clean-css/issues/607) - adds better rule reordering. +* Fixed issue [#612](https://github.com/jakubpawlowicz/clean-css/issues/612) - adds HTTP proxy support. +* Fixed issue [#618](https://github.com/jakubpawlowicz/clean-css/issues/618) - adds safer function validation. +* Fixed issue [#625](https://github.com/jakubpawlowicz/clean-css/issues/625) - adds length unit optimizations. +* Fixed issue [#632](https://github.com/jakubpawlowicz/clean-css/issues/632) - adds disabling remote `import`s. +* Fixed issue [#635](https://github.com/jakubpawlowicz/clean-css/issues/635) - adds safer `0%` optimizations. +* Fixed issue [#644](https://github.com/jakubpawlowicz/clean-css/issues/644) - adds time unit optimizations. +* Fixed issue [#645](https://github.com/jakubpawlowicz/clean-css/issues/645) - adds bottom to top `media` merging. +* Fixed issue [#648](https://github.com/jakubpawlowicz/clean-css/issues/648) - adds property level at-rule support. + +[3.3.10 / 2015-08-27](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.9...v3.3.10) +================== + +* Adds better comments + keepBreaks handling. +* Adds better text normalizing in source maps mode. +* Fixes non-adjacent optimizations for source maps. +* Fixes removing unused items. +* Improves `outline` break up with source maps. +* Refixed issue [#629](https://github.com/jakubpawlowicz/clean-css/issues/629) - source maps & background shorthands. + +[3.3.9 / 2015-08-09](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.8...v3.3.9) +================== + +* Fixed issue [#640](https://github.com/jakubpawlowicz/clean-css/issues/640) - URI processing regression. + +[3.3.8 / 2015-08-06](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.7...v3.3.8) +================== + +* Fixed issue [#629](https://github.com/jakubpawlowicz/clean-css/issues/629) - source maps & background shorthands. +* Fixed issue [#630](https://github.com/jakubpawlowicz/clean-css/issues/630) - vendor prefixed flex optimizations. +* Fixed issue [#633](https://github.com/jakubpawlowicz/clean-css/issues/633) - handling data URI with brackets. +* Fixed issue [#634](https://github.com/jakubpawlowicz/clean-css/issues/634) - merging :placeholder selectors. + +[3.3.7 / 2015-07-29](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.6...v3.3.7) +================== + +* Fixed issue [#616](https://github.com/jakubpawlowicz/clean-css/issues/616) - ordering in restructuring. + +[3.3.6 / 2015-07-14](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.5...v3.3.6) +================== + +* Fixed issue [#620](https://github.com/jakubpawlowicz/clean-css/issues/620) - `bold` style in font shorthands. + +[3.3.5 / 2015-07-01](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.4...v3.3.5) +================== + +* Fixed issue [#608](https://github.com/jakubpawlowicz/clean-css/issues/608) - custom URI protocols handling. + +[3.3.4 / 2015-06-24](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.3...v3.3.4) +================== + +* Fixed issue [#610](https://github.com/jakubpawlowicz/clean-css/issues/610) - `border:inherit` restoring. +* Fixed issue [#611](https://github.com/jakubpawlowicz/clean-css/issues/611) - edge case in quote stripping. + +[3.3.3 / 2015-06-16](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.2...v3.3.3) +================== + +* Fixed issue [#603](https://github.com/jakubpawlowicz/clean-css/issues/603) - IE suffix hack defaults to on. + +[3.3.2 / 2015-06-14](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.1...v3.3.2) +================== + +* Fixed issue [#595](https://github.com/jakubpawlowicz/clean-css/issues/595) - more relaxed block matching. +* Fixed issue [#601](https://github.com/jakubpawlowicz/clean-css/issues/601) - percentage minifying inside `flex`. +* Fixed issue [#602](https://github.com/jakubpawlowicz/clean-css/issues/602) - backslash IE hacks after a space. + +[3.3.1 / 2015-06-02](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.0...v3.3.1) +================== + +* Fixed issue [#590](https://github.com/jakubpawlowicz/clean-css/issues/590) - edge case in `@import` processing. + +[3.3.0 / 2015-05-31](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.11...v3.3.0) +================== + +* Cleans up url rebase code getting rid of unnecessary state. +* Cleans up tokenizer code getting rid of unnecessary state. +* Moves source maps tracker into lib/source-maps/track. +* Moves tokenizer code into lib/tokenizer. +* Moves URL scanner into lib/urls/reduce (was named incorrectly before). +* Moves URL rebasing & rewriting into lib/urls. +* Fixed issue [#375](https://github.com/jakubpawlowicz/clean-css/issues/375) - unit compatibility switches. +* Fixed issue [#436](https://github.com/jakubpawlowicz/clean-css/issues/436) - refactors URI rewriting. +* Fixed issue [#448](https://github.com/jakubpawlowicz/clean-css/issues/448) - rebasing no protocol URIs. +* Fixed issue [#517](https://github.com/jakubpawlowicz/clean-css/issues/517) - turning off color optimizations. +* Fixed issue [#542](https://github.com/jakubpawlowicz/clean-css/issues/542) - space after closing brace in IE. +* Fixed issue [#562](https://github.com/jakubpawlowicz/clean-css/issues/562) - optimizing invalid color values. +* Fixed issue [#563](https://github.com/jakubpawlowicz/clean-css/issues/563) - `background:inherit` restoring. +* Fixed issue [#570](https://github.com/jakubpawlowicz/clean-css/issues/570) - rebasing "no-url()" imports. +* Fixed issue [#574](https://github.com/jakubpawlowicz/clean-css/issues/574) - rewriting internal URLs. +* Fixed issue [#575](https://github.com/jakubpawlowicz/clean-css/issues/575) - missing directory as a `target`. +* Fixed issue [#577](https://github.com/jakubpawlowicz/clean-css/issues/577) - `background-clip` into shorthand. +* Fixed issue [#579](https://github.com/jakubpawlowicz/clean-css/issues/579) - `background-origin` into shorthand. +* Fixed issue [#580](https://github.com/jakubpawlowicz/clean-css/issues/580) - mixed `@import` processing. +* Fixed issue [#582](https://github.com/jakubpawlowicz/clean-css/issues/582) - overriding with prefixed values. +* Fixed issue [#583](https://github.com/jakubpawlowicz/clean-css/issues/583) - URL quoting for SVG data. +* Fixed issue [#587](https://github.com/jakubpawlowicz/clean-css/issues/587) - too aggressive `border` reordering. + +[3.2.11 / 2015-05-31](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.10...v3.2.11) +================== + +* Fixed issue [#563](https://github.com/jakubpawlowicz/clean-css/issues/563) - `background:inherit` restoring. +* Fixed issue [#582](https://github.com/jakubpawlowicz/clean-css/issues/582) - overriding with prefixed values. +* Fixed issue [#583](https://github.com/jakubpawlowicz/clean-css/issues/583) - URL quoting for SVG data. +* Fixed issue [#587](https://github.com/jakubpawlowicz/clean-css/issues/587) - too aggressive `border` reordering. + +[3.2.10 / 2015-05-14](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.9...v3.2.10) +================== + +* Fixed issue [#572](https://github.com/jakubpawlowicz/clean-css/issues/572) - empty elements removal. + +[3.2.9 / 2015-05-08](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.8...v3.2.9) +================== + +* Fixed issue [#567](https://github.com/jakubpawlowicz/clean-css/issues/567) - merging colors as functions. + +[3.2.8 / 2015-05-04](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.7...v3.2.8) +================== + +* Fixed issue [#561](https://github.com/jakubpawlowicz/clean-css/issues/561) - restructuring special selectors. + +[3.2.7 / 2015-05-03](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.6...v3.2.7) +================== + +* Fixed issue [#551](https://github.com/jakubpawlowicz/clean-css/issues/551) - edge case in restructuring. +* Fixed issue [#553](https://github.com/jakubpawlowicz/clean-css/issues/553) - another style of SVG fallback. +* Fixed issue [#558](https://github.com/jakubpawlowicz/clean-css/issues/558) - units in same selector merging. + +[3.2.6 / 2015-04-28](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.5...v3.2.6) +================== + +* Fixed issue [#550](https://github.com/jakubpawlowicz/clean-css/issues/550) - proper `contentSources` tracking. +* Fixed issue [#556](https://github.com/jakubpawlowicz/clean-css/issues/556) - regression in IE backslash hacks. + +[3.2.5 / 2015-04-25](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.4...v3.2.5) +================== + +* Fixed issue [#543](https://github.com/jakubpawlowicz/clean-css/issues/543) - better "comment in body" handling. +* Fixed issue [#548](https://github.com/jakubpawlowicz/clean-css/issues/548) - regression in font minifying. +* Fixed issue [#549](https://github.com/jakubpawlowicz/clean-css/issues/549) - special comments in source maps. + +[3.2.4 / 2015-04-24](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.3...v3.2.4) +================== + +* Fixed issue [#544](https://github.com/jakubpawlowicz/clean-css/issues/544) - regression in same value merging. +* Fixed issue [#546](https://github.com/jakubpawlowicz/clean-css/issues/546) - IE<11 `calc()` issue. + +[3.2.3 / 2015-04-22](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.2...v3.2.3) +================== + +* Fixed issue [#541](https://github.com/jakubpawlowicz/clean-css/issues/541) - `outline-style:auto` in shorthand. + +[3.2.2 / 2015-04-21](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.1...v3.2.2) +================== + +* Fixed issue [#537](https://github.com/jakubpawlowicz/clean-css/issues/537) - regression in simple optimizer. + +[3.2.1 / 2015-04-20](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.0...v3.2.1) +================== + +* Fixed issue [#534](https://github.com/jakubpawlowicz/clean-css/issues/534) - wrong `@font-face` stringifying. + +[3.2.0 / 2015-04-19](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.9...v3.2.0) +================== + +* Bumps commander to 2.8.x. +* Fixes remote asset rebasing when passing data as a hash. +* Improves path resolution inside source maps. +* Makes `root` option implicitely default to `process.cwd()`. +* Fixed issue [#371](https://github.com/jakubpawlowicz/clean-css/issues/371) - `background` fallback with `none`. +* Fixed issue [#376](https://github.com/jakubpawlowicz/clean-css/issues/376) - option to disable `0[unit]` -> `0`. +* Fixed issue [#396](https://github.com/jakubpawlowicz/clean-css/issues/396) - better input source maps tracking. +* Fixed issue [#397](https://github.com/jakubpawlowicz/clean-css/issues/397) - support for source map sources. +* Fixed issue [#399](https://github.com/jakubpawlowicz/clean-css/issues/399) - support compacting with source maps. +* Fixed issue [#429](https://github.com/jakubpawlowicz/clean-css/issues/429) - unifies data tokenization. +* Fixed issue [#446](https://github.com/jakubpawlowicz/clean-css/issues/446) - `list-style` fuzzy matching. +* Fixed issue [#468](https://github.com/jakubpawlowicz/clean-css/issues/468) - bumps `source-map` to 0.4.x. +* Fixed issue [#480](https://github.com/jakubpawlowicz/clean-css/issues/480) - extracting uppercase property names. +* Fixed issue [#487](https://github.com/jakubpawlowicz/clean-css/issues/487) - source map paths under Windows. +* Fixed issue [#490](https://github.com/jakubpawlowicz/clean-css/issues/490) - vendor prefixed multivalue `background`. +* Fixed issue [#500](https://github.com/jakubpawlowicz/clean-css/issues/500) - merging duplicate adjacent properties. +* Fixed issue [#504](https://github.com/jakubpawlowicz/clean-css/issues/504) - keeping `url()` quotes. +* Fixed issue [#507](https://github.com/jakubpawlowicz/clean-css/issues/507) - merging longhands into many shorthands. +* Fixed issue [#508](https://github.com/jakubpawlowicz/clean-css/issues/508) - removing duplicate media queries. +* Fixed issue [#521](https://github.com/jakubpawlowicz/clean-css/issues/521) - unit optimizations inside `calc()`. +* Fixed issue [#524](https://github.com/jakubpawlowicz/clean-css/issues/524) - timeouts in `@import` inlining. +* Fixed issue [#526](https://github.com/jakubpawlowicz/clean-css/issues/526) - shorthand overriding into a function. +* Fixed issue [#528](https://github.com/jakubpawlowicz/clean-css/issues/528) - better support for IE<9 hacks. +* Fixed issue [#529](https://github.com/jakubpawlowicz/clean-css/issues/529) - wrong font weight minification. + +[3.1.9 / 2015-04-04](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.8...v3.1.9) +================== + +* Fixes issue [#511](https://github.com/jakubpawlowicz/clean-css/issues/511) - `)` advanced processing. + +[3.1.8 / 2015-03-17](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.7...v3.1.8) +================== + +* Fixes issue [#498](https://github.com/jakubpawlowicz/clean-css/issues/498) - reordering and flexbox. +* Fixes issue [#499](https://github.com/jakubpawlowicz/clean-css/issues/499) - too aggressive `-` removal. + +[3.1.7 / 2015-03-16](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.6...v3.1.7) +================== + +* Backports fix to [#480](https://github.com/jakubpawlowicz/clean-css/issues/480) - reordering and uppercase properties. +* Fixes issue [#496](https://github.com/jakubpawlowicz/clean-css/issues/496) - space after bracket removal. + +[3.1.6 / 2015-03-12](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.5...v3.1.6) +================== + +* Fixes issue [#489](https://github.com/jakubpawlowicz/clean-css/issues/489) - `AlphaImageLoader` IE filter. + +[3.1.5 / 2015-03-06](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.4...v3.1.5) +================== + +* Fixes issue [#483](https://github.com/jakubpawlowicz/clean-css/issues/483) - property order in restructuring. + +[3.1.4 / 2015-03-04](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.3...v3.1.4) +================== + +* Fixes issue [#472](https://github.com/jakubpawlowicz/clean-css/issues/472) - broken function minification. +* Fixes issue [#477](https://github.com/jakubpawlowicz/clean-css/issues/477) - `@import`s order in restructuring. +* Fixes issue [#478](https://github.com/jakubpawlowicz/clean-css/issues/478) - ultimate fix to brace whitespace. + +[3.1.3 / 2015-03-03](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.2...v3.1.3) +================== + +* Fixes issue [#464](https://github.com/jakubpawlowicz/clean-css/issues/464) - data URI with quoted braces. +* Fixes issue [#475](https://github.com/jakubpawlowicz/clean-css/issues/475) - whitespace after closing brace. + +[3.1.2 / 2015-03-01](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.1...v3.1.2) +================== + +* Refixed issue [#471](https://github.com/jakubpawlowicz/clean-css/issues/471) - correct order after restructuring. +* Fixes issue [#466](https://github.com/jakubpawlowicz/clean-css/issues/466) - rebuilding background shorthand. +* Fixes issue [#462](https://github.com/jakubpawlowicz/clean-css/issues/462) - escaped apostrophes in selectors. + +[3.1.1 / 2015-02-27](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.0...v3.1.1) +================== + +* Fixed issue [#469](https://github.com/jakubpawlowicz/clean-css/issues/469) - extracting broken property. +* Fixed issue [#470](https://github.com/jakubpawlowicz/clean-css/issues/470) - negative padding removal. +* Fixed issue [#471](https://github.com/jakubpawlowicz/clean-css/issues/471) - correct order after restructuring. + +[3.1.0 / 2015-02-26](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.10...v3.1.0) +================== + +* Adds `0deg` to `0` minification where possible. +* Adds better non-adjacent selector merging when body is the same. +* Adds official support for node.js 0.12. +* Adds official support for io.js 1.0. +* Adds restructuring optimizations to reorganize selectors, which vastly improves minification. +* Fixed issue [#158](https://github.com/jakubpawlowicz/clean-css/issues/158) - adds body-based selectors reduction. +* Fixed issue [#182](https://github.com/jakubpawlowicz/clean-css/issues/182) - removing space after closing brace. +* Fixed issue [#204](https://github.com/jakubpawlowicz/clean-css/issues/204) - `@media` merging. +* Fixed issue [#351](https://github.com/jakubpawlowicz/clean-css/issues/351) - remote `@import`s after content. +* Fixed issue [#357](https://github.com/jakubpawlowicz/clean-css/issues/357) - non-standard but valid URLs. +* Fixed issue [#416](https://github.com/jakubpawlowicz/clean-css/issues/416) - accepts hash as `minify` argument. +* Fixed issue [#419](https://github.com/jakubpawlowicz/clean-css/issues/419) - multiple input source maps. +* Fixed issue [#435](https://github.com/jakubpawlowicz/clean-css/issues/435) - `background-clip` in shorthand. +* Fixed issue [#439](https://github.com/jakubpawlowicz/clean-css/issues/439) - `background-origin` in shorthand. +* Fixed issue [#442](https://github.com/jakubpawlowicz/clean-css/issues/442) - space before adjacent `nav`. +* Fixed issue [#445](https://github.com/jakubpawlowicz/clean-css/issues/445) - regression issue in url processor. +* Fixed issue [#449](https://github.com/jakubpawlowicz/clean-css/issues/449) - warns of missing close braces. +* Fixed issue [#463](https://github.com/jakubpawlowicz/clean-css/issues/463) - relative remote `@import` URLs. + +[3.0.10 / 2015-02-07](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.9...v3.0.10) +================== + +* Fixed issue [#453](https://github.com/jakubpawlowicz/clean-css/issues/453) - double `background-repeat`. +* Fixed issue [#455](https://github.com/jakubpawlowicz/clean-css/issues/455) - property extracting regression. + +[3.0.9 / 2015-02-04](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.8...v3.0.9) +================== + +* Fixed issue [#452](https://github.com/jakubpawlowicz/clean-css/issues/452) - regression in advanced merging. + +[3.0.8 / 2015-01-31](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.7...v3.0.8) +================== + +* Fixed issue [#447](https://github.com/jakubpawlowicz/clean-css/issues/447) - `background-color` in shorthands. +* Fixed issue [#450](https://github.com/jakubpawlowicz/clean-css/issues/450) - name to hex color converting. + +[3.0.7 / 2015-01-22](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.6...v3.0.7) +================== + +* Fixed issue [#441](https://github.com/jakubpawlowicz/clean-css/issues/441) - hex to name color converting. + +[3.0.6 / 2015-01-20](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.5...v3.0.6) +================== + +* Refixed issue [#414](https://github.com/jakubpawlowicz/clean-css/issues/414) - source maps position fallback. + +[3.0.5 / 2015-01-18](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.4...v3.0.5) +================== + +* Fixed issue [#414](https://github.com/jakubpawlowicz/clean-css/issues/414) - source maps position fallback. +* Fixed issue [#433](https://github.com/jakubpawlowicz/clean-css/issues/433) - meging `!important` in shorthands. + +[3.0.4 / 2015-01-11](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.3...v3.0.4) +================== + +* Fixed issue [#314](https://github.com/jakubpawlowicz/clean-css/issues/314) - spaces inside `calc`. + +[3.0.3 / 2015-01-07](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.2...v3.0.3) +================== + +* Just a version bump as npm incorrectly things 2.2.23 is the latest one. + +[3.0.2 / 2015-01-04](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.1...v3.0.2) +================== + +* Fixed issue [#422](https://github.com/jakubpawlowicz/clean-css/issues/422) - handling `calc` as a unit. + +[3.0.1 / 2014-12-19](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.0...v3.0.1) +================== + +* Fixed issue [#410](https://github.com/jakubpawlowicz/clean-css/issues/410) - advanced merging and comments. +* Fixed issue [#411](https://github.com/jakubpawlowicz/clean-css/issues/411) - properties and important comments. + +[3.0.0 / 2014-12-18](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.22...v3.0.0) +================== + +* Adds more granular control over compatibility settings. +* Adds support for @counter-style at-rule. +* Adds `--source-map`/`sourceMap` switch for building input's source map. +* Adds `--skip-shorthand-compacting`/`shorthandComacting` option for disabling shorthand compacting. +* Allows `target` option to be a path to a folder instead of a file. +* Allows disabling rounding precision. By [@superlukas](https://github.com/superlukas). +* Breaks 2.x compatibility for using CleanCSS as a function. +* Changes `minify` method output to handle multiple outputs. +* Reworks minification to tokenize first then minify. + See [changes](https://github.com/jakubpawlowicz/clean-css/compare/b06f37d...dd8c14a). +* Removes support for node.js 0.8.x. +* Renames `noAdvanced` option into `advanced`. +* Renames `noAggressiveMerging` option into `aggressiveMerging`. +* Renames `noRebase` option into `rebase`. +* Speeds up advanced processing by shortening optimize loop. +* Fixed issue [#125](https://github.com/jakubpawlowicz/clean-css/issues/125) - source maps! +* Fixed issue [#344](https://github.com/jakubpawlowicz/clean-css/issues/344) - merging `background-size` into shorthand. +* Fixed issue [#352](https://github.com/jakubpawlowicz/clean-css/issues/352) - honors rebasing in imported stylesheets. +* Fixed issue [#360](https://github.com/jakubpawlowicz/clean-css/issues/360) - adds 7 extra CSS colors. +* Fixed issue [#363](https://github.com/jakubpawlowicz/clean-css/issues/363) - `rem` units overriding `px`. +* Fixed issue [#373](https://github.com/jakubpawlowicz/clean-css/issues/373) - proper `background` shorthand merging. +* Fixed issue [#395](https://github.com/jakubpawlowicz/clean-css/issues/395) - unescaped brackets in data URIs. +* Fixed issue [#398](https://github.com/jakubpawlowicz/clean-css/issues/398) - restoring important comments. +* Fixed issue [#400](https://github.com/jakubpawlowicz/clean-css/issues/400) - API to accept an array of filenames. +* Fixed issue [#403](https://github.com/jakubpawlowicz/clean-css/issues/403) - tracking input files in source maps. +* Fixed issue [#404](https://github.com/jakubpawlowicz/clean-css/issues/404) - no state sharing in API. +* Fixed issue [#405](https://github.com/jakubpawlowicz/clean-css/issues/405) - disables default `background-size` merging. +* Refixed issue [#304](https://github.com/jakubpawlowicz/clean-css/issues/304) - `background-position` merging. + +[2.2.22 / 2014-12-13](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.21...v2.2.22) +================== + +* Backports fix to issue [#304](https://github.com/jakubpawlowicz/clean-css/issues/304) - `background-position` merging. + +[2.2.21 / 2014-12-10](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.20...v2.2.21) +================== + +* Backports fix to issue [#373](https://github.com/jakubpawlowicz/clean-css/issues/373) - `background` shorthand merging. + +[2.2.20 / 2014-12-02](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.19...v2.2.20) +================== + +* Backports fix to issue [#390](https://github.com/jakubpawlowicz/clean-css/issues/390) - pseudo-class merging. + +[2.2.19 / 2014-11-20](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.18...v2.2.19) +================== + +* Fixed issue [#385](https://github.com/jakubpawlowicz/clean-css/issues/385) - edge cases in processing cut off data. + +[2.2.18 / 2014-11-17](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.17...v2.2.18) +================== + +* Fixed issue [#383](https://github.com/jakubpawlowicz/clean-css/issues/383) - rounding fractions once again. + +[2.2.17 / 2014-11-09](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.16...v2.2.17) +================== + +* Fixed issue [#380](https://github.com/jakubpawlowicz/clean-css/issues/380) - rounding fractions to a whole number. + +[2.2.16 / 2014-09-16](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.15...v2.2.16) +================== + +* Fixed issue [#359](https://github.com/jakubpawlowicz/clean-css/issues/359) - handling escaped double backslash. +* Fixed issue [#358](https://github.com/jakubpawlowicz/clean-css/issues/358) - property merging in compatibility mode. +* Fixed issue [#356](https://github.com/jakubpawlowicz/clean-css/issues/356) - preserving `*+html` hack. +* Fixed issue [#354](https://github.com/jakubpawlowicz/clean-css/issues/354) - `!important` overriding in shorthands. + +[2.2.15 / 2014-09-01](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.14...v2.2.15) +================== + +* Fixed issue [#343](https://github.com/jakubpawlowicz/clean-css/issues/343) - too aggressive `rgba`/`hsla` minification. +* Fixed issue [#345](https://github.com/jakubpawlowicz/clean-css/issues/345) - URL rebasing for document relative ones. +* Fixed issue [#346](https://github.com/jakubpawlowicz/clean-css/issues/346) - overriding `!important` by `!important`. +* Fixed issue [#350](https://github.com/jakubpawlowicz/clean-css/issues/350) - edge cases in `@import` processing. + +[2.2.14 / 2014-08-25](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.13...v2.2.14) +================== + +* Makes multival operations idempotent. +* Fixed issue [#339](https://github.com/jakubpawlowicz/clean-css/issues/339) - skips invalid properties. +* Fixed issue [#341](https://github.com/jakubpawlowicz/clean-css/issues/341) - ensure output is shorter than input. + +[2.2.13 / 2014-08-12](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.12...v2.2.13) +================== + +* Fixed issue [#337](https://github.com/jakubpawlowicz/clean-css/issues/337) - handling component importance. + +[2.2.12 / 2014-08-02](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.11...v2.2.12) +================== + +* Fixed issue with tokenizer removing first selector after an unknown @ rule. +* Fixed issue [#329](https://github.com/jakubpawlowicz/clean-css/issues/329) - `font` shorthands incorrectly processed. +* Fixed issue [#332](https://github.com/jakubpawlowicz/clean-css/issues/332) - `background` shorthand with colors. +* Refixed issue [#325](https://github.com/jakubpawlowicz/clean-css/issues/325) - invalid charset declarations. + +[2.2.11 / 2014-07-28](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.10...v2.2.11) +================== + +* Fixed issue [#326](https://github.com/jakubpawlowicz/clean-css/issues/326) - `background-size` regression. + +[2.2.10 / 2014-07-27](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.9...v2.2.10) +================== + +* Improved performance of advanced mode validators. +* Fixed issue [#307](https://github.com/jakubpawlowicz/clean-css/issues/307) - `background-color` in multiple backgrounds. +* Fixed issue [#322](https://github.com/jakubpawlowicz/clean-css/issues/322) - adds `background-size` support. +* Fixed issue [#323](https://github.com/jakubpawlowicz/clean-css/issues/323) - stripping variable references. +* Fixed issue [#325](https://github.com/jakubpawlowicz/clean-css/issues/325) - removing invalid `@charset` declarations. + +[2.2.9 / 2014-07-23](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.8...v2.2.9) +================== + +* Adds `background` normalization according to W3C spec. +* Fixed issue [#316](https://github.com/jakubpawlowicz/clean-css/issues/316) - incorrect `background` processing. + +[2.2.8 / 2014-07-14](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.7...v2.2.8) +================== + +* Fixed issue [#313](https://github.com/jakubpawlowicz/clean-css/issues/313) - processing comment marks in URLs. +* Fixed issue [#315](https://github.com/jakubpawlowicz/clean-css/issues/315) - `rgba`/`hsla` -> `transparent` in gradients. + +[2.2.7 / 2014-07-10](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.6...v2.2.7) +================== + +* Fixed issue [#304](https://github.com/jakubpawlowicz/clean-css/issues/304) - merging multiple backgrounds. +* Fixed issue [#312](https://github.com/jakubpawlowicz/clean-css/issues/312) - merging with mixed repeat. + +[2.2.6 / 2014-07-05](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.5...v2.2.6) +================== + +* Adds faster quote matching in QuoteScanner. +* Improves QuoteScanner to handle comments correctly. +* Fixed issue [#308](https://github.com/jakubpawlowicz/clean-css/issues/308) - parsing comments in quoted URLs. +* Fixed issue [#311](https://github.com/jakubpawlowicz/clean-css/issues/311) - leading/trailing decimal points. + +[2.2.5 / 2014-06-29](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.4...v2.2.5) +================== + +* Adds removing extra spaces around / in border-radius. +* Adds replacing same horizontal & vertical value in border-radius. +* Fixed issue [#305](https://github.com/jakubpawlowicz/clean-css/issues/305) - allows width keywords in `border-width`. + +[2.2.4 / 2014-06-27](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.3...v2.2.4) +================== + +* Fixed issue [#301](https://github.com/jakubpawlowicz/clean-css/issues/301) - proper `border-radius` processing. +* Fixed issue [#303](https://github.com/jakubpawlowicz/clean-css/issues/303) - correctly preserves viewport units. + +[2.2.3 / 2014-06-24](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.2...v2.2.3) +================== + +* Fixed issue [#302](https://github.com/jakubpawlowicz/clean-css/issues/302) - handling of `outline-style: auto`. + +[2.2.2 / 2014-06-18](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.1...v2.2.2) +================== + +* Fixed issue [#297](https://github.com/jakubpawlowicz/clean-css/issues/297) - `box-shadow` zeros minification. + +[2.2.1 / 2014-06-14](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.0...v2.2.1) +================== + +* Fixes new property optimizer for 'none' values. +* Fixed issue [#294](https://github.com/jakubpawlowicz/clean-css/issues/294) - space after `rgba`/`hsla` in IE<=11. + +[2.2.0 / 2014-06-11](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.8...v2.2.0) +================== + +* Adds a better algorithm for quotation marks' removal. +* Adds a better non-adjacent optimizer compatible with the upcoming new property optimizer. +* Adds minifying remote files directly from CLI. +* Adds `--rounding-precision` to control rounding precision. +* Moves quotation matching into a `QuoteScanner` class. +* Adds `npm run browserify` for creating embeddable version of clean-css. +* Fixed list-style-* advanced processing. +* Fixed issue [#134](https://github.com/jakubpawlowicz/clean-css/issues/134) - merges properties into shorthand form. +* Fixed issue [#164](https://github.com/jakubpawlowicz/clean-css/issues/164) - removes default values if not needed. +* Fixed issue [#168](https://github.com/jakubpawlowicz/clean-css/issues/168) - adds better property merging algorithm. +* Fixed issue [#173](https://github.com/jakubpawlowicz/clean-css/issues/173) - merges same properties if grouped. +* Fixed issue [#184](https://github.com/jakubpawlowicz/clean-css/issues/184) - uses `!important` for optimization opportunities. +* Fixed issue [#190](https://github.com/jakubpawlowicz/clean-css/issues/190) - uses shorthand to override another shorthand. +* Fixed issue [#197](https://github.com/jakubpawlowicz/clean-css/issues/197) - adds borders merging by understandability. +* Fixed issue [#210](https://github.com/jakubpawlowicz/clean-css/issues/210) - adds temporary workaround for aggressive merging. +* Fixed issue [#246](https://github.com/jakubpawlowicz/clean-css/issues/246) - removes IE hacks when not in compatibility mode. +* Fixed issue [#247](https://github.com/jakubpawlowicz/clean-css/issues/247) - removes deprecated `selectorsMergeMode` switch. +* Refixed issue [#250](https://github.com/jakubpawlowicz/clean-css/issues/250) - based on new quotation marks removal. +* Fixed issue [#257](https://github.com/jakubpawlowicz/clean-css/issues/257) - turns `rgba`/`hsla` to `transparent` if possible. +* Fixed issue [#265](https://github.com/jakubpawlowicz/clean-css/issues/265) - adds support for multiple input files. +* Fixed issue [#275](https://github.com/jakubpawlowicz/clean-css/issues/275) - handling transform properties. +* Fixed issue [#276](https://github.com/jakubpawlowicz/clean-css/issues/276) - corrects unicode handling. +* Fixed issue [#288](https://github.com/jakubpawlowicz/clean-css/issues/288) - adds smarter expression parsing. +* Fixed issue [#293](https://github.com/jakubpawlowicz/clean-css/issues/293) - handles escaped `@` symbols in class names and IDs. + +[2.1.8 / 2014-03-28](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.7...v2.1.8) +================== + +* Fixed issue [#267](https://github.com/jakubpawlowicz/clean-css/issues/267) - incorrect non-adjacent selector merging. + +[2.1.7 / 2014-03-24](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.6...v2.1.7) +================== + +* Fixed issue [#264](https://github.com/jakubpawlowicz/clean-css/issues/264) - `@import` statements inside comments. + +[2.1.6 / 2014-03-10](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.5...v2.1.6) +================== + +* Fixed issue [#258](https://github.com/jakubpawlowicz/clean-css/issues/258) - wrong `@import` handling in `EmptyRemoval`. + +[2.1.5 / 2014-03-07](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.4...v2.1.5) +================== + +* Fixed issue [#255](https://github.com/jakubpawlowicz/clean-css/issues/255) - incorrect processing of a trailing `-0`. + +[2.1.4 / 2014-03-01](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.3...v2.1.4) +================== + +* Fixed issue [#250](https://github.com/jakubpawlowicz/clean-css/issues/250) - correctly handle JSON data in quotations. + +[2.1.3 / 2014-02-26](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.2...v2.1.3) +================== + +* Fixed issue [#248](https://github.com/jakubpawlowicz/clean-css/issues/248) - incorrect merging for vendor selectors. + +[2.1.2 / 2014-02-25](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.1...v2.1.2) +================== + +* Fixed issue [#245](https://github.com/jakubpawlowicz/clean-css/issues/245) - incorrect handling of backslash IE hack. + +[2.1.1 / 2014-02-18](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.0...v2.1.1) +================== + +* Adds faster selectors processing in advanced optimizer. +* Fixed issue [#241](https://github.com/jakubpawlowicz/clean-css/issues/241) - incorrect handling of `:not()` selectors. + +[2.1.0 / 2014-02-13](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.8...v2.1.0) +================== + +* Adds an optional callback to minify method. +* Deprecates `--selectors-merge-mode` / `selectorsMergeMode` in favor to `--compatibility` / `compatibility`. +* Fixes debug mode stats for stylesheets using `@import` statements. +* Skips empty removal if advanced processing is enabled. +* Fixed issue [#85](https://github.com/jakubpawlowicz/clean-css/issues/85) - resolving protocol `@import`s. +* Fixed issue [#160](https://github.com/jakubpawlowicz/clean-css/issues/160) - re-runs optimizer until a clean pass. +* Fixed issue [#161](https://github.com/jakubpawlowicz/clean-css/issues/161) - improves tokenizer performance. +* Fixed issue [#163](https://github.com/jakubpawlowicz/clean-css/issues/163) - round pixels to 2nd decimal place. +* Fixed issue [#165](https://github.com/jakubpawlowicz/clean-css/issues/165) - extra space after trailing parenthesis. +* Fixed issue [#186](https://github.com/jakubpawlowicz/clean-css/issues/186) - strip unit from `0rem`. +* Fixed issue [#207](https://github.com/jakubpawlowicz/clean-css/issues/207) - bug in parsing protocol `@import`s. +* Fixed issue [#213](https://github.com/jakubpawlowicz/clean-css/issues/213) - faster `rgb` to `hex` transforms. +* Fixed issue [#215](https://github.com/jakubpawlowicz/clean-css/issues/215) - leading zeros in numerical values. +* Fixed issue [#217](https://github.com/jakubpawlowicz/clean-css/issues/217) - whitespace inside attribute selectors and URLs. +* Fixed issue [#218](https://github.com/jakubpawlowicz/clean-css/issues/218) - `@import` statements cleanup. +* Fixed issue [#220](https://github.com/jakubpawlowicz/clean-css/issues/220) - selector between comments. +* Fixed issue [#223](https://github.com/jakubpawlowicz/clean-css/issues/223) - two-pass adjacent selectors merging. +* Fixed issue [#226](https://github.com/jakubpawlowicz/clean-css/issues/226) - don't minify `border:none` to `border:0`. +* Fixed issue [#229](https://github.com/jakubpawlowicz/clean-css/issues/229) - improved processing of fraction numbers. +* Fixed issue [#230](https://github.com/jakubpawlowicz/clean-css/issues/230) - better handling of zero values. +* Fixed issue [#235](https://github.com/jakubpawlowicz/clean-css/issues/235) - IE7 compatibility mode. +* Fixed issue [#236](https://github.com/jakubpawlowicz/clean-css/issues/236) - incorrect rebasing with nested `import`s. + +[2.0.8 / 2014-02-07](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.7...v2.0.8) +================== + +* Fixed issue [#232](https://github.com/jakubpawlowicz/clean-css/issues/232) - edge case in non-adjacent selectors merging. + +[2.0.7 / 2014-01-16](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.6...v2.0.7) +================== + +* Fixed issue [#208](https://github.com/jakubpawlowicz/clean-css/issues/208) - don't swallow `@page` and `@viewport`. + +[2.0.6 / 2014-01-04](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.5...v2.0.6) +================== + +* Fixed issue [#198](https://github.com/jakubpawlowicz/clean-css/issues/198) - process comments and `@import`s correctly. +* Fixed issue [#205](https://github.com/jakubpawlowicz/clean-css/issues/205) - freeze on broken `@import` declaration. + +[2.0.5 / 2014-01-03](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.4...v2.0.5) +================== + +* Fixed issue [#199](https://github.com/jakubpawlowicz/clean-css/issues/199) - keep line breaks with no advanced optimizations. +* Fixed issue [#203](https://github.com/jakubpawlowicz/clean-css/issues/203) - Buffer as a first argument to minify method. + +[2.0.4 / 2013-12-19](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.3...v2.0.4) +================== + +* Fixed issue [#193](https://github.com/jakubpawlowicz/clean-css/issues/193) - HSL color space normalization. + +[2.0.3 / 2013-12-18](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.2...v2.0.3) +================== + +* Fixed issue [#191](https://github.com/jakubpawlowicz/clean-css/issues/191) - leading numbers in `font`/`animation` names. +* Fixed issue [#192](https://github.com/jakubpawlowicz/clean-css/issues/192) - many `@import`s inside a comment. + +[2.0.2 / 2013-11-18](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.1...v2.0.2) +================== + +* Fixed issue [#177](https://github.com/jakubpawlowicz/clean-css/issues/177) - process broken content correctly. + +[2.0.1 / 2013-11-14](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.0...v2.0.1) +================== + +* Fixed issue [#176](https://github.com/jakubpawlowicz/clean-css/issues/176) - hangs on `undefined` keyword. + +[2.0.0 / 2013-11-04](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.7...v2.0.0) +================== + +* Adds simplified and more advanced text escaping / restoring via `EscapeStore` class. +* Adds simplified and much faster empty elements removal. +* Adds missing `@import` processing to our benchmark (run via `npm run bench`). +* Adds CSS tokenizer which will make it possible to optimize content by reordering and/or merging selectors. +* Adds basic optimizer removing duplicate selectors from a list. +* Adds merging duplicate properties within a single selector's body. +* Adds merging adjacent selectors within a scope (single and multiple ones). +* Changes behavior of `--keep-line-breaks`/`keepBreaks` option to keep breaks after trailing braces only. +* Makes all multiple selectors ordered alphabetically (aids merging). +* Adds property overriding so more coarse properties override more granular ones. +* Adds reducing non-adjacent selectors. +* Adds `--skip-advanced`/`noAdvanced` switch to disable advanced optimizations. +* Adds reducing non-adjacent selectors when overridden by more complex selectors. +* Fixed issue [#138](https://github.com/jakubpawlowicz/clean-css/issues/138) - makes CleanCSS interface OO. +* Fixed issue [#139](https://github.com/jakubpawlowicz/clean-css/issues/138) - consistent error & warning handling. +* Fixed issue [#145](https://github.com/jakubpawlowicz/clean-css/issues/145) - debug mode in library too. +* Fixed issue [#157](https://github.com/jakubpawlowicz/clean-css/issues/157) - gets rid of `removeEmpty` option. +* Fixed issue [#159](https://github.com/jakubpawlowicz/clean-css/issues/159) - escaped quotes inside content. +* Fixed issue [#162](https://github.com/jakubpawlowicz/clean-css/issues/162) - strip quotes from Base64 encoded URLs. +* Fixed issue [#166](https://github.com/jakubpawlowicz/clean-css/issues/166) - `debug` formatting in CLI +* Fixed issue [#167](https://github.com/jakubpawlowicz/clean-css/issues/167) - `background:transparent` minification. + +[1.1.7 / 2013-10-28](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.6...v1.1.7) +================== + +* Fixed issue [#156](https://github.com/jakubpawlowicz/clean-css/issues/156) - `@import`s inside comments. + +[1.1.6 / 2013-10-26](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.5...v1.1.6) +================== + +* Fixed issue [#155](https://github.com/jakubpawlowicz/clean-css/issues/155) - broken irregular CSS content. + +[1.1.5 / 2013-10-24](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.4...v1.1.5) +================== + +* Fixed issue [#153](https://github.com/jakubpawlowicz/clean-css/issues/153) - `keepSpecialComments` `0`/`1` as a string. + +[1.1.4 / 2013-10-23](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.3...v1.1.4) +================== + +* Fixed issue [#152](https://github.com/jakubpawlowicz/clean-css/issues/152) - adds an option to disable rebasing. + +[1.1.3 / 2013-10-04](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.2...v1.1.3) +================== + +* Fixed issue [#150](https://github.com/jakubpawlowicz/clean-css/issues/150) - minifying `background:none`. + +[1.1.2 / 2013-09-29](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.1...v1.1.2) +================== + +* Fixed issue [#149](https://github.com/jakubpawlowicz/clean-css/issues/149) - shorthand `font` property. + +[1.1.1 / 2013-09-07](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.0...v1.1.1) +================== + +* Fixed issue [#144](https://github.com/jakubpawlowicz/clean-css/issues/144) - skip URLs rebasing by default. + +[1.1.0 / 2013-09-06](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.12...v1.1.0) +================== + +* Renamed lib's `debug` option to `benchmark` when doing per-minification benchmarking. +* Added simplified comments processing & imports. +* Fixed issue [#43](https://github.com/jakubpawlowicz/clean-css/issues/43) - `--debug` switch for minification stats. +* Fixed issue [#65](https://github.com/jakubpawlowicz/clean-css/issues/65) - full color name / hex shortening. +* Fixed issue [#84](https://github.com/jakubpawlowicz/clean-css/issues/84) - support for `@import` with media queries. +* Fixed issue [#124](https://github.com/jakubpawlowicz/clean-css/issues/124) - raise error on broken imports. +* Fixed issue [#126](https://github.com/jakubpawlowicz/clean-css/issues/126) - proper CSS expressions handling. +* Fixed issue [#129](https://github.com/jakubpawlowicz/clean-css/issues/129) - rebasing imported URLs. +* Fixed issue [#130](https://github.com/jakubpawlowicz/clean-css/issues/130) - better code modularity. +* Fixed issue [#135](https://github.com/jakubpawlowicz/clean-css/issues/135) - require node.js 0.8+. + +[1.0.12 / 2013-07-19](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.11...v1.0.12) +=================== + +* Fixed issue [#121](https://github.com/jakubpawlowicz/clean-css/issues/121) - ability to skip `@import` processing. + +[1.0.11 / 2013-07-08](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.10...v1.0.11) +=================== + +* Fixed issue [#117](https://github.com/jakubpawlowicz/clean-css/issues/117) - line break escaping in comments. + +[1.0.10 / 2013-06-13](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.9...v1.0.10) +=================== + +* Fixed issue [#114](https://github.com/jakubpawlowicz/clean-css/issues/114) - comments in imported stylesheets. + +[1.0.9 / 2013-06-11](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.8...v1.0.9) +================== + +* Fixed issue [#113](https://github.com/jakubpawlowicz/clean-css/issues/113) - `@import` in comments. + +[1.0.8 / 2013-06-10](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.7...v1.0.8) +================== + +* Fixed issue [#112](https://github.com/jakubpawlowicz/clean-css/issues/112) - reducing `box-shadow` zeros. + +[1.0.7 / 2013-06-05](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.6...v1.0.7) +================== + +* Support for `@import` URLs starting with `//`. By [@petetak](https://github.com/petetak). + +[1.0.6 / 2013-06-04](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.5...v1.0.6) +================== + +* Fixed issue [#110](https://github.com/jakubpawlowicz/clean-css/issues/110) - data URIs in URLs. + +[1.0.5 / 2013-05-26](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.4...v1.0.5) +================== + +* Fixed issue [#107](https://github.com/jakubpawlowicz/clean-css/issues/107) - data URIs in imported stylesheets. + +1.0.4 / 2013-05-23 +================== + +* Rewrite relative URLs in imported stylesheets. By [@bluej100](https://github.com/bluej100). + +1.0.3 / 2013-05-20 +================== + +* Support alternative `@import` syntax with file name not wrapped inside `url()` statement. + By [@bluej100](https://github.com/bluej100). + +1.0.2 / 2013-04-29 +================== + +* Fixed issue [#97](https://github.com/jakubpawlowicz/clean-css/issues/97) - `--remove-empty` & FontAwesome. + +1.0.1 / 2013-04-08 +================== + +* Do not pick up `bench` and `test` while building `npm` package. + By [@sindresorhus](https://https://github.com/sindresorhus). + +1.0.0 / 2013-03-30 +================== + +* Fixed issue [#2](https://github.com/jakubpawlowicz/clean-css/issues/2) - resolving `@import` rules. +* Fixed issue [#44](https://github.com/jakubpawlowicz/clean-css/issues/44) - examples in `--help`. +* Fixed issue [#46](https://github.com/jakubpawlowicz/clean-css/issues/46) - preserving special characters in URLs and attributes. +* Fixed issue [#80](https://github.com/jakubpawlowicz/clean-css/issues/80) - quotation in multi line strings. +* Fixed issue [#83](https://github.com/jakubpawlowicz/clean-css/issues/83) - HSL to hex color conversions. +* Fixed issue [#86](https://github.com/jakubpawlowicz/clean-css/issues/86) - broken `@charset` replacing. +* Fixed issue [#88](https://github.com/jakubpawlowicz/clean-css/issues/88) - removes space in `! important`. +* Fixed issue [#92](https://github.com/jakubpawlowicz/clean-css/issues/92) - uppercase hex to short versions. + +0.10.2 / 2013-03-19 +=================== + +* Fixed issue [#79](https://github.com/jakubpawlowicz/clean-css/issues/79) - node.js 0.10.x compatibility. + +0.10.1 / 2013-02-14 +=================== + +* Fixed issue [#66](https://github.com/jakubpawlowicz/clean-css/issues/66) - line breaks without extra spaces should + be handled correctly. + +0.10.0 / 2013-02-09 +=================== + +* Switched from [optimist](https://github.com/substack/node-optimist) to + [commander](https://github.com/visionmedia/commander.js) for CLI processing. +* Changed long options from `--removeempty` to `--remove-empty` and from `--keeplinebreaks` to `--keep-line-breaks`. +* Fixed performance issue with replacing multiple `@charset` declarations and issue + with line break after `@charset` when using `keepLineBreaks` option. By [@rrjaime](https://github.com/rrjamie). +* Removed Makefile in favor to `npm run` commands (e.g. `make check` -> `npm run check`). +* Fixed issue [#47](https://github.com/jakubpawlowicz/clean-css/issues/47) - commandline issues on Windows. +* Fixed issue [#49](https://github.com/jakubpawlowicz/clean-css/issues/49) - remove empty selectors from media query. +* Fixed issue [#52](https://github.com/jakubpawlowicz/clean-css/issues/52) - strip fraction zeros if not needed. +* Fixed issue [#58](https://github.com/jakubpawlowicz/clean-css/issues/58) - remove colon where possible. +* Fixed issue [#59](https://github.com/jakubpawlowicz/clean-css/issues/59) - content property handling. + +0.9.1 / 2012-12-19 +================== + +* Fixed issue [#37](https://github.com/jakubpawlowicz/clean-css/issues/37) - converting + `white` and other colors in class names (reported by [@malgorithms](https://github.com/malgorithms)). + +0.9.0 / 2012-12-15 +================== + +* Added stripping quotation from font names (if possible). +* Added stripping quotation from `@keyframes` declaration, `animation` and + `animation-name` property. +* Added stripping quotations from attributes' value (e.g. `[data-target='x']`). +* Added better hex->name and name->hex color shortening. +* Added `font: normal` and `font: bold` shortening the same way as `font-weight` is. +* Refactored shorthand selectors and added `border-radius`, `border-style` + and `border-color` shortening. +* Added `margin`, `padding` and `border-width` shortening. +* Added removing line break after commas. +* Fixed removing whitespace inside media query definition. +* Added removing line breaks after a comma, so all declarations are one-liners now. +* Speed optimizations (~10% despite many new features). +* Added [JSHint](https://github.com/jshint/jshint/) validation rules via `make check`. + +0.8.3 / 2012-11-29 +================== + +* Fixed HSL/HSLA colors processing. + +0.8.2 / 2012-10-31 +================== + +* Fixed shortening hex colors and their relation to hashes in URLs. +* Cleanup by [@XhmikosR](https://github.com/XhmikosR). + +0.8.1 / 2012-10-28 +================== + +* Added better zeros processing for `rect(...)` syntax (clip property). + +0.8.0 / 2012-10-21 +================== + +* Added removing URLs quotation if possible. +* Rewrote breaks processing. +* Added `keepBreaks`/`-b` option to keep line breaks in the minimized file. +* Reformatted [lib/clean.js](/lib/clean.js) so it's easier to follow the rules. +* Minimized test data is now minimized with line breaks so it's easier to + compare the changes line by line. + +0.7.0 / 2012-10-14 +================== + +* Added stripping special comments to CLI (`--s0` and `--s1` options). +* Added stripping special comments to programmatic interface + (`keepSpecialComments` option). + +0.6.0 / 2012-08-05 +================== + +* Full Windows support with tests (./test.bat). + +0.5.0 / 2012-08-02 +================== + +* Made path to vows local. +* Explicit node.js 0.6 requirement. + +0.4.2 / 2012-06-28 +================== + +* Updated binary `-v` option (version). +* Updated binary to output help when no options given (but not in piped mode). +* Added binary tests. + +0.4.1 / 2012-06-10 +================== + +* Fixed stateless mode where calling `CleanCSS#process` directly was giving + errors (reported by [@facelessuser](https://github.com/facelessuser)). + +0.4.0 / 2012-06-04 +================== + +* Speed improvements up to 4x thanks to the rewrite of comments and CSS' content + processing. +* Stripping empty CSS tags is now optional (see [bin/cleancss](/bin/cleancss) for details). +* Improved debugging mode (see [test/bench.js](/test/bench.js)) +* Added `make bench` for a one-pass benchmark. + +0.3.3 / 2012-05-27 +================== + +* Fixed tests, [package.json](/package.json) for development, and regex + for removing empty declarations (thanks to [@vvo](https://github.com/vvo)). + +0.3.2 / 2012-01-17 +================== + +* Fixed output method under node.js 0.6 which incorrectly tried to close + `process.stdout`. + +0.3.1 / 2011-12-16 +================== + +* Fixed cleaning up `0 0 0 0` expressions. + +0.3.0 / 2011-11-29 +================== + +* Clean-css requires node.js 0.4.0+ to run. +* Removed node.js's 0.2.x 'sys' package dependency + (thanks to [@jmalonzo](https://github.com/jmalonzo) for a patch). + +0.2.6 / 2011-11-27 +================== + +* Fixed expanding `+` signs in `calc()` when mixed up with adjacent `+` selector. + +0.2.5 / 2011-11-27 +================== + +* Fixed issue with cleaning up spaces inside `calc`/`-moz-calc` declarations + (thanks to [@cvan](https://github.com/cvan) for reporting it). +* Fixed converting `#f00` to `red` in borders and gradients. + +0.2.4 / 2011-05-25 +================== + +* Fixed problem with expanding `none` to `0` in partial/full background + declarations. +* Fixed including clean-css library from binary (global to local). + +0.2.3 / 2011-04-18 +================== + +* Fixed problem with optimizing IE filters. + +0.2.2 / 2011-04-17 +================== + +* Fixed problem with space before color in `border` property. + +0.2.1 / 2011-03-19 +================== + +* Added stripping space before `!important` keyword. +* Updated repository location and author information in [package.json](/package.json). + +0.2.0 / 2011-03-02 +================== + +* Added options parsing via optimist. +* Changed code inclusion (thus the version bump). + +0.1.0 / 2011-02-27 +================== + +* First version of clean-css library. +* Implemented all basic CSS transformations. diff --git a/node_modules/clean-css/LICENSE b/node_modules/clean-css/LICENSE new file mode 100644 index 000000000..bf2f4055b --- /dev/null +++ b/node_modules/clean-css/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2017 JakubPawlowicz.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/node_modules/clean-css/README.md b/node_modules/clean-css/README.md new file mode 100644 index 000000000..a19594801 --- /dev/null +++ b/node_modules/clean-css/README.md @@ -0,0 +1,619 @@ +<h1 align="center"> + <br/> + <img src="https://cdn.rawgit.com/jakubpawlowicz/clean-css/master/logo.v2.svg" alt="clean-css logo" width="525px"/> + <br/> + <br/> +</h1> + +[![NPM version](https://img.shields.io/npm/v/clean-css.svg?style=flat)](https://www.npmjs.com/package/clean-css) +[![Linux Build Status](https://img.shields.io/travis/jakubpawlowicz/clean-css/master.svg?style=flat&label=Linux%20build)](https://travis-ci.org/jakubpawlowicz/clean-css) +[![Windows Build status](https://img.shields.io/appveyor/ci/jakubpawlowicz/clean-css/master.svg?style=flat&label=Windows%20build)](https://ci.appveyor.com/project/jakubpawlowicz/clean-css/branch/master) +[![Dependency Status](https://img.shields.io/david/jakubpawlowicz/clean-css.svg?style=flat)](https://david-dm.org/jakubpawlowicz/clean-css) +[![NPM Downloads](https://img.shields.io/npm/dm/clean-css.svg)](https://www.npmjs.com/package/clean-css) +[![Twitter](https://img.shields.io/badge/Twitter-@cleancss-blue.svg)](https://twitter.com/cleancss) + +clean-css is a fast and efficient CSS optimizer for [Node.js](http://nodejs.org/) platform and [any modern browser](https://jakubpawlowicz.github.io/clean-css). + +According to [tests](http://goalsmashers.github.io/css-minification-benchmark/) it is one of the best available. + +**Table of Contents** + +- [Node.js version support](#nodejs-version-support) +- [Install](#install) +- [Use](#use) + * [Important: 4.0 breaking changes](#important-40-breaking-changes) + * [Constructor options](#constructor-options) + * [Compatibility modes](#compatibility-modes) + * [Formatting options](#formatting-options) + * [Inlining options](#inlining-options) + * [Optimization levels](#optimization-levels) + + [Level 0 optimizations](#level-0-optimizations) + + [Level 1 optimizations](#level-1-optimizations) + + [Level 2 optimizations](#level-2-optimizations) + * [Minify method](#minify-method) + * [Promise interface](#promise-interface) +- [FAQ](#faq) + * [How to optimize multiple files?](#how-to-optimize-multiple-files) + * [How to process remote `@import`s correctly?](#how-to-process-remote-imports-correctly) + * [How to apply arbitrary transformations to CSS properties?](#how-to-apply-arbitrary-transformations-to-css-properties) + * [How to specify a custom rounding precision?](#how-to-specify-a-custom-rounding-precision) + * [How to preserve a comment block?](#how-to-preserve-a-comment-block) + * [How to rebase relative image URLs?](#how-to-rebase-relative-image-urls) + * [How to work with source maps?](#how-to-work-with-source-maps) + * [How to apply level 1 & 2 optimizations at the same time?](#how-to-apply-level-1--2-optimizations-at-the-same-time) + * [What level 2 optimizations do?](#what-level-2-optimizations-do) + * [How to use clean-css with build tools?](#how-to-use-clean-css-with-build-tools) + * [How to use clean-css from web browser?](#how-to-use-clean-css-from-web-browser) +- [Contributing](#contributing) + * [How to get started?](#how-to-get-started) +- [Acknowledgments](#acknowledgments) +- [License](#license) + +# Node.js version support + +clean-css requires Node.js 4.0+ (tested on Linux, OS X, and Windows) + +# Install + +``` +npm install clean-css +``` + +# Use + +```js +var CleanCSS = require('clean-css'); +var input = 'a{font-weight:bold;}'; +var options = { /* options */ }; +var output = new CleanCSS(options).minify(input); +``` + +## Important: 4.0 breaking changes + +clean-css 4.0 introduces some breaking changes: + +* API and CLI interfaces are split, so API stays in this repository while CLI moves to [clean-css-cli](https://github.com/jakubpawlowicz/clean-css-cli); +* `root`, `relativeTo`, and `target` options are replaced by a single `rebaseTo` option - this means that rebasing URLs and import inlining is much simpler but may not be (YMMV) as powerful as in 3.x; +* `debug` option is gone as stats are always provided in output object under `stats` property; +* `roundingPrecision` is disabled by default; +* `roundingPrecision` applies to **all** units now, not only `px` as in 3.x; +* `processImport` and `processImportFrom` are merged into `inline` option which defaults to `local`. Remote `@import` rules are **NOT** inlined by default anymore; +* splits `inliner: { request: ..., timeout: ... }` option into `inlineRequest` and `inlineTimeout` options; +* remote resources without a protocol, e.g. `//fonts.googleapis.com/css?family=Domine:700`, are not inlined anymore; +* changes default Internet Explorer compatibility from 9+ to 10+, to revert the old default use `{ compatibility: 'ie9' }` flag; +* renames `keepSpecialComments` to `specialComments`; +* moves `roundingPrecision` and `specialComments` to level 1 optimizations options, see examples; +* moves `mediaMerging`, `restructuring`, `semanticMerging`, and `shorthandCompacting` to level 2 optimizations options, see examples below; +* renames `shorthandCompacting` option to `mergeIntoShorthands`; +* level 1 optimizations are the new default, up to 3.x it was level 2; +* `keepBreaks` option is replaced with `{ format: 'keep-breaks' }` to ease transition; +* `sourceMap` option has to be a boolean from now on - to specify an input source map pass it a 2nd argument to `minify` method or via a hash instead; +* `aggressiveMerging` option is removed as aggressive merging is replaced by smarter override merging. + +## Constructor options + +clean-css constructor accepts a hash as a parameter with the following options available: + +* `compatibility` - controls compatibility mode used; defaults to `ie10+`; see [compatibility modes](#compatibility-modes) for examples; +* `format` - controls output CSS formatting; defaults to `false`; see [formatting options](#formatting-options) for examples; +* `inline` - controls `@import` inlining rules; defaults to `'local'`; see [inlining options](#inlining-options) for examples; +* `inlineRequest` - controls extra options for inlining remote `@import` rules, can be any of [HTTP(S) request options](https://nodejs.org/api/http.html#http_http_request_options_callback); +* `inlineTimeout` - controls number of milliseconds after which inlining a remote `@import` fails; defaults to 5000; +* `level` - controls optimization level used; defaults to `1`; see [optimization levels](#optimization-levels) for examples; +* `rebase` - controls URL rebasing; defaults to `true`; +* `rebaseTo` - controls a directory to which all URLs are rebased, most likely the directory under which the output file will live; defaults to the current directory; +* `returnPromise` - controls whether `minify` method returns a Promise object or not; defaults to `false`; see [promise interface](#promise-interface) for examples; +* `sourceMap` - controls whether an output source map is built; defaults to `false`; +* `sourceMapInlineSources` - controls embedding sources inside a source map's `sourcesContent` field; defaults to false. + +## Compatibility modes + +There is a certain number of compatibility mode shortcuts, namely: + +* `new CleanCSS({ compatibility: '*' })` (default) - Internet Explorer 10+ compatibility mode +* `new CleanCSS({ compatibility: 'ie9' })` - Internet Explorer 9+ compatibility mode +* `new CleanCSS({ compatibility: 'ie8' })` - Internet Explorer 8+ compatibility mode +* `new CleanCSS({ compatibility: 'ie7' })` - Internet Explorer 7+ compatibility mode + +Each of these modes is an alias to a [fine grained configuration](https://github.com/jakubpawlowicz/clean-css/blob/master/lib/options/compatibility.js), with the following options available: + +```js +new CleanCSS({ + compatibility: { + colors: { + opacity: true // controls `rgba()` / `hsla()` color support + }, + properties: { + backgroundClipMerging: true, // controls background-clip merging into shorthand + backgroundOriginMerging: true, // controls background-origin merging into shorthand + backgroundSizeMerging: true, // controls background-size merging into shorthand + colors: true, // controls color optimizations + ieBangHack: false, // controls keeping IE bang hack + ieFilters: false, // controls keeping IE `filter` / `-ms-filter` + iePrefixHack: false, // controls keeping IE prefix hack + ieSuffixHack: false, // controls keeping IE suffix hack + merging: true, // controls property merging based on understandability + shorterLengthUnits: false, // controls shortening pixel units into `pc`, `pt`, or `in` units + spaceAfterClosingBrace: true, // controls keeping space after closing brace - `url() no-repeat` into `url()no-repeat` + urlQuotes: false, // controls keeping quoting inside `url()` + zeroUnits: true // controls removal of units `0` value + }, + selectors: { + adjacentSpace: false, // controls extra space before `nav` element + ie7Hack: true, // controls removal of IE7 selector hacks, e.g. `*+html...` + mergeablePseudoClasses: [':active', ...], // controls a whitelist of mergeable pseudo classes + mergeablePseudoElements: ['::after', ...] // controls a whitelist of mergeable pseudo elements + }, + units: { + ch: true, // controls treating `ch` as a supported unit + in: true, // controls treating `in` as a supported unit + pc: true, // controls treating `pc` as a supported unit + pt: true, // controls treating `pt` as a supported unit + rem: true, // controls treating `rem` as a supported unit + vh: true, // controls treating `vh` as a supported unit + vm: true, // controls treating `vm` as a supported unit + vmax: true, // controls treating `vmax` as a supported unit + vmin: true // controls treating `vmin` as a supported unit + } + } +}) +``` + +You can also use a string when setting a compatibility mode, e.g. + +```js +new CleanCSS({ + compatibility: 'ie9,-properties.merging' // sets compatibility to IE9 mode with disabled property merging +}) +``` + +## Formatting options + +The `format` option accept the following options: + +```js +new CleanCSS({ + format: { + breaks: { // controls where to insert breaks + afterAtRule: false, // controls if a line break comes after an at-rule; e.g. `@charset`; defaults to `false` + afterBlockBegins: false, // controls if a line break comes after a block begins; e.g. `@media`; defaults to `false` + afterBlockEnds: false, // controls if a line break comes after a block ends, defaults to `false` + afterComment: false, // controls if a line break comes after a comment; defaults to `false` + afterProperty: false, // controls if a line break comes after a property; defaults to `false` + afterRuleBegins: false, // controls if a line break comes after a rule begins; defaults to `false` + afterRuleEnds: false, // controls if a line break comes after a rule ends; defaults to `false` + beforeBlockEnds: false, // controls if a line break comes before a block ends; defaults to `false` + betweenSelectors: false // controls if a line break comes between selectors; defaults to `false` + }, + indentBy: 0, // controls number of characters to indent with; defaults to `0` + indentWith: 'space', // controls a character to indent with, can be `'space'` or `'tab'`; defaults to `'space'` + spaces: { // controls where to insert spaces + aroundSelectorRelation: false, // controls if spaces come around selector relations; e.g. `div > a`; defaults to `false` + beforeBlockBegins: false, // controls if a space comes before a block begins; e.g. `.block {`; defaults to `false` + beforeValue: false // controls if a space comes before a value; e.g. `width: 1rem`; defaults to `false` + }, + wrapAt: false // controls maximum line length; defaults to `false` + } +}) +``` + +## Inlining options + +`inline` option whitelists which `@import` rules will be processed, e.g. + +```js +new CleanCSS({ + inline: ['local'] // default +}) +``` + +```js +new CleanCSS({ + inline: ['all'] // same as ['local', 'remote'] +}) +``` + +```js +new CleanCSS({ + inline: ['local', 'mydomain.example.com'] +}) +``` + +```js +new CleanCSS({ + inline: ['local', 'remote', '!fonts.googleapis.com'] +}) +``` + +## Optimization levels + +The `level` option can be either `0`, `1` (default), or `2`, e.g. + +```js +new CleanCSS({ + level: 2 +}) +``` + +or a fine-grained configuration given via a hash. + +Please note that level 1 optimization options are generally safe while level 2 optimizations should be safe for most users. + +### Level 0 optimizations + +Level 0 optimizations simply means "no optimizations". Use it when you'd like to inline imports and / or rebase URLs but skip everything else. + +### Level 1 optimizations + +Level 1 optimizations (default) operate on single properties only, e.g. can remove units when not required, turn rgb colors to a shorter hex representation, remove comments, etc + +Here is a full list of available options: + +```js +new CleanCSS({ + level: { + 1: { + cleanupCharsets: true, // controls `@charset` moving to the front of a stylesheet; defaults to `true` + normalizeUrls: true, // controls URL normalization; defaults to `true` + optimizeBackground: true, // controls `background` property optimizatons; defaults to `true` + optimizeBorderRadius: true, // controls `border-radius` property optimizatons; defaults to `true` + optimizeFilter: true, // controls `filter` property optimizatons; defaults to `true` + optimizeFont: true, // ontrols `font` property optimizatons; defaults to `true` + optimizeFontWeight: true, // controls `font-weight` property optimizatons; defaults to `true` + optimizeOutline: true, // controls `outline` property optimizatons; defaults to `true` + removeNegativePaddings: true, // controls removing negative paddings; defaults to `true` + removeQuotes: true, // controls removing quotes when unnecessary; defaults to `true` + removeWhitespace: true, // controls removing unused whitespace; defaults to `true` + replaceMultipleZeros: true, // contols removing redundant zeros; defaults to `true` + replaceTimeUnits: true, // controls replacing time units with shorter values; defaults to `true` + replaceZeroUnits: true, // controls replacing zero values with units; defaults to `true` + roundingPrecision: false, // rounds pixel values to `N` decimal places; `false` disables rounding; defaults to `false` + selectorsSortingMethod: 'standard', // denotes selector sorting method; can be `natural` or `standard`; defaults to `standard` + specialComments: 'all', // denotes a number of /*! ... */ comments preserved; defaults to `all` + tidyAtRules: true, // controls at-rules (e.g. `@charset`, `@import`) optimizing; defaults to `true` + tidyBlockScopes: true, // controls block scopes (e.g. `@media`) optimizing; defaults to `true` + tidySelectors: true, // controls selectors optimizing; defaults to `true`, + transform: function () {} // defines a callback for fine-grained property optimization; defaults to no-op + } + } +}); +``` + +There is an `all` shortcut for toggling all options at the same time, e.g. + +```js +new CleanCSS({ + level: { + 1: { + all: false, // set all values to `false` + tidySelectors: true // turns on optimizing selectors + } + } +}); +``` + +### Level 2 optimizations + +Level 2 optimizations operate at rules or multiple properties level, e.g. can remove duplicate rules, remove properties redefined further down a stylesheet, or restructure rules by moving them around. + +Please note that if level 2 optimizations are turned on then, unless explicitely disabled, level 1 optimizations are applied as well. + +Here is a full list of available options: + +```js +new CleanCSS({ + level: { + 2: { + mergeAdjacentRules: true, // controls adjacent rules merging; defaults to true + mergeIntoShorthands: true, // controls merging properties into shorthands; defaults to true + mergeMedia: true, // controls `@media` merging; defaults to true + mergeNonAdjacentRules: true, // controls non-adjacent rule merging; defaults to true + mergeSemantically: false, // controls semantic merging; defaults to false + overrideProperties: true, // controls property overriding based on understandability; defaults to true + reduceNonAdjacentRules: true, // controls non-adjacent rule reducing; defaults to true + removeDuplicateFontRules: true, // controls duplicate `@font-face` removing; defaults to true + removeDuplicateMediaBlocks: true, // controls duplicate `@media` removing; defaults to true + removeDuplicateRules: true, // controls duplicate rules removing; defaults to true + restructureRules: false // controls rule restructuring; defaults to false + } + } +}); +``` + +There is an `all` shortcut for toggling all options at the same time, e.g. + +```js +new CleanCSS({ + level: { + 2: { + all: false, // sets all values to `false` + removeDuplicateRules: true // turns on removing duplicate rules + } + } +}); +``` + +## Minify method + +Once configured clean-css provides a `minify` method to optimize a given CSS, e.g. + +```js +var output = new CleanCSS(options).minify(source); +``` + +The output of the `minify` method is a hash with following fields: + +```js +console.log(output.styles); // optimized output CSS as a string +console.log(output.sourceMap); // output source map if requested with `sourceMap` option +console.log(output.errors); // a list of errors raised +console.log(output.warnings); // a list of warnings raised +console.log(output.stats.originalSize); // original content size after import inlining +console.log(output.stats.minifiedSize); // optimized content size +console.log(output.stats.timeSpent); // time spent on optimizations in milliseconds +console.log(output.stats.efficiency); // a ratio of output size to input size (e.g. 25% if content was reduced from 100 bytes to 75 bytes) +``` + +The `minify` method also accepts an input source map, e.g. + +```js +var output = new CleanCSS(options).minify(source, inputSourceMap); +``` + +or a callback invoked when optimizations are finished, e.g. + +```js +new CleanCSS(options).minify(source, function (error, output) { + // `output` is the same as in the synchronous call above +}); +``` + +## Promise interface + +If you prefer clean-css to return a Promise object then you need to explicitely ask for it, e.g. + +```js +new CleanCSS({ returnPromise: true }) + .minify(source) + .then(function (output) { console.log(output.styles); }) + .catch(function (error) { // deal with errors }); +``` + +# FAQ + +## How to optimize multiple files? + +It can be done either by passing an array of paths, or, when sources are already available, a hash: + +```js +new CleanCSS().minify(['path/to/file/one', 'path/to/file/two']); +``` + +```js +new CleanCSS().minify({ + 'path/to/file/one': { + styles: 'contents of file one' + }, + 'path/to/file/two': { + styles: 'contents of file two' + } +}); +``` + +Important note - any `@import` rules already present in the hash will be resolved in memory. + +## How to process remote `@import`s correctly? + +In order to inline remote `@import` statements you need to provide a callback to minify method as fetching remote assets is an asynchronous operation, e.g.: + +```js +var source = '@import url(http://example.com/path/to/remote/styles);'; +new CleanCSS({ inline: ['remote'] }).minify(source, function (error, output) { + // output.styles +}); +``` + +If you don't provide a callback, then remote `@import`s will be left as is. + +## How to apply arbitrary transformations to CSS properties? + +If clean-css doesn't perform a particular property optimization, you can use `transform` callback to apply it: + +```js +var source = '.block{background-image:url(/path/to/image.png)}'; +var output = new CleanCSS({ + level: { + 1: { + transform: function (propertyName, propertyValue) { + if (propertyName == 'background-image' && propertyValue.indexOf('/path/to') > -1) { + return propertyValue.replace('/path/to', '../valid/path/to'); + } + } + } + } +}).minify(source); + +console.log(output.styles); # => .block{background-image:url(../valid/path/to/image.png)} +``` + +Note: returning `false` from `transform` callback will drop a property. + +## How to specify a custom rounding precision? + +The level 1 `roundingPrecision` optimization option accept a string with per-unit rounding precision settings, e.g. + +```js +new CleanCSS({ + level: { + 1: { + roundingPrecision: 'all=3,px=5' + } + } +}).minify(source) +``` + +which sets all units rounding precision to 3 digits except `px` unit precision of 5 digits. + +## How to preserve a comment block? + +Use the `/*!` notation instead of the standard one `/*`: + +```css +/*! + Important comments included in optimized output. +*/ +``` + +## How to rebase relative image URLs? + +clean-css will handle it automatically for you in the following cases: + +* when full paths to input files are passed in as options; +* when correct paths are passed in via a hash; +* when `rebaseTo` is used with any of above two. + +## How to work with source maps? + +To generate a source map, use `sourceMap: true` option, e.g.: + +```js +new CleanCSS({ sourceMap: true, rebaseTo: pathToOutputDirectory }) + .minify(source, function (error, output) { + // access output.sourceMap for SourceMapGenerator object + // see https://github.com/mozilla/source-map/#sourcemapgenerator for more details +}); +``` + +You can also pass an input source map directly as a 2nd argument to `minify` method: + +```js +new CleanCSS({ sourceMap: true, rebaseTo: pathToOutputDirectory }) + .minify(source, inputSourceMap, function (error, output) { + // access output.sourceMap to access SourceMapGenerator object + // see https://github.com/mozilla/source-map/#sourcemapgenerator for more details +}); +``` + +or even multiple input source maps at once: + +```js +new CleanCSS({ sourceMap: true, rebaseTo: pathToOutputDirectory }).minify({ + 'path/to/source/1': { + styles: '...styles...', + sourceMap: '...source-map...' + }, + 'path/to/source/2': { + styles: '...styles...', + sourceMap: '...source-map...' + } +}, function (error, output) { + // access output.sourceMap as above +}); +``` + +## How to apply level 1 & 2 optimizations at the same time? + +Using the hash configuration specifying both optimization levels, e.g. + +```js +new CleanCSS({ + level: { + 1: { + all: true, + normalizeUrls: false + }, + 2: { + restructureRules: true + } + } +}) +``` + +will apply level 1 optimizations, except url normalization, and default level 2 optimizations with rule restructuring. + +## What level 2 optimizations do? + +All level 2 optimizations are dispatched [here](https://github.com/jakubpawlowicz/clean-css/blob/master/lib/optimizer/level-2/optimize.js#L67), and this is what they do: + +* `recursivelyOptimizeBlocks` - does all the following operations on a nested block, like `@media` or `@keyframe`; +* `recursivelyOptimizeProperties` - optimizes properties in rulesets and flat at-rules, like @font-face, by splitting them into components (e.g. `margin` into `margin-(bottom|left|right|top)`), optimizing, and restoring them back. You may want to use `mergeIntoShorthands` option to control whether you want to turn multiple components into shorthands; +* `removeDuplicates` - gets rid of duplicate rulesets with exactly the same set of properties, e.g. when including a Sass / Less partial twice for no good reason; +* `mergeAdjacent` - merges adjacent rulesets with the same selector or rules; +* `reduceNonAdjacent` - identifies which properties are overridden in same-selector non-adjacent rulesets, and removes them; +* `mergeNonAdjacentBySelector` - identifies same-selector non-adjacent rulesets which can be moved (!) to be merged, requires all intermediate rulesets to not redefine the moved properties, or if redefined to have the same value; +* `mergeNonAdjacentByBody` - same as the one above but for same-selector non-adjacent rulesets; +* `restructure` - tries to reorganize different-selector different-rules rulesets so they take less space, e.g. `.one{padding:0}.two{margin:0}.one{margin-bottom:3px}` into `.two{margin:0}.one{padding:0;margin-bottom:3px}`; +* `removeDuplicateFontAtRules` - removes duplicated `@font-face` rules; +* `removeDuplicateMediaQueries` - removes duplicated `@media` nested blocks; +* `mergeMediaQueries` - merges non-adjacent `@media` at-rules by the same rules as `mergeNonAdjacentBy*` above; + +## How to use clean-css with build tools? + +There is a number of 3rd party plugins to popular build tools: + +* [Broccoli](https://github.com/broccolijs/broccoli#broccoli): [broccoli-clean-css](https://github.com/shinnn/broccoli-clean-css) +* [Brunch](http://brunch.io/): [clean-css-brunch](https://github.com/brunch/clean-css-brunch) +* [Grunt](http://gruntjs.com): [grunt-contrib-cssmin](https://github.com/gruntjs/grunt-contrib-cssmin) +* [Gulp](http://gulpjs.com/): [gulp-clean-css](https://github.com/scniro/gulp-clean-css) +* [Gulp](http://gulpjs.com/): [using vinyl-map as a wrapper - courtesy of @sogko](https://github.com/jakubpawlowicz/clean-css/issues/342) +* [component-builder2](https://github.com/component/builder2.js): [builder-clean-css](https://github.com/poying/builder-clean-css) +* [Metalsmith](http://metalsmith.io): [metalsmith-clean-css](https://github.com/aymericbeaumet/metalsmith-clean-css) +* [Lasso](https://github.com/lasso-js/lasso): [lasso-clean-css](https://github.com/yomed/lasso-clean-css) +* [Start](https://github.com/start-runner/start): [start-clean-css](https://github.com/start-runner/clean-css) + +## How to use clean-css from web browser? + +* https://jakubpawlowicz.github.io/clean-css/ (official web interface) +* http://refresh-sf.com/ +* http://adamburgess.github.io/clean-css-online/ + +# Contributing + +See [CONTRIBUTING.md](https://github.com/jakubpawlowicz/clean-css/blob/master/CONTRIBUTING.md). + +## How to get started? + +First clone the sources: + +```bash +git clone git@github.com:jakubpawlowicz/clean-css.git +``` + +then install dependencies: + +```bash +cd clean-css +npm install +``` + +then use any of the following commands to verify your copy: + +```bash +npm run bench # for clean-css benchmarks (see [test/bench.js](https://github.com/jakubpawlowicz/clean-css/blob/master/test/bench.js) for details) +npm run browserify # to create the browser-ready clean-css version +npm run check # to lint JS sources with [JSHint](https://github.com/jshint/jshint/) +npm test # to run all tests +``` + +# Acknowledgments + +Sorted alphabetically by GitHub handle: + +* [@abarre](https://github.com/abarre) (Anthony Barre) for improvements to `@import` processing; +* [@alexlamsl](https://github.com/alexlamsl) (Alex Lam S.L.) for testing early clean-css 4 versions, reporting bugs, and suggesting numerous improvements. +* [@altschuler](https://github.com/altschuler) (Simon Altschuler) for fixing `@import` processing inside comments; +* [@ben-eb](https://github.com/ben-eb) (Ben Briggs) for sharing ideas about CSS optimizations; +* [@facelessuser](https://github.com/facelessuser) (Isaac) for pointing out a flaw in clean-css' stateless mode; +* [@jmalonzo](https://github.com/jmalonzo) (Jan Michael Alonzo) for a patch removing node.js' old `sys` package; +* [@lukeapage](https://github.com/lukeapage) (Luke Page) for suggestions and testing the source maps feature; + Plus everyone else involved in [#125](https://github.com/jakubpawlowicz/clean-css/issues/125) for pushing it forward; +* [@madwizard-thomas](https://github.com/madwizard-thomas) for sharing ideas about `@import` inlining and URL rebasing. +* [@ngyikp](https://github.com/ngyikp) (Ng Yik Phang) for testing early clean-css 4 versions, reporting bugs, and suggesting numerous improvements. +* [@wagenet](https://github.com/wagenet) (Peter Wagenet) for suggesting improvements to `@import` inlining behavior; +* [@venemo](https://github.com/venemo) (Timur Kristóf) for an outstanding contribution of advanced property optimizer for 2.2 release; +* [@vvo](https://github.com/vvo) (Vincent Voyer) for a patch with better empty element regex and for inspiring us to do many performance improvements in 0.4 release; +* [@xhmikosr](https://github.com/xhmikosr) for suggesting new features, like option to remove special comments and strip out URLs quotation, and pointing out numerous improvements like JSHint, media queries, etc. + +# License + +clean-css is released under the [MIT License](https://github.com/jakubpawlowicz/clean-css/blob/master/LICENSE). diff --git a/node_modules/clean-css/index.js b/node_modules/clean-css/index.js new file mode 100644 index 000000000..d7b05030f --- /dev/null +++ b/node_modules/clean-css/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/clean'); diff --git a/node_modules/clean-css/lib/clean.js b/node_modules/clean-css/lib/clean.js new file mode 100644 index 000000000..f8268777b --- /dev/null +++ b/node_modules/clean-css/lib/clean.js @@ -0,0 +1,154 @@ +/** + * Clean-css - https://github.com/jakubpawlowicz/clean-css + * Released under the terms of MIT license + * + * Copyright (C) 2017 JakubPawlowicz.com + */ + +var level0Optimize = require('./optimizer/level-0/optimize'); +var level1Optimize = require('./optimizer/level-1/optimize'); +var level2Optimize = require('./optimizer/level-2/optimize'); +var validator = require('./optimizer/validator'); + +var compatibilityFrom = require('./options/compatibility'); +var formatFrom = require('./options/format').formatFrom; +var inlineFrom = require('./options/inline'); +var inlineRequestFrom = require('./options/inline-request'); +var inlineTimeoutFrom = require('./options/inline-timeout'); +var OptimizationLevel = require('./options/optimization-level').OptimizationLevel; +var optimizationLevelFrom = require('./options/optimization-level').optimizationLevelFrom; +var rebaseFrom = require('./options/rebase'); +var rebaseToFrom = require('./options/rebase-to'); + +var inputSourceMapTracker = require('./reader/input-source-map-tracker'); +var readSources = require('./reader/read-sources'); + +var serializeStyles = require('./writer/simple'); +var serializeStylesAndSourceMap = require('./writer/source-maps'); + +var CleanCSS = module.exports = function CleanCSS(options) { + options = options || {}; + + this.options = { + compatibility: compatibilityFrom(options.compatibility), + format: formatFrom(options.format), + inline: inlineFrom(options.inline), + inlineRequest: inlineRequestFrom(options.inlineRequest), + inlineTimeout: inlineTimeoutFrom(options.inlineTimeout), + level: optimizationLevelFrom(options.level), + rebase: rebaseFrom(options.rebase), + rebaseTo: rebaseToFrom(options.rebaseTo), + returnPromise: !!options.returnPromise, + sourceMap: !!options.sourceMap, + sourceMapInlineSources: !!options.sourceMapInlineSources + }; +}; + +CleanCSS.prototype.minify = function (input, maybeSourceMap, maybeCallback) { + var options = this.options; + + if (options.returnPromise) { + return new Promise(function (resolve, reject) { + minify(input, options, maybeSourceMap, function (errors, output) { + return errors ? + reject(errors) : + resolve(output); + }); + }); + } else { + return minify(input, options, maybeSourceMap, maybeCallback); + } +}; + +function minify(input, options, maybeSourceMap, maybeCallback) { + var sourceMap = typeof maybeSourceMap != 'function' ? + maybeSourceMap : + null; + var callback = typeof maybeCallback == 'function' ? + maybeCallback : + (typeof maybeSourceMap == 'function' ? maybeSourceMap : null); + var context = { + stats: { + efficiency: 0, + minifiedSize: 0, + originalSize: 0, + startedAt: Date.now(), + timeSpent: 0 + }, + cache: { + specificity: {} + }, + errors: [], + inlinedStylesheets: [], + inputSourceMapTracker: inputSourceMapTracker(), + localOnly: !callback, + options: options, + source: null, + sourcesContent: {}, + validator: validator(options.compatibility), + warnings: [] + }; + + if (sourceMap) { + context.inputSourceMapTracker.track(undefined, sourceMap); + } + + return runner(context.localOnly)(function () { + return readSources(input, context, function (tokens) { + var serialize = context.options.sourceMap ? + serializeStylesAndSourceMap : + serializeStyles; + + var optimizedTokens = optimize(tokens, context); + var optimizedStyles = serialize(optimizedTokens, context); + var output = withMetadata(optimizedStyles, context); + + return callback ? + callback(context.errors.length > 0 ? context.errors : null, output) : + output; + }); + }); +} + +function runner(localOnly) { + // to always execute code asynchronously when a callback is given + // more at blog.izs.me/post/59142742143/designing-apis-for-asynchrony + return localOnly ? + function (callback) { return callback(); } : + process.nextTick; +} + +function optimize(tokens, context) { + var optimized; + + optimized = level0Optimize(tokens, context); + optimized = OptimizationLevel.One in context.options.level ? + level1Optimize(tokens, context) : + tokens; + optimized = OptimizationLevel.Two in context.options.level ? + level2Optimize(tokens, context, true) : + optimized; + + return optimized; +} + +function withMetadata(output, context) { + output.stats = calculateStatsFrom(output.styles, context); + output.errors = context.errors; + output.inlinedStylesheets = context.inlinedStylesheets; + output.warnings = context.warnings; + + return output; +} + +function calculateStatsFrom(styles, context) { + var finishedAt = Date.now(); + var timeSpent = finishedAt - context.stats.startedAt; + + delete context.stats.startedAt; + context.stats.timeSpent = timeSpent; + context.stats.efficiency = 1 - styles.length / context.stats.originalSize; + context.stats.minifiedSize = styles.length; + + return context.stats; +} diff --git a/node_modules/clean-css/lib/optimizer/hack.js b/node_modules/clean-css/lib/optimizer/hack.js new file mode 100644 index 000000000..812b5d5ec --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/hack.js @@ -0,0 +1,8 @@ +var Hack = { + ASTERISK: 'asterisk', + BANG: 'bang', + BACKSLASH: 'backslash', + UNDERSCORE: 'underscore' +}; + +module.exports = Hack; diff --git a/node_modules/clean-css/lib/optimizer/level-0/optimize.js b/node_modules/clean-css/lib/optimizer/level-0/optimize.js new file mode 100644 index 000000000..2a56f89c6 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-0/optimize.js @@ -0,0 +1,6 @@ +function level0Optimize(tokens) { + // noop as level 0 means no optimizations! + return tokens; +} + +module.exports = level0Optimize; diff --git a/node_modules/clean-css/lib/optimizer/level-1/optimize.js b/node_modules/clean-css/lib/optimizer/level-1/optimize.js new file mode 100644 index 000000000..e7a9bbb47 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/optimize.js @@ -0,0 +1,734 @@ +var shortenHex = require('./shorten-hex'); +var shortenHsl = require('./shorten-hsl'); +var shortenRgb = require('./shorten-rgb'); +var sortSelectors = require('./sort-selectors'); +var tidyRules = require('./tidy-rules'); +var tidyBlock = require('./tidy-block'); +var tidyAtRule = require('./tidy-at-rule'); + +var Hack = require('../hack'); +var removeUnused = require('../remove-unused'); +var restoreFromOptimizing = require('../restore-from-optimizing'); +var wrapForOptimizing = require('../wrap-for-optimizing').all; + +var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; + +var Token = require('../../tokenizer/token'); +var Marker = require('../../tokenizer/marker'); + +var formatPosition = require('../../utils/format-position'); +var split = require('../../utils/split'); + +var IgnoreProperty = 'ignore-property'; + +var CHARSET_TOKEN = '@charset'; +var CHARSET_REGEXP = new RegExp('^' + CHARSET_TOKEN, 'i'); + +var DEFAULT_ROUNDING_PRECISION = require('../../options/rounding-precision').DEFAULT; + +var FONT_NUMERAL_WEIGHTS = ['100', '200', '300', '400', '500', '600', '700', '800', '900']; +var FONT_NAME_WEIGHTS = ['normal', 'bold', 'bolder', 'lighter']; +var FONT_NAME_WEIGHTS_WITHOUT_NORMAL = ['bold', 'bolder', 'lighter']; + +var WHOLE_PIXEL_VALUE = /(?:^|\s|\()(-?\d+)px/; +var TIME_VALUE = /^(\-?[\d\.]+)(m?s)$/; + +var PROPERTY_NAME_PATTERN = /^(?:\-chrome\-|\-[\w\-]+\w|\w[\w\-]+\w|\-\-\S+)$/; +var IMPORT_PREFIX_PATTERN = /^@import/i; +var QUOTED_PATTERN = /^('.*'|".*")$/; +var QUOTED_BUT_SAFE_PATTERN = /^['"][a-zA-Z][a-zA-Z\d\-_]+['"]$/; +var URL_PREFIX_PATTERN = /^url\(/i; +var VARIABLE_NAME_PATTERN = /^--\S+$/; + +function isNegative(value) { + return value && value[1][0] == '-' && parseFloat(value[1]) < 0; +} + +function isQuoted(value) { + return QUOTED_PATTERN.test(value); +} + +function isUrl(value) { + return URL_PREFIX_PATTERN.test(value); +} + +function normalizeUrl(value) { + return value + .replace(URL_PREFIX_PATTERN, 'url(') + .replace(/\\?\n|\\?\r\n/g, ''); +} + +function optimizeBackground(property) { + var values = property.value; + + if (values.length == 1 && values[0][1] == 'none') { + values[0][1] = '0 0'; + } + + if (values.length == 1 && values[0][1] == 'transparent') { + values[0][1] = '0 0'; + } +} + +function optimizeBorderRadius(property) { + var values = property.value; + var spliceAt; + + if (values.length == 3 && values[1][1] == '/' && values[0][1] == values[2][1]) { + spliceAt = 1; + } else if (values.length == 5 && values[2][1] == '/' && values[0][1] == values[3][1] && values[1][1] == values[4][1]) { + spliceAt = 2; + } else if (values.length == 7 && values[3][1] == '/' && values[0][1] == values[4][1] && values[1][1] == values[5][1] && values[2][1] == values[6][1]) { + spliceAt = 3; + } else if (values.length == 9 && values[4][1] == '/' && values[0][1] == values[5][1] && values[1][1] == values[6][1] && values[2][1] == values[7][1] && values[3][1] == values[8][1]) { + spliceAt = 4; + } + + if (spliceAt) { + property.value.splice(spliceAt); + property.dirty = true; + } +} + +function optimizeColors(name, value, compatibility) { + if (value.indexOf('#') === -1 && value.indexOf('rgb') == -1 && value.indexOf('hsl') == -1) { + return shortenHex(value); + } + + value = value + .replace(/rgb\((\-?\d+),(\-?\d+),(\-?\d+)\)/g, function (match, red, green, blue) { + return shortenRgb(red, green, blue); + }) + .replace(/hsl\((-?\d+),(-?\d+)%?,(-?\d+)%?\)/g, function (match, hue, saturation, lightness) { + return shortenHsl(hue, saturation, lightness); + }) + .replace(/(^|[^='"])#([0-9a-f]{6})/gi, function (match, prefix, color) { + if (color[0] == color[1] && color[2] == color[3] && color[4] == color[5]) { + return (prefix + '#' + color[0] + color[2] + color[4]).toLowerCase(); + } else { + return (prefix + '#' + color).toLowerCase(); + } + }) + .replace(/(^|[^='"])#([0-9a-f]{3})/gi, function (match, prefix, color) { + return prefix + '#' + color.toLowerCase(); + }) + .replace(/(rgb|rgba|hsl|hsla)\(([^\)]+)\)/g, function (match, colorFunction, colorDef) { + var tokens = colorDef.split(','); + var applies = (colorFunction == 'hsl' && tokens.length == 3) || + (colorFunction == 'hsla' && tokens.length == 4) || + (colorFunction == 'rgb' && tokens.length == 3 && colorDef.indexOf('%') > 0) || + (colorFunction == 'rgba' && tokens.length == 4 && colorDef.indexOf('%') > 0); + + if (!applies) { + return match; + } + + if (tokens[1].indexOf('%') == -1) { + tokens[1] += '%'; + } + + if (tokens[2].indexOf('%') == -1) { + tokens[2] += '%'; + } + + return colorFunction + '(' + tokens.join(',') + ')'; + }); + + if (compatibility.colors.opacity && name.indexOf('background') == -1) { + value = value.replace(/(?:rgba|hsla)\(0,0%?,0%?,0\)/g, function (match) { + if (split(value, ',').pop().indexOf('gradient(') > -1) { + return match; + } + + return 'transparent'; + }); + } + + return shortenHex(value); +} + +function optimizeFilter(property) { + if (property.value.length == 1) { + property.value[0][1] = property.value[0][1].replace(/progid:DXImageTransform\.Microsoft\.(Alpha|Chroma)(\W)/, function (match, filter, suffix) { + return filter.toLowerCase() + suffix; + }); + } + + property.value[0][1] = property.value[0][1] + .replace(/,(\S)/g, ', $1') + .replace(/ ?= ?/g, '='); +} + +function optimizeFont(property, options) { + var values = property.value; + var hasNumeral = FONT_NUMERAL_WEIGHTS.indexOf(values[0][1]) > -1 || + values[1] && FONT_NUMERAL_WEIGHTS.indexOf(values[1][1]) > -1 || + values[2] && FONT_NUMERAL_WEIGHTS.indexOf(values[2][1]) > -1; + var canOptimizeFontWeight = options.level[OptimizationLevel.One].optimizeFontWeight; + var normalCount = 0; + var toOptimize; + + if (!canOptimizeFontWeight) { + return; + } + + if (hasNumeral) { + return; + } + + if (values[1] && values[1][1] == '/') { + return; + } + + if (values[0][1] == 'normal') { + normalCount++; + } + + if (values[1] && values[1][1] == 'normal') { + normalCount++; + } + + if (values[2] && values[2][1] == 'normal') { + normalCount++; + } + + if (normalCount > 1) { + return; + } + + if (FONT_NAME_WEIGHTS_WITHOUT_NORMAL.indexOf(values[0][1]) > -1) { + toOptimize = 0; + } else if (values[1] && FONT_NAME_WEIGHTS_WITHOUT_NORMAL.indexOf(values[1][1]) > -1) { + toOptimize = 1; + } else if (values[2] && FONT_NAME_WEIGHTS_WITHOUT_NORMAL.indexOf(values[2][1]) > -1) { + toOptimize = 2; + } else if (FONT_NAME_WEIGHTS.indexOf(values[0][1]) > -1) { + toOptimize = 0; + } else if (values[1] && FONT_NAME_WEIGHTS.indexOf(values[1][1]) > -1) { + toOptimize = 1; + } else if (values[2] && FONT_NAME_WEIGHTS.indexOf(values[2][1]) > -1) { + toOptimize = 2; + } + + if (toOptimize !== undefined && canOptimizeFontWeight) { + optimizeFontWeight(property, toOptimize); + property.dirty = true; + } +} + +function optimizeFontWeight(property, atIndex) { + var value = property.value[atIndex][1]; + + if (value == 'normal') { + value = '400'; + } else if (value == 'bold') { + value = '700'; + } + + property.value[atIndex][1] = value; +} + +function optimizeMultipleZeros(property) { + var values = property.value; + var spliceAt; + + if (values.length == 4 && values[0][1] === '0' && values[1][1] === '0' && values[2][1] === '0' && values[3][1] === '0') { + if (property.name.indexOf('box-shadow') > -1) { + spliceAt = 2; + } else { + spliceAt = 1; + } + } + + if (spliceAt) { + property.value.splice(spliceAt); + property.dirty = true; + } +} + +function optimizeOutline(property) { + var values = property.value; + + if (values.length == 1 && values[0][1] == 'none') { + values[0][1] = '0'; + } +} + +function optimizePixelLengths(_, value, compatibility) { + if (!WHOLE_PIXEL_VALUE.test(value)) { + return value; + } + + return value.replace(WHOLE_PIXEL_VALUE, function (match, val) { + var newValue; + var intVal = parseInt(val); + + if (intVal === 0) { + return match; + } + + if (compatibility.properties.shorterLengthUnits && compatibility.units.pt && intVal * 3 % 4 === 0) { + newValue = intVal * 3 / 4 + 'pt'; + } + + if (compatibility.properties.shorterLengthUnits && compatibility.units.pc && intVal % 16 === 0) { + newValue = intVal / 16 + 'pc'; + } + + if (compatibility.properties.shorterLengthUnits && compatibility.units.in && intVal % 96 === 0) { + newValue = intVal / 96 + 'in'; + } + + if (newValue) { + newValue = match.substring(0, match.indexOf(val)) + newValue; + } + + return newValue && newValue.length < match.length ? newValue : match; + }); +} + +function optimizePrecision(_, value, precisionOptions) { + if (!precisionOptions.enabled || value.indexOf('.') === -1) { + return value; + } + + return value + .replace(precisionOptions.decimalPointMatcher, '$1$2$3') + .replace(precisionOptions.zeroMatcher, function (match, integerPart, fractionPart, unit) { + var multiplier = precisionOptions.units[unit].multiplier; + var parsedInteger = parseInt(integerPart); + var integer = isNaN(parsedInteger) ? 0 : parsedInteger; + var fraction = parseFloat(fractionPart); + + return Math.round((integer + fraction) * multiplier) / multiplier + unit; + }); +} + +function optimizeTimeUnits(_, value) { + if (!TIME_VALUE.test(value)) + return value; + + return value.replace(TIME_VALUE, function (match, val, unit) { + var newValue; + + if (unit == 'ms') { + newValue = parseInt(val) / 1000 + 's'; + } else if (unit == 's') { + newValue = parseFloat(val) * 1000 + 'ms'; + } + + return newValue.length < match.length ? newValue : match; + }); +} + +function optimizeUnits(name, value, unitsRegexp) { + if (/^(?:\-moz\-calc|\-webkit\-calc|calc|rgb|hsl|rgba|hsla)\(/.test(value)) { + return value; + } + + if (name == 'flex' || name == '-ms-flex' || name == '-webkit-flex' || name == 'flex-basis' || name == '-webkit-flex-basis') { + return value; + } + + if (value.indexOf('%') > 0 && (name == 'height' || name == 'max-height')) { + return value; + } + + return value + .replace(unitsRegexp, '$1' + '0' + '$2') + .replace(unitsRegexp, '$1' + '0' + '$2'); +} + +function optimizeWhitespace(name, value) { + if (name.indexOf('filter') > -1 || value.indexOf(' ') == -1 || value.indexOf('expression') === 0) { + return value; + } + + if (value.indexOf(Marker.SINGLE_QUOTE) > -1 || value.indexOf(Marker.DOUBLE_QUOTE) > -1) { + return value; + } + + value = value.replace(/\s+/g, ' '); + + if (value.indexOf('calc') > -1) { + value = value.replace(/\) ?\/ ?/g, ')/ '); + } + + return value + .replace(/(\(;?)\s+/g, '$1') + .replace(/\s+(;?\))/g, '$1') + .replace(/, /g, ','); +} + +function optimizeZeroDegUnit(_, value) { + if (value.indexOf('0deg') == -1) { + return value; + } + + return value.replace(/\(0deg\)/g, '(0)'); +} + +function optimizeZeroUnits(name, value) { + if (value.indexOf('0') == -1) { + return value; + } + + if (value.indexOf('-') > -1) { + value = value + .replace(/([^\w\d\-]|^)\-0([^\.]|$)/g, '$10$2') + .replace(/([^\w\d\-]|^)\-0([^\.]|$)/g, '$10$2'); + } + + return value + .replace(/(^|\s)0+([1-9])/g, '$1$2') + .replace(/(^|\D)\.0+(\D|$)/g, '$10$2') + .replace(/(^|\D)\.0+(\D|$)/g, '$10$2') + .replace(/\.([1-9]*)0+(\D|$)/g, function (match, nonZeroPart, suffix) { + return (nonZeroPart.length > 0 ? '.' : '') + nonZeroPart + suffix; + }) + .replace(/(^|\D)0\.(\d)/g, '$1.$2'); +} + +function removeQuotes(name, value) { + if (name == 'content' || name.indexOf('font-feature-settings') > -1 || name.indexOf('grid-') > -1) { + return value; + } + + return QUOTED_BUT_SAFE_PATTERN.test(value) ? + value.substring(1, value.length - 1) : + value; +} + +function removeUrlQuotes(value) { + return /^url\(['"].+['"]\)$/.test(value) && !/^url\(['"].*[\*\s\(\)'"].*['"]\)$/.test(value) && !/^url\(['"]data:[^;]+;charset/.test(value) ? + value.replace(/["']/g, '') : + value; +} + +function transformValue(propertyName, propertyValue, transformCallback) { + var transformedValue = transformCallback(propertyName, propertyValue); + + if (transformedValue === undefined) { + return propertyValue; + } else if (transformedValue === false) { + return IgnoreProperty; + } else { + return transformedValue; + } +} + +// + +function optimizeBody(properties, context) { + var options = context.options; + var levelOptions = options.level[OptimizationLevel.One]; + var property, name, type, value; + var valueIsUrl; + var propertyToken; + var _properties = wrapForOptimizing(properties, true); + + propertyLoop: + for (var i = 0, l = _properties.length; i < l; i++) { + property = _properties[i]; + name = property.name; + + if (!PROPERTY_NAME_PATTERN.test(name)) { + propertyToken = property.all[property.position]; + context.warnings.push('Invalid property name \'' + name + '\' at ' + formatPosition(propertyToken[1][2][0]) + '. Ignoring.'); + property.unused = true; + } + + if (property.value.length === 0) { + propertyToken = property.all[property.position]; + context.warnings.push('Empty property \'' + name + '\' at ' + formatPosition(propertyToken[1][2][0]) + '. Ignoring.'); + property.unused = true; + } + + if (property.hack && ( + (property.hack[0] == Hack.ASTERISK || property.hack[0] == Hack.UNDERSCORE) && !options.compatibility.properties.iePrefixHack || + property.hack[0] == Hack.BACKSLASH && !options.compatibility.properties.ieSuffixHack || + property.hack[0] == Hack.BANG && !options.compatibility.properties.ieBangHack)) { + property.unused = true; + } + + if (levelOptions.removeNegativePaddings && name.indexOf('padding') === 0 && (isNegative(property.value[0]) || isNegative(property.value[1]) || isNegative(property.value[2]) || isNegative(property.value[3]))) { + property.unused = true; + } + + if (!options.compatibility.properties.ieFilters && isLegacyFilter(property)) { + property.unused = true; + } + + if (property.unused) { + continue; + } + + if (property.block) { + optimizeBody(property.value[0][1], context); + continue; + } + + if (VARIABLE_NAME_PATTERN.test(name)) { + continue; + } + + for (var j = 0, m = property.value.length; j < m; j++) { + type = property.value[j][0]; + value = property.value[j][1]; + valueIsUrl = isUrl(value); + + if (type == Token.PROPERTY_BLOCK) { + property.unused = true; + context.warnings.push('Invalid value token at ' + formatPosition(value[0][1][2][0]) + '. Ignoring.'); + break; + } + + if (valueIsUrl && !context.validator.isValidUrl(value)) { + property.unused = true; + context.warnings.push('Broken URL \'' + value + '\' at ' + formatPosition(property.value[j][2][0]) + '. Ignoring.'); + break; + } + + if (valueIsUrl) { + value = levelOptions.normalizeUrls ? + normalizeUrl(value) : + value; + value = !options.compatibility.properties.urlQuotes ? + removeUrlQuotes(value) : + value; + } else if (isQuoted(value)) { + value = levelOptions.removeQuotes ? + removeQuotes(name, value) : + value; + } else { + value = levelOptions.removeWhitespace ? + optimizeWhitespace(name, value) : + value; + value = optimizePrecision(name, value, options.precision); + value = optimizePixelLengths(name, value, options.compatibility); + value = levelOptions.replaceTimeUnits ? + optimizeTimeUnits(name, value) : + value; + value = levelOptions.replaceZeroUnits ? + optimizeZeroUnits(name, value) : + value; + + if (options.compatibility.properties.zeroUnits) { + value = optimizeZeroDegUnit(name, value); + value = optimizeUnits(name, value, options.unitsRegexp); + } + + if (options.compatibility.properties.colors) { + value = optimizeColors(name, value, options.compatibility); + } + } + + value = transformValue(name, value, levelOptions.transform); + + if (value === IgnoreProperty) { + property.unused = true; + continue propertyLoop; + } + + property.value[j][1] = value; + } + + if (levelOptions.replaceMultipleZeros) { + optimizeMultipleZeros(property); + } + + if (name == 'background' && levelOptions.optimizeBackground) { + optimizeBackground(property); + } else if (name.indexOf('border') === 0 && name.indexOf('radius') > 0 && levelOptions.optimizeBorderRadius) { + optimizeBorderRadius(property); + } else if (name == 'filter'&& levelOptions.optimizeFilter && options.compatibility.properties.ieFilters) { + optimizeFilter(property); + } else if (name == 'font' && levelOptions.optimizeFont) { + optimizeFont(property, options); + } else if (name == 'font-weight' && levelOptions.optimizeFontWeight) { + optimizeFontWeight(property, 0); + } else if (name == 'outline' && levelOptions.optimizeOutline) { + optimizeOutline(property); + } + } + + restoreFromOptimizing(_properties); + removeUnused(_properties); + + if (_properties.length != properties.length) { + removeComments(properties, options); + } +} + +function removeComments(tokens, options) { + var token; + var i; + + for (i = 0; i < tokens.length; i++) { + token = tokens[i]; + + if (token[0] != Token.COMMENT) { + continue; + } + + optimizeComment(token, options); + + if (token[1].length === 0) { + tokens.splice(i, 1); + i--; + } + } +} + +function optimizeComment(token, options) { + if (token[1][2] == Marker.EXCLAMATION && (options.level[OptimizationLevel.One].specialComments == 'all' || options.commentsKept < options.level[OptimizationLevel.One].specialComments)) { + options.commentsKept++; + return; + } + + token[1] = []; +} + +function cleanupCharsets(tokens) { + var hasCharset = false; + + for (var i = 0, l = tokens.length; i < l; i++) { + var token = tokens[i]; + + if (token[0] != Token.AT_RULE) + continue; + + if (!CHARSET_REGEXP.test(token[1])) + continue; + + if (hasCharset || token[1].indexOf(CHARSET_TOKEN) == -1) { + tokens.splice(i, 1); + i--; + l--; + } else { + hasCharset = true; + tokens.splice(i, 1); + tokens.unshift([Token.AT_RULE, token[1].replace(CHARSET_REGEXP, CHARSET_TOKEN)]); + } + } +} + +function buildUnitRegexp(options) { + var units = ['px', 'em', 'ex', 'cm', 'mm', 'in', 'pt', 'pc', '%']; + var otherUnits = ['ch', 'rem', 'vh', 'vm', 'vmax', 'vmin', 'vw']; + + otherUnits.forEach(function (unit) { + if (options.compatibility.units[unit]) { + units.push(unit); + } + }); + + return new RegExp('(^|\\s|\\(|,)0(?:' + units.join('|') + ')(\\W|$)', 'g'); +} + +function buildPrecisionOptions(roundingPrecision) { + var precisionOptions = { + matcher: null, + units: {}, + }; + var optimizable = []; + var unit; + var value; + + for (unit in roundingPrecision) { + value = roundingPrecision[unit]; + + if (value != DEFAULT_ROUNDING_PRECISION) { + precisionOptions.units[unit] = {}; + precisionOptions.units[unit].value = value; + precisionOptions.units[unit].multiplier = Math.pow(10, value); + + optimizable.push(unit); + } + } + + if (optimizable.length > 0) { + precisionOptions.enabled = true; + precisionOptions.decimalPointMatcher = new RegExp('(\\d)\\.($|' + optimizable.join('|') + ')($|\W)', 'g'); + precisionOptions.zeroMatcher = new RegExp('(\\d*)(\\.\\d+)(' + optimizable.join('|') + ')', 'g'); + } + + return precisionOptions; +} + +function isImport(token) { + return IMPORT_PREFIX_PATTERN.test(token[1]); +} + +function isLegacyFilter(property) { + var value; + + if (property.name == 'filter' || property.name == '-ms-filter') { + value = property.value[0][1]; + + return value.indexOf('progid') > -1 || + value.indexOf('alpha') === 0 || + value.indexOf('chroma') === 0; + } else { + return false; + } +} + +function level1Optimize(tokens, context) { + var options = context.options; + var levelOptions = options.level[OptimizationLevel.One]; + var ie7Hack = options.compatibility.selectors.ie7Hack; + var adjacentSpace = options.compatibility.selectors.adjacentSpace; + var spaceAfterClosingBrace = options.compatibility.properties.spaceAfterClosingBrace; + var format = options.format; + var mayHaveCharset = false; + var afterRules = false; + + options.unitsRegexp = options.unitsRegexp || buildUnitRegexp(options); + options.precision = options.precision || buildPrecisionOptions(levelOptions.roundingPrecision); + options.commentsKept = options.commentsKept || 0; + + for (var i = 0, l = tokens.length; i < l; i++) { + var token = tokens[i]; + + switch (token[0]) { + case Token.AT_RULE: + token[1] = isImport(token) && afterRules ? '' : token[1]; + token[1] = levelOptions.tidyAtRules ? tidyAtRule(token[1]) : token[1]; + mayHaveCharset = true; + break; + case Token.AT_RULE_BLOCK: + optimizeBody(token[2], context); + afterRules = true; + break; + case Token.NESTED_BLOCK: + token[1] = levelOptions.tidyBlockScopes ? tidyBlock(token[1], spaceAfterClosingBrace) : token[1]; + level1Optimize(token[2], context); + afterRules = true; + break; + case Token.COMMENT: + optimizeComment(token, options); + break; + case Token.RULE: + token[1] = levelOptions.tidySelectors ? tidyRules(token[1], !ie7Hack, adjacentSpace, format, context.warnings) : token[1]; + token[1] = token[1].length > 1 ? sortSelectors(token[1], levelOptions.selectorsSortingMethod) : token[1]; + optimizeBody(token[2], context); + afterRules = true; + break; + } + + if (token[1].length === 0 || (token[2] && token[2].length === 0)) { + tokens.splice(i, 1); + i--; + l--; + } + } + + if (levelOptions.cleanupCharsets && mayHaveCharset) { + cleanupCharsets(tokens); + } + + return tokens; +} + +module.exports = level1Optimize; diff --git a/node_modules/clean-css/lib/optimizer/level-1/shorten-hex.js b/node_modules/clean-css/lib/optimizer/level-1/shorten-hex.js new file mode 100644 index 000000000..3deea381c --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/shorten-hex.js @@ -0,0 +1,189 @@ +var COLORS = { + aliceblue: '#f0f8ff', + antiquewhite: '#faebd7', + aqua: '#0ff', + aquamarine: '#7fffd4', + azure: '#f0ffff', + beige: '#f5f5dc', + bisque: '#ffe4c4', + black: '#000', + blanchedalmond: '#ffebcd', + blue: '#00f', + blueviolet: '#8a2be2', + brown: '#a52a2a', + burlywood: '#deb887', + cadetblue: '#5f9ea0', + chartreuse: '#7fff00', + chocolate: '#d2691e', + coral: '#ff7f50', + cornflowerblue: '#6495ed', + cornsilk: '#fff8dc', + crimson: '#dc143c', + cyan: '#0ff', + darkblue: '#00008b', + darkcyan: '#008b8b', + darkgoldenrod: '#b8860b', + darkgray: '#a9a9a9', + darkgreen: '#006400', + darkgrey: '#a9a9a9', + darkkhaki: '#bdb76b', + darkmagenta: '#8b008b', + darkolivegreen: '#556b2f', + darkorange: '#ff8c00', + darkorchid: '#9932cc', + darkred: '#8b0000', + darksalmon: '#e9967a', + darkseagreen: '#8fbc8f', + darkslateblue: '#483d8b', + darkslategray: '#2f4f4f', + darkslategrey: '#2f4f4f', + darkturquoise: '#00ced1', + darkviolet: '#9400d3', + deeppink: '#ff1493', + deepskyblue: '#00bfff', + dimgray: '#696969', + dimgrey: '#696969', + dodgerblue: '#1e90ff', + firebrick: '#b22222', + floralwhite: '#fffaf0', + forestgreen: '#228b22', + fuchsia: '#f0f', + gainsboro: '#dcdcdc', + ghostwhite: '#f8f8ff', + gold: '#ffd700', + goldenrod: '#daa520', + gray: '#808080', + green: '#008000', + greenyellow: '#adff2f', + grey: '#808080', + honeydew: '#f0fff0', + hotpink: '#ff69b4', + indianred: '#cd5c5c', + indigo: '#4b0082', + ivory: '#fffff0', + khaki: '#f0e68c', + lavender: '#e6e6fa', + lavenderblush: '#fff0f5', + lawngreen: '#7cfc00', + lemonchiffon: '#fffacd', + lightblue: '#add8e6', + lightcoral: '#f08080', + lightcyan: '#e0ffff', + lightgoldenrodyellow: '#fafad2', + lightgray: '#d3d3d3', + lightgreen: '#90ee90', + lightgrey: '#d3d3d3', + lightpink: '#ffb6c1', + lightsalmon: '#ffa07a', + lightseagreen: '#20b2aa', + lightskyblue: '#87cefa', + lightslategray: '#778899', + lightslategrey: '#778899', + lightsteelblue: '#b0c4de', + lightyellow: '#ffffe0', + lime: '#0f0', + limegreen: '#32cd32', + linen: '#faf0e6', + magenta: '#ff00ff', + maroon: '#800000', + mediumaquamarine: '#66cdaa', + mediumblue: '#0000cd', + mediumorchid: '#ba55d3', + mediumpurple: '#9370db', + mediumseagreen: '#3cb371', + mediumslateblue: '#7b68ee', + mediumspringgreen: '#00fa9a', + mediumturquoise: '#48d1cc', + mediumvioletred: '#c71585', + midnightblue: '#191970', + mintcream: '#f5fffa', + mistyrose: '#ffe4e1', + moccasin: '#ffe4b5', + navajowhite: '#ffdead', + navy: '#000080', + oldlace: '#fdf5e6', + olive: '#808000', + olivedrab: '#6b8e23', + orange: '#ffa500', + orangered: '#ff4500', + orchid: '#da70d6', + palegoldenrod: '#eee8aa', + palegreen: '#98fb98', + paleturquoise: '#afeeee', + palevioletred: '#db7093', + papayawhip: '#ffefd5', + peachpuff: '#ffdab9', + peru: '#cd853f', + pink: '#ffc0cb', + plum: '#dda0dd', + powderblue: '#b0e0e6', + purple: '#800080', + rebeccapurple: '#663399', + red: '#f00', + rosybrown: '#bc8f8f', + royalblue: '#4169e1', + saddlebrown: '#8b4513', + salmon: '#fa8072', + sandybrown: '#f4a460', + seagreen: '#2e8b57', + seashell: '#fff5ee', + sienna: '#a0522d', + silver: '#c0c0c0', + skyblue: '#87ceeb', + slateblue: '#6a5acd', + slategray: '#708090', + slategrey: '#708090', + snow: '#fffafa', + springgreen: '#00ff7f', + steelblue: '#4682b4', + tan: '#d2b48c', + teal: '#008080', + thistle: '#d8bfd8', + tomato: '#ff6347', + turquoise: '#40e0d0', + violet: '#ee82ee', + wheat: '#f5deb3', + white: '#fff', + whitesmoke: '#f5f5f5', + yellow: '#ff0', + yellowgreen: '#9acd32' +}; + +var toHex = {}; +var toName = {}; + +for (var name in COLORS) { + var hex = COLORS[name]; + + if (name.length < hex.length) { + toName[hex] = name; + } else { + toHex[name] = hex; + } +} + +var toHexPattern = new RegExp('(^| |,|\\))(' + Object.keys(toHex).join('|') + ')( |,|\\)|$)', 'ig'); +var toNamePattern = new RegExp('(' + Object.keys(toName).join('|') + ')([^a-f0-9]|$)', 'ig'); + +function hexConverter(match, prefix, colorValue, suffix) { + return prefix + toHex[colorValue.toLowerCase()] + suffix; +} + +function nameConverter(match, colorValue, suffix) { + return toName[colorValue.toLowerCase()] + suffix; +} + +function shortenHex(value) { + var hasHex = value.indexOf('#') > -1; + var shortened = value.replace(toHexPattern, hexConverter); + + if (shortened != value) { + shortened = shortened.replace(toHexPattern, hexConverter); + } + + return hasHex ? + shortened.replace(toNamePattern, nameConverter) : + shortened; +} + +module.exports = shortenHex; diff --git a/node_modules/clean-css/lib/optimizer/level-1/shorten-hsl.js b/node_modules/clean-css/lib/optimizer/level-1/shorten-hsl.js new file mode 100644 index 000000000..fe98dfd39 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/shorten-hsl.js @@ -0,0 +1,61 @@ +// HSL to RGB converter. Both methods adapted from: +// http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript + +function hslToRgb(h, s, l) { + var r, g, b; + + // normalize hue orientation b/w 0 and 360 degrees + h = h % 360; + if (h < 0) + h += 360; + h = ~~h / 360; + + if (s < 0) + s = 0; + else if (s > 100) + s = 100; + s = ~~s / 100; + + if (l < 0) + l = 0; + else if (l > 100) + l = 100; + l = ~~l / 100; + + if (s === 0) { + r = g = b = l; // achromatic + } else { + var q = l < 0.5 ? + l * (1 + s) : + l + s - l * s; + var p = 2 * l - q; + r = hueToRgb(p, q, h + 1/3); + g = hueToRgb(p, q, h); + b = hueToRgb(p, q, h - 1/3); + } + + return [~~(r * 255), ~~(g * 255), ~~(b * 255)]; +} + +function hueToRgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1/6) return p + (q - p) * 6 * t; + if (t < 1/2) return q; + if (t < 2/3) return p + (q - p) * (2/3 - t) * 6; + return p; +} + +function shortenHsl(hue, saturation, lightness) { + var asRgb = hslToRgb(hue, saturation, lightness); + var redAsHex = asRgb[0].toString(16); + var greenAsHex = asRgb[1].toString(16); + var blueAsHex = asRgb[2].toString(16); + + return '#' + + ((redAsHex.length == 1 ? '0' : '') + redAsHex) + + ((greenAsHex.length == 1 ? '0' : '') + greenAsHex) + + ((blueAsHex.length == 1 ? '0' : '') + blueAsHex); +} + +module.exports = shortenHsl; diff --git a/node_modules/clean-css/lib/optimizer/level-1/shorten-rgb.js b/node_modules/clean-css/lib/optimizer/level-1/shorten-rgb.js new file mode 100644 index 000000000..3c0a5fa31 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/shorten-rgb.js @@ -0,0 +1,10 @@ +function shortenRgb(red, green, blue) { + var normalizedRed = Math.max(0, Math.min(parseInt(red), 255)); + var normalizedGreen = Math.max(0, Math.min(parseInt(green), 255)); + var normalizedBlue = Math.max(0, Math.min(parseInt(blue), 255)); + + // Credit: Asen http://jsbin.com/UPUmaGOc/2/edit?js,console + return '#' + ('00000' + (normalizedRed << 16 | normalizedGreen << 8 | normalizedBlue).toString(16)).slice(-6); +} + +module.exports = shortenRgb; diff --git a/node_modules/clean-css/lib/optimizer/level-1/sort-selectors.js b/node_modules/clean-css/lib/optimizer/level-1/sort-selectors.js new file mode 100644 index 000000000..2f179e6ef --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/sort-selectors.js @@ -0,0 +1,25 @@ +var naturalCompare = require('../../utils/natural-compare'); + +function naturalSorter(scope1, scope2) { + return naturalCompare(scope1[1], scope2[1]); +} + +function standardSorter(scope1, scope2) { + return scope1[1] > scope2[1] ? 1 : -1; +} + +function sortSelectors(selectors, method) { + var sorter; + + switch (method) { + case 'natural': + sorter = naturalSorter; + break; + case 'standard': + sorter = standardSorter; + } + + return selectors.sort(sorter); +} + +module.exports = sortSelectors; diff --git a/node_modules/clean-css/lib/optimizer/level-1/tidy-at-rule.js b/node_modules/clean-css/lib/optimizer/level-1/tidy-at-rule.js new file mode 100644 index 000000000..a7b149fb5 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/tidy-at-rule.js @@ -0,0 +1,9 @@ +function tidyAtRule(value) { + return value + .replace(/\s+/g, ' ') + .replace(/url\(\s+/g, 'url(') + .replace(/\s+\)/g, ')') + .trim(); +} + +module.exports = tidyAtRule; diff --git a/node_modules/clean-css/lib/optimizer/level-1/tidy-block.js b/node_modules/clean-css/lib/optimizer/level-1/tidy-block.js new file mode 100644 index 000000000..8322aeca7 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/tidy-block.js @@ -0,0 +1,23 @@ +var SUPPORTED_COMPACT_BLOCK_MATCHER = /^@media\W/; + +function tidyBlock(values, spaceAfterClosingBrace) { + var withoutSpaceAfterClosingBrace; + var i; + + for (i = values.length - 1; i >= 0; i--) { + withoutSpaceAfterClosingBrace = !spaceAfterClosingBrace && SUPPORTED_COMPACT_BLOCK_MATCHER.test(values[i][1]); + + values[i][1] = values[i][1] + .replace(/\n|\r\n/g, ' ') + .replace(/\s+/g, ' ') + .replace(/(,|:|\() /g, '$1') + .replace(/ \)/g, ')') + .replace(/'([a-zA-Z][a-zA-Z\d\-_]+)'/, '$1') + .replace(/"([a-zA-Z][a-zA-Z\d\-_]+)"/, '$1') + .replace(withoutSpaceAfterClosingBrace ? /\) /g : null, ')'); + } + + return values; +} + +module.exports = tidyBlock; diff --git a/node_modules/clean-css/lib/optimizer/level-1/tidy-rules.js b/node_modules/clean-css/lib/optimizer/level-1/tidy-rules.js new file mode 100644 index 000000000..b47ed6b79 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/tidy-rules.js @@ -0,0 +1,213 @@ +var Spaces = require('../../options/format').Spaces; +var Marker = require('../../tokenizer/marker'); +var formatPosition = require('../../utils/format-position'); + +var CASE_ATTRIBUTE_PATTERN = /[\s"'][iI]\s*\]/; +var CASE_RESTORE_PATTERN = /([\d\w])([iI])\]/g; +var DOUBLE_QUOTE_CASE_PATTERN = /="([a-zA-Z][a-zA-Z\d\-_]+)"([iI])/g; +var DOUBLE_QUOTE_PATTERN = /="([a-zA-Z][a-zA-Z\d\-_]+)"(\s|\])/g; +var HTML_COMMENT_PATTERN = /^(?:(?:<!--|-->)\s*)+/; +var SINGLE_QUOTE_CASE_PATTERN = /='([a-zA-Z][a-zA-Z\d\-_]+)'([iI])/g; +var SINGLE_QUOTE_PATTERN = /='([a-zA-Z][a-zA-Z\d\-_]+)'(\s|\])/g; +var RELATION_PATTERN = /[>\+~]/; +var WHITESPACE_PATTERN = /\s/; + +var ASTERISK_PLUS_HTML_HACK = '*+html '; +var ASTERISK_FIRST_CHILD_PLUS_HTML_HACK = '*:first-child+html '; +var LESS_THAN = '<'; + +function hasInvalidCharacters(value) { + var isEscaped; + var isInvalid = false; + var character; + var isQuote = false; + var i, l; + + for (i = 0, l = value.length; i < l; i++) { + character = value[i]; + + if (isEscaped) { + // continue as always + } else if (character == Marker.SINGLE_QUOTE || character == Marker.DOUBLE_QUOTE) { + isQuote = !isQuote; + } else if (!isQuote && (character == Marker.CLOSE_CURLY_BRACKET || character == Marker.EXCLAMATION || character == LESS_THAN || character == Marker.SEMICOLON)) { + isInvalid = true; + break; + } else if (!isQuote && i === 0 && RELATION_PATTERN.test(character)) { + isInvalid = true; + break; + } + + isEscaped = character == Marker.BACK_SLASH; + } + + return isInvalid; +} + +function removeWhitespace(value, format) { + var stripped = []; + var character; + var isNewLineNix; + var isNewLineWin; + var isEscaped; + var wasEscaped; + var isQuoted; + var isSingleQuoted; + var isDoubleQuoted; + var isAttribute; + var isRelation; + var isWhitespace; + var roundBracketLevel = 0; + var wasRelation = false; + var wasWhitespace = false; + var withCaseAttribute = CASE_ATTRIBUTE_PATTERN.test(value); + var spaceAroundRelation = format && format.spaces[Spaces.AroundSelectorRelation]; + var i, l; + + for (i = 0, l = value.length; i < l; i++) { + character = value[i]; + + isNewLineNix = character == Marker.NEW_LINE_NIX; + isNewLineWin = character == Marker.NEW_LINE_NIX && value[i - 1] == Marker.NEW_LINE_WIN; + isQuoted = isSingleQuoted || isDoubleQuoted; + isRelation = !isEscaped && roundBracketLevel === 0 && RELATION_PATTERN.test(character); + isWhitespace = WHITESPACE_PATTERN.test(character); + + if (wasEscaped && isQuoted && isNewLineWin) { + // swallow escaped new windows lines in comments + stripped.pop(); + stripped.pop(); + } else if (isEscaped && isQuoted && isNewLineNix) { + // swallow escaped new *nix lines in comments + stripped.pop(); + } else if (isEscaped) { + stripped.push(character); + } else if (character == Marker.OPEN_SQUARE_BRACKET && !isQuoted) { + stripped.push(character); + isAttribute = true; + } else if (character == Marker.CLOSE_SQUARE_BRACKET && !isQuoted) { + stripped.push(character); + isAttribute = false; + } else if (character == Marker.OPEN_ROUND_BRACKET && !isQuoted) { + stripped.push(character); + roundBracketLevel++; + } else if (character == Marker.CLOSE_ROUND_BRACKET && !isQuoted) { + stripped.push(character); + roundBracketLevel--; + } else if (character == Marker.SINGLE_QUOTE && !isQuoted) { + stripped.push(character); + isSingleQuoted = true; + } else if (character == Marker.DOUBLE_QUOTE && !isQuoted) { + stripped.push(character); + isDoubleQuoted = true; + } else if (character == Marker.SINGLE_QUOTE && isQuoted) { + stripped.push(character); + isSingleQuoted = false; + } else if (character == Marker.DOUBLE_QUOTE && isQuoted) { + stripped.push(character); + isDoubleQuoted = false; + } else if (isWhitespace && wasRelation && !spaceAroundRelation) { + continue; + } else if (!isWhitespace && wasRelation && spaceAroundRelation) { + stripped.push(Marker.SPACE); + stripped.push(character); + } else if (isWhitespace && (isAttribute || roundBracketLevel > 0) && !isQuoted) { + // skip space + } else if (isWhitespace && wasWhitespace && !isQuoted) { + // skip extra space + } else if ((isNewLineWin || isNewLineNix) && (isAttribute || roundBracketLevel > 0) && isQuoted) { + // skip newline + } else if (isRelation && wasWhitespace && !spaceAroundRelation) { + stripped.pop(); + stripped.push(character); + } else if (isRelation && !wasWhitespace && spaceAroundRelation) { + stripped.push(Marker.SPACE); + stripped.push(character); + } else if (isWhitespace) { + stripped.push(Marker.SPACE); + } else { + stripped.push(character); + } + + wasEscaped = isEscaped; + isEscaped = character == Marker.BACK_SLASH; + wasRelation = isRelation; + wasWhitespace = isWhitespace; + } + + return withCaseAttribute ? + stripped.join('').replace(CASE_RESTORE_PATTERN, '$1 $2]') : + stripped.join(''); +} + +function removeQuotes(value) { + if (value.indexOf('\'') == -1 && value.indexOf('"') == -1) { + return value; + } + + return value + .replace(SINGLE_QUOTE_CASE_PATTERN, '=$1 $2') + .replace(SINGLE_QUOTE_PATTERN, '=$1$2') + .replace(DOUBLE_QUOTE_CASE_PATTERN, '=$1 $2') + .replace(DOUBLE_QUOTE_PATTERN, '=$1$2'); +} + +function tidyRules(rules, removeUnsupported, adjacentSpace, format, warnings) { + var list = []; + var repeated = []; + + function removeHTMLComment(rule, match) { + warnings.push('HTML comment \'' + match + '\' at ' + formatPosition(rule[2][0]) + '. Removing.'); + return ''; + } + + for (var i = 0, l = rules.length; i < l; i++) { + var rule = rules[i]; + var reduced = rule[1]; + + reduced = reduced.replace(HTML_COMMENT_PATTERN, removeHTMLComment.bind(null, rule)); + + if (hasInvalidCharacters(reduced)) { + warnings.push('Invalid selector \'' + rule[1] + '\' at ' + formatPosition(rule[2][0]) + '. Ignoring.'); + continue; + } + + reduced = removeWhitespace(reduced, format); + reduced = removeQuotes(reduced); + + if (adjacentSpace && reduced.indexOf('nav') > 0) { + reduced = reduced.replace(/\+nav(\S|$)/, '+ nav$1'); + } + + if (removeUnsupported && reduced.indexOf(ASTERISK_PLUS_HTML_HACK) > -1) { + continue; + } + + if (removeUnsupported && reduced.indexOf(ASTERISK_FIRST_CHILD_PLUS_HTML_HACK) > -1) { + continue; + } + + if (reduced.indexOf('*') > -1) { + reduced = reduced + .replace(/\*([:#\.\[])/g, '$1') + .replace(/^(\:first\-child)?\+html/, '*$1+html'); + } + + if (repeated.indexOf(reduced) > -1) { + continue; + } + + rule[1] = reduced; + repeated.push(reduced); + list.push(rule); + } + + if (list.length == 1 && list[0][1].length === 0) { + warnings.push('Empty selector \'' + list[0][1] + '\' at ' + formatPosition(list[0][2][0]) + '. Ignoring.'); + list = []; + } + + return list; +} + +module.exports = tidyRules; diff --git a/node_modules/clean-css/lib/optimizer/level-2/break-up.js b/node_modules/clean-css/lib/optimizer/level-2/break-up.js new file mode 100644 index 000000000..9e44d5a1f --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/break-up.js @@ -0,0 +1,362 @@ +var InvalidPropertyError = require('./invalid-property-error'); + +var wrapSingle = require('../wrap-for-optimizing').single; + +var Token = require('../../tokenizer/token'); + +var formatPosition = require('../../utils/format-position'); + +var MULTIPLEX_SEPARATOR = ','; + +function _colorFilter(validator) { + return function (value) { + return value[1] == 'invert' || validator.isValidColor(value[1]) || validator.isValidVendorPrefixedValue(value[1]); + }; +} + +function _styleFilter(validator) { + return function (value) { + return value[1] != 'inherit' && validator.isValidStyle(value[1]) && !validator.isValidColorValue(value[1]); + }; +} + +function _wrapDefault(name, property, compactable) { + var descriptor = compactable[name]; + if (descriptor.doubleValues && descriptor.defaultValue.length == 2) { + return wrapSingle([ + Token.PROPERTY, + [Token.PROPERTY_NAME, name], + [Token.PROPERTY_VALUE, descriptor.defaultValue[0]], + [Token.PROPERTY_VALUE, descriptor.defaultValue[1]] + ]); + } else if (descriptor.doubleValues && descriptor.defaultValue.length == 1) { + return wrapSingle([ + Token.PROPERTY, + [Token.PROPERTY_NAME, name], + [Token.PROPERTY_VALUE, descriptor.defaultValue[0]] + ]); + } else { + return wrapSingle([ + Token.PROPERTY, + [Token.PROPERTY_NAME, name], + [Token.PROPERTY_VALUE, descriptor.defaultValue] + ]); + } +} + +function _widthFilter(validator) { + return function (value) { + return value[1] != 'inherit' && validator.isValidWidth(value[1]) && !validator.isValidStyle(value[1]) && !validator.isValidColorValue(value[1]); + }; +} + +function background(property, compactable, validator) { + var image = _wrapDefault('background-image', property, compactable); + var position = _wrapDefault('background-position', property, compactable); + var size = _wrapDefault('background-size', property, compactable); + var repeat = _wrapDefault('background-repeat', property, compactable); + var attachment = _wrapDefault('background-attachment', property, compactable); + var origin = _wrapDefault('background-origin', property, compactable); + var clip = _wrapDefault('background-clip', property, compactable); + var color = _wrapDefault('background-color', property, compactable); + var components = [image, position, size, repeat, attachment, origin, clip, color]; + var values = property.value; + + var positionSet = false; + var clipSet = false; + var originSet = false; + var repeatSet = false; + + var anyValueSet = false; + + if (property.value.length == 1 && property.value[0][1] == 'inherit') { + // NOTE: 'inherit' is not a valid value for background-attachment + color.value = image.value = repeat.value = position.value = size.value = origin.value = clip.value = property.value; + return components; + } + + if (property.value.length == 1 && property.value[0][1] == '0 0') { + return components; + } + + for (var i = values.length - 1; i >= 0; i--) { + var value = values[i]; + + if (validator.isValidBackgroundAttachment(value[1])) { + attachment.value = [value]; + anyValueSet = true; + } else if (validator.isValidBackgroundClip(value[1]) || validator.isValidBackgroundOrigin(value[1])) { + if (clipSet) { + origin.value = [value]; + originSet = true; + } else { + clip.value = [value]; + clipSet = true; + } + anyValueSet = true; + } else if (validator.isValidBackgroundRepeat(value[1])) { + if (repeatSet) { + repeat.value.unshift(value); + } else { + repeat.value = [value]; + repeatSet = true; + } + anyValueSet = true; + } else if (validator.isValidBackgroundPositionPart(value[1]) || validator.isValidBackgroundSizePart(value[1])) { + if (i > 0) { + var previousValue = values[i - 1]; + + if (previousValue[1] == '/') { + size.value = [value]; + } else if (i > 1 && values[i - 2][1] == '/') { + size.value = [previousValue, value]; + i -= 2; + } else { + if (!positionSet) + position.value = []; + + position.value.unshift(value); + positionSet = true; + } + } else { + if (!positionSet) + position.value = []; + + position.value.unshift(value); + positionSet = true; + } + anyValueSet = true; + } else if ((color.value[0][1] == compactable[color.name].defaultValue || color.value[0][1] == 'none') && (validator.isValidColor(value[1]) || validator.isValidVendorPrefixedValue(value[1]))) { + color.value = [value]; + anyValueSet = true; + } else if (validator.isValidUrl(value[1]) || validator.isValidFunction(value[1])) { + image.value = [value]; + anyValueSet = true; + } + } + + if (clipSet && !originSet) + origin.value = clip.value.slice(0); + + if (!anyValueSet) { + throw new InvalidPropertyError('Invalid background value at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); + } + + return components; +} + +function borderRadius(property, compactable) { + var values = property.value; + var splitAt = -1; + + for (var i = 0, l = values.length; i < l; i++) { + if (values[i][1] == '/') { + splitAt = i; + break; + } + } + + if (splitAt === 0 || splitAt === values.length - 1) { + throw new InvalidPropertyError('Invalid border-radius value at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); + } + + var target = _wrapDefault(property.name, property, compactable); + target.value = splitAt > -1 ? + values.slice(0, splitAt) : + values.slice(0); + target.components = fourValues(target, compactable); + + var remainder = _wrapDefault(property.name, property, compactable); + remainder.value = splitAt > -1 ? + values.slice(splitAt + 1) : + values.slice(0); + remainder.components = fourValues(remainder, compactable); + + for (var j = 0; j < 4; j++) { + target.components[j].multiplex = true; + target.components[j].value = target.components[j].value.concat(remainder.components[j].value); + } + + return target.components; +} + +function fourValues(property, compactable) { + var componentNames = compactable[property.name].components; + var components = []; + var value = property.value; + + if (value.length < 1) + return []; + + if (value.length < 2) + value[1] = value[0].slice(0); + if (value.length < 3) + value[2] = value[0].slice(0); + if (value.length < 4) + value[3] = value[1].slice(0); + + for (var i = componentNames.length - 1; i >= 0; i--) { + var component = wrapSingle([ + Token.PROPERTY, + [Token.PROPERTY_NAME, componentNames[i]] + ]); + component.value = [value[i]]; + components.unshift(component); + } + + return components; +} + +function multiplex(splitWith) { + return function (property, compactable, validator) { + var splitsAt = []; + var values = property.value; + var i, j, l, m; + + // find split commas + for (i = 0, l = values.length; i < l; i++) { + if (values[i][1] == ',') + splitsAt.push(i); + } + + if (splitsAt.length === 0) + return splitWith(property, compactable, validator); + + var splitComponents = []; + + // split over commas, and into components + for (i = 0, l = splitsAt.length; i <= l; i++) { + var from = i === 0 ? 0 : splitsAt[i - 1] + 1; + var to = i < l ? splitsAt[i] : values.length; + + var _property = _wrapDefault(property.name, property, compactable); + _property.value = values.slice(from, to); + + splitComponents.push(splitWith(_property, compactable, validator)); + } + + var components = splitComponents[0]; + + // group component values from each split + for (i = 0, l = components.length; i < l; i++) { + components[i].multiplex = true; + + for (j = 1, m = splitComponents.length; j < m; j++) { + components[i].value.push([Token.PROPERTY_VALUE, MULTIPLEX_SEPARATOR]); + Array.prototype.push.apply(components[i].value, splitComponents[j][i].value); + } + } + + return components; + }; +} + +function listStyle(property, compactable, validator) { + var type = _wrapDefault('list-style-type', property, compactable); + var position = _wrapDefault('list-style-position', property, compactable); + var image = _wrapDefault('list-style-image', property, compactable); + var components = [type, position, image]; + + if (property.value.length == 1 && property.value[0][1] == 'inherit') { + type.value = position.value = image.value = [property.value[0]]; + return components; + } + + var values = property.value.slice(0); + var total = values.length; + var index = 0; + + // `image` first... + for (index = 0, total = values.length; index < total; index++) { + if (validator.isValidUrl(values[index][1]) || values[index][1] == '0') { + image.value = [values[index]]; + values.splice(index, 1); + break; + } + } + + // ... then `type`... + for (index = 0, total = values.length; index < total; index++) { + if (validator.isValidListStyleType(values[index][1])) { + type.value = [values[index]]; + values.splice(index, 1); + break; + } + } + + // ... and what's left is a `position` + if (values.length > 0 && validator.isValidListStylePosition(values[0][1])) + position.value = [values[0]]; + + return components; +} + +function widthStyleColor(property, compactable, validator) { + var descriptor = compactable[property.name]; + var components = [ + _wrapDefault(descriptor.components[0], property, compactable), + _wrapDefault(descriptor.components[1], property, compactable), + _wrapDefault(descriptor.components[2], property, compactable) + ]; + var color, style, width; + + for (var i = 0; i < 3; i++) { + var component = components[i]; + + if (component.name.indexOf('color') > 0) + color = component; + else if (component.name.indexOf('style') > 0) + style = component; + else + width = component; + } + + if ((property.value.length == 1 && property.value[0][1] == 'inherit') || + (property.value.length == 3 && property.value[0][1] == 'inherit' && property.value[1][1] == 'inherit' && property.value[2][1] == 'inherit')) { + color.value = style.value = width.value = [property.value[0]]; + return components; + } + + var values = property.value.slice(0); + var match, matches; + + // NOTE: usually users don't follow the required order of parts in this shorthand, + // so we'll try to parse it caring as little about order as possible + + if (values.length > 0) { + matches = values.filter(_widthFilter(validator)); + match = matches.length > 1 && (matches[0][1] == 'none' || matches[0][1] == 'auto') ? matches[1] : matches[0]; + if (match) { + width.value = [match]; + values.splice(values.indexOf(match), 1); + } + } + + if (values.length > 0) { + match = values.filter(_styleFilter(validator))[0]; + if (match) { + style.value = [match]; + values.splice(values.indexOf(match), 1); + } + } + + if (values.length > 0) { + match = values.filter(_colorFilter(validator))[0]; + if (match) { + color.value = [match]; + values.splice(values.indexOf(match), 1); + } + } + + return components; +} + +module.exports = { + background: background, + border: widthStyleColor, + borderRadius: borderRadius, + fourValues: fourValues, + listStyle: listStyle, + multiplex: multiplex, + outline: widthStyleColor +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/can-override.js b/node_modules/clean-css/lib/optimizer/level-2/can-override.js new file mode 100644 index 000000000..2617234ca --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/can-override.js @@ -0,0 +1,180 @@ +var understandable = require('./properties/understandable'); + +function backgroundPosition(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue('background-position', value2, true)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } else if (validator.isValidKeywordValue('background-position', value2, true)) { + return true; + } + + return unit(validator, value1, value2); +} + +function backgroundSize(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue('background-size', value2, true)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } else if (validator.isValidKeywordValue('background-size', value2, true)) { + return true; + } + + return unit(validator, value1, value2); +} + +function color(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidColor(value2)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } else if (!validator.colorOpacity && (validator.isValidRgbaColor(value1) || validator.isValidHslaColor(value1))) { + return false; + } else if (!validator.colorOpacity && (validator.isValidRgbaColor(value2) || validator.isValidHslaColor(value2))) { + return false; + } else if (validator.isValidColor(value1) && validator.isValidColor(value2)) { + return true; + } + + return sameFunctionOrValue(validator, value1, value2); +} + +function components(overrideCheckers) { + return function (validator, value1, value2, position) { + return overrideCheckers[position](validator, value1, value2); + }; +} + +function image(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidImage(value2)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } else if (validator.isValidImage(value2)) { + return true; + } else if (validator.isValidImage(value1)) { + return false; + } + + return sameFunctionOrValue(validator, value1, value2); +} + +function keyword(propertyName) { + return function(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue(propertyName, value2)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } + + return validator.isValidKeywordValue(propertyName, value2, false); + }; +} + +function keywordWithGlobal(propertyName) { + return function(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue(propertyName, value2, true)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } + + return validator.isValidKeywordValue(propertyName, value2, true); + }; +} + +function sameFunctionOrValue(validator, value1, value2) { + return validator.areSameFunction(value1, value2) ? + true : + value1 === value2; +} + +function textShadow(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidTextShadow(value2)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } + + return validator.isValidTextShadow(value2); +} + +function unit(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidUnitWithoutFunction(value2)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } else if (validator.isValidUnitWithoutFunction(value1) && !validator.isValidUnitWithoutFunction(value2)) { + return false; + } else if (validator.isValidUnitWithoutFunction(value2)) { + return true; + } else if (validator.isValidUnitWithoutFunction(value1)) { + return false; + } else if (validator.isValidFunctionWithoutVendorPrefix(value1) && validator.isValidFunctionWithoutVendorPrefix(value2)) { + return true; + } + + return sameFunctionOrValue(validator, value1, value2); +} + +function unitOrKeywordWithGlobal(propertyName) { + var byKeyword = keywordWithGlobal(propertyName); + + return function(validator, value1, value2) { + return unit(validator, value1, value2) || byKeyword(validator, value1, value2); + }; +} + +function zIndex(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidZIndex(value2)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } + + return validator.isValidZIndex(value2); +} + +module.exports = { + generic: { + color: color, + components: components, + image: image, + unit: unit + }, + property: { + backgroundAttachment: keyword('background-attachment'), + backgroundClip: keywordWithGlobal('background-clip'), + backgroundOrigin: keyword('background-origin'), + backgroundPosition: backgroundPosition, + backgroundRepeat: keyword('background-repeat'), + backgroundSize: backgroundSize, + bottom: unitOrKeywordWithGlobal('bottom'), + borderCollapse: keyword('border-collapse'), + borderStyle: keywordWithGlobal('*-style'), + clear: keywordWithGlobal('clear'), + cursor: keywordWithGlobal('cursor'), + display: keywordWithGlobal('display'), + float: keywordWithGlobal('float'), + fontStyle: keywordWithGlobal('font-style'), + left: unitOrKeywordWithGlobal('left'), + fontWeight: keywordWithGlobal('font-weight'), + listStyleType: keywordWithGlobal('list-style-type'), + listStylePosition: keywordWithGlobal('list-style-position'), + outlineStyle: keywordWithGlobal('*-style'), + overflow: keywordWithGlobal('overflow'), + position: keywordWithGlobal('position'), + right: unitOrKeywordWithGlobal('right'), + textAlign: keywordWithGlobal('text-align'), + textDecoration: keywordWithGlobal('text-decoration'), + textOverflow: keywordWithGlobal('text-overflow'), + textShadow: textShadow, + top: unitOrKeywordWithGlobal('top'), + transform: sameFunctionOrValue, + verticalAlign: unitOrKeywordWithGlobal('vertical-align'), + visibility: keywordWithGlobal('visibility'), + whiteSpace: keywordWithGlobal('white-space'), + zIndex: zIndex + } +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/clone.js b/node_modules/clean-css/lib/optimizer/level-2/clone.js new file mode 100644 index 000000000..3830095e9 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/clone.js @@ -0,0 +1,33 @@ +var wrapSingle = require('../wrap-for-optimizing').single; + +var Token = require('../../tokenizer/token'); + +function deep(property) { + var cloned = shallow(property); + for (var i = property.components.length - 1; i >= 0; i--) { + var component = shallow(property.components[i]); + component.value = property.components[i].value.slice(0); + cloned.components.unshift(component); + } + + cloned.dirty = true; + cloned.value = property.value.slice(0); + + return cloned; +} + +function shallow(property) { + var cloned = wrapSingle([ + Token.PROPERTY, + [Token.PROPERTY_NAME, property.name] + ]); + cloned.important = property.important; + cloned.hack = property.hack; + cloned.unused = false; + return cloned; +} + +module.exports = { + deep: deep, + shallow: shallow +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/compactable.js b/node_modules/clean-css/lib/optimizer/level-2/compactable.js new file mode 100644 index 000000000..9cf334d44 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/compactable.js @@ -0,0 +1,787 @@ +// Contains the interpretation of CSS properties, as used by the property optimizer + +var breakUp = require('./break-up'); +var canOverride = require('./can-override'); +var restore = require('./restore'); + +var override = require('../../utils/override'); + +// Properties to process +// Extend this object in order to add support for more properties in the optimizer. +// +// Each key in this object represents a CSS property and should be an object. +// Such an object contains properties that describe how the represented CSS property should be handled. +// Possible options: +// +// * components: array (Only specify for shorthand properties.) +// Contains the names of the granular properties this shorthand compacts. +// +// * canOverride: function +// Returns whether two tokens of this property can be merged with each other. +// This property has no meaning for shorthands. +// +// * defaultValue: string +// Specifies the default value of the property according to the CSS standard. +// For shorthand, this is used when every component is set to its default value, therefore it should be the shortest possible default value of all the components. +// +// * shortestValue: string +// Specifies the shortest possible value the property can possibly have. +// (Falls back to defaultValue if unspecified.) +// +// * breakUp: function (Only specify for shorthand properties.) +// Breaks the shorthand up to its components. +// +// * restore: function (Only specify for shorthand properties.) +// Puts the shorthand together from its components. +// +var compactable = { + 'background': { + canOverride: canOverride.generic.components([ + canOverride.generic.image, + canOverride.property.backgroundPosition, + canOverride.property.backgroundSize, + canOverride.property.backgroundRepeat, + canOverride.property.backgroundAttachment, + canOverride.property.backgroundOrigin, + canOverride.property.backgroundClip, + canOverride.generic.color + ]), + components: [ + 'background-image', + 'background-position', + 'background-size', + 'background-repeat', + 'background-attachment', + 'background-origin', + 'background-clip', + 'background-color' + ], + breakUp: breakUp.multiplex(breakUp.background), + defaultValue: '0 0', + restore: restore.multiplex(restore.background), + shortestValue: '0', + shorthand: true + }, + 'background-attachment': { + canOverride: canOverride.property.backgroundAttachment, + componentOf: [ + 'background' + ], + defaultValue: 'scroll' + }, + 'background-clip': { + canOverride: canOverride.property.backgroundClip, + componentOf: [ + 'background' + ], + defaultValue: 'border-box', + shortestValue: 'border-box' + }, + 'background-color': { + canOverride: canOverride.generic.color, + componentOf: [ + 'background' + ], + defaultValue: 'transparent', + multiplexLastOnly: true, + nonMergeableValue: 'none', + shortestValue: 'red' + }, + 'background-image': { + canOverride: canOverride.generic.image, + componentOf: [ + 'background' + ], + defaultValue: 'none' + }, + 'background-origin': { + canOverride: canOverride.property.backgroundOrigin, + componentOf: [ + 'background' + ], + defaultValue: 'padding-box', + shortestValue: 'border-box' + }, + 'background-position': { + canOverride: canOverride.property.backgroundPosition, + componentOf: [ + 'background' + ], + defaultValue: ['0', '0'], + doubleValues: true, + shortestValue: '0' + }, + 'background-repeat': { + canOverride: canOverride.property.backgroundRepeat, + componentOf: [ + 'background' + ], + defaultValue: ['repeat'], + doubleValues: true + }, + 'background-size': { + canOverride: canOverride.property.backgroundSize, + componentOf: [ + 'background' + ], + defaultValue: ['auto'], + doubleValues: true, + shortestValue: '0 0' + }, + 'bottom': { + canOverride: canOverride.property.bottom, + defaultValue: 'auto' + }, + 'border': { + breakUp: breakUp.border, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.property.borderStyle, + canOverride.generic.color + ]), + components: [ + 'border-width', + 'border-style', + 'border-color' + ], + defaultValue: 'none', + overridesShorthands: [ + 'border-bottom', + 'border-left', + 'border-right', + 'border-top' + ], + restore: restore.withoutDefaults, + shorthand: true, + shorthandComponents: true + }, + 'border-bottom': { + breakUp: breakUp.border, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.property.borderStyle, + canOverride.generic.color + ]), + components: [ + 'border-bottom-width', + 'border-bottom-style', + 'border-bottom-color' + ], + defaultValue: 'none', + restore: restore.withoutDefaults, + shorthand: true + }, + 'border-bottom-color': { + canOverride: canOverride.generic.color, + componentOf: [ + 'border-bottom', + 'border-color' + ], + defaultValue: 'none' + }, + 'border-bottom-left-radius': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-radius' + ], + defaultValue: '0', + vendorPrefixes: [ + '-moz-', + '-o-' + ] + }, + 'border-bottom-right-radius': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-radius' + ], + defaultValue: '0', + vendorPrefixes: [ + '-moz-', + '-o-' + ] + }, + 'border-bottom-style': { + canOverride: canOverride.property.borderStyle, + componentOf: [ + 'border-bottom', + 'border-style' + ], + defaultValue: 'none' + }, + 'border-bottom-width': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-bottom', + 'border-width' + ], + defaultValue: 'medium', + shortestValue: '0' + }, + 'border-collapse': { + canOverride: canOverride.property.borderCollapse, + defaultValue: 'separate' + }, + 'border-color': { + breakUp: breakUp.fourValues, + canOverride: canOverride.generic.components([ + canOverride.generic.color, + canOverride.generic.color, + canOverride.generic.color, + canOverride.generic.color + ]), + componentOf: ['border'], + components: [ + 'border-top-color', + 'border-right-color', + 'border-bottom-color', + 'border-left-color' + ], + defaultValue: 'none', + restore: restore.fourValues, + shortestValue: 'red', + shorthand: true + }, + 'border-left': { + breakUp: breakUp.border, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.property.borderStyle, + canOverride.generic.color + ]), + components: [ + 'border-left-width', + 'border-left-style', + 'border-left-color' + ], + defaultValue: 'none', + restore: restore.withoutDefaults, + shorthand: true + }, + 'border-left-color': { + canOverride: canOverride.generic.color, + componentOf: [ + 'border-color', + 'border-left' + ], + defaultValue: 'none' + }, + 'border-left-style': { + canOverride: canOverride.property.borderStyle, + componentOf: [ + 'border-left', + 'border-style' + ], + defaultValue: 'none' + }, + 'border-left-width': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-left', + 'border-width' + ], + defaultValue: 'medium', + shortestValue: '0' + }, + 'border-radius': { + breakUp: breakUp.borderRadius, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit + ]), + components: [ + 'border-top-left-radius', + 'border-top-right-radius', + 'border-bottom-right-radius', + 'border-bottom-left-radius' + ], + defaultValue: '0', + restore: restore.borderRadius, + shorthand: true, + vendorPrefixes: [ + '-moz-', + '-o-' + ] + }, + 'border-right': { + breakUp: breakUp.border, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.property.borderStyle, + canOverride.generic.color + ]), + components: [ + 'border-right-width', + 'border-right-style', + 'border-right-color' + ], + defaultValue: 'none', + restore: restore.withoutDefaults, + shorthand: true + }, + 'border-right-color': { + canOverride: canOverride.generic.color, + componentOf: [ + 'border-color', + 'border-right' + ], + defaultValue: 'none' + }, + 'border-right-style': { + canOverride: canOverride.property.borderStyle, + componentOf: [ + 'border-right', + 'border-style' + ], + defaultValue: 'none' + }, + 'border-right-width': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-right', + 'border-width' + ], + defaultValue: 'medium', + shortestValue: '0' + }, + 'border-style': { + breakUp: breakUp.fourValues, + canOverride: canOverride.generic.components([ + canOverride.property.borderStyle, + canOverride.property.borderStyle, + canOverride.property.borderStyle, + canOverride.property.borderStyle + ]), + componentOf: [ + 'border' + ], + components: [ + 'border-top-style', + 'border-right-style', + 'border-bottom-style', + 'border-left-style' + ], + defaultValue: 'none', + restore: restore.fourValues, + shorthand: true + }, + 'border-top': { + breakUp: breakUp.border, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.property.borderStyle, + canOverride.generic.color + ]), + components: [ + 'border-top-width', + 'border-top-style', + 'border-top-color' + ], + defaultValue: 'none', + restore: restore.withoutDefaults, + shorthand: true + }, + 'border-top-color': { + canOverride: canOverride.generic.color, + componentOf: [ + 'border-color', + 'border-top' + ], + defaultValue: 'none' + }, + 'border-top-left-radius': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-radius' + ], + defaultValue: '0', + vendorPrefixes: [ + '-moz-', + '-o-' + ] + }, + 'border-top-right-radius': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-radius' + ], + defaultValue: '0', + vendorPrefixes: [ + '-moz-', + '-o-' + ] + }, + 'border-top-style': { + canOverride: canOverride.property.borderStyle, + componentOf: [ + 'border-style', + 'border-top' + ], + defaultValue: 'none' + }, + 'border-top-width': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-top', + 'border-width' + ], + defaultValue: 'medium', + shortestValue: '0' + }, + 'border-width': { + breakUp: breakUp.fourValues, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit + ]), + components: [ + 'border-top-width', + 'border-right-width', + 'border-bottom-width', + 'border-left-width' + ], + defaultValue: 'medium', + restore: restore.fourValues, + shortestValue: '0', + shorthand: true + }, + 'clear': { + canOverride: canOverride.property.clear, + defaultValue: 'none' + }, + 'color': { + canOverride: canOverride.generic.color, + defaultValue: 'transparent', + shortestValue: 'red' + }, + 'cursor': { + canOverride: canOverride.property.cursor, + defaultValue: 'auto' + }, + 'display': { + canOverride: canOverride.property.display, + }, + 'float': { + canOverride: canOverride.property.float, + defaultValue: 'none' + }, + 'font-size': { + canOverride: canOverride.generic.unit, + defaultValue: 'medium', + shortestValue: '0' + }, + 'font-style': { + canOverride: canOverride.property.fontStyle, + defaultValue: 'normal' + }, + 'font-weight': { + canOverride: canOverride.property.fontWeight, + defaultValue: '400', + shortestValue: '400' + }, + 'height': { + canOverride: canOverride.generic.unit, + defaultValue: 'auto', + shortestValue: '0' + }, + 'left': { + canOverride: canOverride.property.left, + defaultValue: 'auto' + }, + 'line-height': { + canOverride: canOverride.generic.unit, + defaultValue: 'normal', + shortestValue: '0' + }, + 'list-style': { + canOverride: canOverride.generic.components([ + canOverride.property.listStyleType, + canOverride.property.listStylePosition, + canOverride.property.listStyleImage + ]), + components: [ + 'list-style-type', + 'list-style-position', + 'list-style-image' + ], + breakUp: breakUp.listStyle, + restore: restore.withoutDefaults, + defaultValue: 'outside', // can't use 'disc' because that'd override default 'decimal' for <ol> + shortestValue: 'none', + shorthand: true + }, + 'list-style-image' : { + canOverride: canOverride.generic.image, + componentOf: [ + 'list-style' + ], + defaultValue: 'none' + }, + 'list-style-position' : { + canOverride: canOverride.property.listStylePosition, + componentOf: [ + 'list-style' + ], + defaultValue: 'outside', + shortestValue: 'inside' + }, + 'list-style-type' : { + canOverride: canOverride.property.listStyleType, + componentOf: [ + 'list-style' + ], + // NOTE: we can't tell the real default value here, it's 'disc' for <ul> and 'decimal' for <ol> + // this is a hack, but it doesn't matter because this value will be either overridden or + // it will disappear at the final step anyway + defaultValue: 'decimal|disc', + shortestValue: 'none' + }, + 'margin': { + breakUp: breakUp.fourValues, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit + ]), + components: [ + 'margin-top', + 'margin-right', + 'margin-bottom', + 'margin-left' + ], + defaultValue: '0', + restore: restore.fourValues, + shorthand: true + }, + 'margin-bottom': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'margin' + ], + defaultValue: '0' + }, + 'margin-left': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'margin' + ], + defaultValue: '0' + }, + 'margin-right': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'margin' + ], + defaultValue: '0' + }, + 'margin-top': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'margin' + ], + defaultValue: '0' + }, + 'outline': { + canOverride: canOverride.generic.components([ + canOverride.generic.color, + canOverride.property.outlineStyle, + canOverride.generic.unit + ]), + components: [ + 'outline-color', + 'outline-style', + 'outline-width' + ], + breakUp: breakUp.outline, + restore: restore.withoutDefaults, + defaultValue: '0', + shorthand: true + }, + 'outline-color': { + canOverride: canOverride.generic.color, + componentOf: [ + 'outline' + ], + defaultValue: 'invert', + shortestValue: 'red' + }, + 'outline-style': { + canOverride: canOverride.property.outlineStyle, + componentOf: [ + 'outline' + ], + defaultValue: 'none' + }, + 'outline-width': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'outline' + ], + defaultValue: 'medium', + shortestValue: '0' + }, + 'overflow': { + canOverride: canOverride.property.overflow, + defaultValue: 'visible' + }, + 'overflow-x': { + canOverride: canOverride.property.overflow, + defaultValue: 'visible' + }, + 'overflow-y': { + canOverride: canOverride.property.overflow, + defaultValue: 'visible' + }, + 'padding': { + breakUp: breakUp.fourValues, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit + ]), + components: [ + 'padding-top', + 'padding-right', + 'padding-bottom', + 'padding-left' + ], + defaultValue: '0', + restore: restore.fourValues, + shorthand: true + }, + 'padding-bottom': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'padding' + ], + defaultValue: '0' + }, + 'padding-left': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'padding' + ], + defaultValue: '0' + }, + 'padding-right': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'padding' + ], + defaultValue: '0' + }, + 'padding-top': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'padding' + ], + defaultValue: '0' + }, + 'position': { + canOverride: canOverride.property.position, + defaultValue: 'static' + }, + 'right': { + canOverride: canOverride.property.right, + defaultValue: 'auto' + }, + 'text-align': { + canOverride: canOverride.property.textAlign, + // NOTE: we can't tell the real default value here, as it depends on default text direction + // this is a hack, but it doesn't matter because this value will be either overridden or + // it will disappear anyway + defaultValue: 'left|right' + }, + 'text-decoration': { + canOverride: canOverride.property.textDecoration, + defaultValue: 'none' + }, + 'text-overflow': { + canOverride: canOverride.property.textOverflow, + defaultValue: 'none' + }, + 'text-shadow': { + canOverride: canOverride.property.textShadow, + defaultValue: 'none' + }, + 'top': { + canOverride: canOverride.property.top, + defaultValue: 'auto' + }, + 'transform': { + canOverride: canOverride.property.transform, + vendorPrefixes: [ + '-moz-', + '-ms-', + '-webkit-' + ] + }, + 'vertical-align': { + canOverride: canOverride.property.verticalAlign, + defaultValue: 'baseline' + }, + 'visibility': { + canOverride: canOverride.property.visibility, + defaultValue: 'visible' + }, + 'white-space': { + canOverride: canOverride.property.whiteSpace, + defaultValue: 'normal' + }, + 'width': { + canOverride: canOverride.generic.unit, + defaultValue: 'auto', + shortestValue: '0' + }, + 'z-index': { + canOverride: canOverride.property.zIndex, + defaultValue: 'auto' + } +}; + +function cloneDescriptor(propertyName, prefix) { + var clonedDescriptor = override(compactable[propertyName], {}); + + if ('componentOf' in clonedDescriptor) { + clonedDescriptor.componentOf = clonedDescriptor.componentOf.map(function (shorthandName) { + return prefix + shorthandName; + }); + } + + if ('components' in clonedDescriptor) { + clonedDescriptor.components = clonedDescriptor.components.map(function (longhandName) { + return prefix + longhandName; + }); + } + + return clonedDescriptor; +} + +// generate vendor-prefixed properties +var vendorPrefixedCompactable = {}; + +for (var propertyName in compactable) { + var descriptor = compactable[propertyName]; + + if (!('vendorPrefixes' in descriptor)) { + continue; + } + + for (var i = 0; i < descriptor.vendorPrefixes.length; i++) { + var prefix = descriptor.vendorPrefixes[i]; + var clonedDescriptor = cloneDescriptor(propertyName, prefix); + delete clonedDescriptor.vendorPrefixes; + + vendorPrefixedCompactable[prefix + propertyName] = clonedDescriptor; + } + + delete descriptor.vendorPrefixes; +} + +module.exports = override(compactable, vendorPrefixedCompactable); diff --git a/node_modules/clean-css/lib/optimizer/level-2/extract-properties.js b/node_modules/clean-css/lib/optimizer/level-2/extract-properties.js new file mode 100644 index 000000000..8b9b1c2e0 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/extract-properties.js @@ -0,0 +1,73 @@ +// This extractor is used in level 2 optimizations +// IMPORTANT: Mind Token class and this code is not related! +// Properties will be tokenized in one step, see #429 + +var Token = require('../../tokenizer/token'); +var serializeRules = require('../../writer/one-time').rules; +var serializeValue = require('../../writer/one-time').value; + +function extractProperties(token) { + var properties = []; + var inSpecificSelector; + var property; + var name; + var value; + var i, l; + + if (token[0] == Token.RULE) { + inSpecificSelector = !/[\.\+>~]/.test(serializeRules(token[1])); + + for (i = 0, l = token[2].length; i < l; i++) { + property = token[2][i]; + + if (property[0] != Token.PROPERTY) + continue; + + name = property[1][1]; + if (name.length === 0) + continue; + + if (name.indexOf('--') === 0) + continue; + + value = serializeValue(property, i); + + properties.push([ + name, + value, + findNameRoot(name), + token[2][i], + name + ':' + value, + token[1], + inSpecificSelector + ]); + } + } else if (token[0] == Token.NESTED_BLOCK) { + for (i = 0, l = token[2].length; i < l; i++) { + properties = properties.concat(extractProperties(token[2][i])); + } + } + + return properties; +} + +function findNameRoot(name) { + if (name == 'list-style') + return name; + if (name.indexOf('-radius') > 0) + return 'border-radius'; + if (name == 'border-collapse' || name == 'border-spacing' || name == 'border-image') + return name; + if (name.indexOf('border-') === 0 && /^border\-\w+\-\w+$/.test(name)) + return name.match(/border\-\w+/)[0]; + if (name.indexOf('border-') === 0 && /^border\-\w+$/.test(name)) + return 'border'; + if (name.indexOf('text-') === 0) + return name; + if (name == '-chrome-') + return name; + + return name.replace(/^\-\w+\-/, '').match(/([a-zA-Z]+)/)[0].toLowerCase(); +} + +module.exports = extractProperties; diff --git a/node_modules/clean-css/lib/optimizer/level-2/invalid-property-error.js b/node_modules/clean-css/lib/optimizer/level-2/invalid-property-error.js new file mode 100644 index 000000000..86d5b5f9b --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/invalid-property-error.js @@ -0,0 +1,10 @@ +function InvalidPropertyError(message) { + this.name = 'InvalidPropertyError'; + this.message = message; + this.stack = (new Error()).stack; +} + +InvalidPropertyError.prototype = Object.create(Error.prototype); +InvalidPropertyError.prototype.constructor = InvalidPropertyError; + +module.exports = InvalidPropertyError; diff --git a/node_modules/clean-css/lib/optimizer/level-2/is-mergeable.js b/node_modules/clean-css/lib/optimizer/level-2/is-mergeable.js new file mode 100644 index 000000000..4a91a8331 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/is-mergeable.js @@ -0,0 +1,236 @@ +var Marker = require('../../tokenizer/marker'); +var split = require('../../utils/split'); + +var DEEP_SELECTOR_PATTERN = /\/deep\//; +var DOUBLE_COLON_PATTERN = /^::/; +var NOT_PSEUDO = ':not'; +var PSEUDO_CLASSES_WITH_ARGUMENTS = [ + ':dir', + ':lang', + ':not', + ':nth-child', + ':nth-last-child', + ':nth-last-of-type', + ':nth-of-type' +]; +var RELATION_PATTERN = /[>\+~]/; + +var Level = { + DOUBLE_QUOTE: 'double-quote', + SINGLE_QUOTE: 'single-quote', + ROOT: 'root' +}; + +function isMergeable(selector, mergeablePseudoClasses, mergeablePseudoElements) { + var singleSelectors = split(selector, Marker.COMMA); + var singleSelector; + var i, l; + + for (i = 0, l = singleSelectors.length; i < l; i++) { + singleSelector = singleSelectors[i]; + + if (singleSelector.length === 0 || + isDeepSelector(singleSelector) || + (singleSelector.indexOf(Marker.COLON) > -1 && !areMergeable(singleSelector, extractPseudoFrom(singleSelector), mergeablePseudoClasses, mergeablePseudoElements))) { + return false; + } + } + + return true; +} + +function isDeepSelector(selector) { + return DEEP_SELECTOR_PATTERN.test(selector); +} + +function extractPseudoFrom(selector) { + var list = []; + var character; + var buffer = []; + var level = Level.ROOT; + var roundBracketLevel = 0; + var isQuoted; + var isEscaped; + var isPseudo = false; + var isRelation; + var wasColon = false; + var index; + var len; + + for (index = 0, len = selector.length; index < len; index++) { + character = selector[index]; + + isRelation = !isEscaped && RELATION_PATTERN.test(character); + isQuoted = level == Level.DOUBLE_QUOTE || level == Level.SINGLE_QUOTE; + + if (isEscaped) { + buffer.push(character); + } else if (character == Marker.DOUBLE_QUOTE && level == Level.ROOT) { + buffer.push(character); + level = Level.DOUBLE_QUOTE; + } else if (character == Marker.DOUBLE_QUOTE && level == Level.DOUBLE_QUOTE) { + buffer.push(character); + level = Level.ROOT; + } else if (character == Marker.SINGLE_QUOTE && level == Level.ROOT) { + buffer.push(character); + level = Level.SINGLE_QUOTE; + } else if (character == Marker.SINGLE_QUOTE && level == Level.SINGLE_QUOTE) { + buffer.push(character); + level = Level.ROOT; + } else if (isQuoted) { + buffer.push(character); + } else if (character == Marker.OPEN_ROUND_BRACKET) { + buffer.push(character); + roundBracketLevel++; + } else if (character == Marker.CLOSE_ROUND_BRACKET && roundBracketLevel == 1 && isPseudo) { + buffer.push(character); + list.push(buffer.join('')); + roundBracketLevel--; + buffer = []; + isPseudo = false; + } else if (character == Marker.CLOSE_ROUND_BRACKET) { + buffer.push(character); + roundBracketLevel--; + } else if (character == Marker.COLON && roundBracketLevel === 0 && isPseudo && !wasColon) { + list.push(buffer.join('')); + buffer = []; + buffer.push(character); + } else if (character == Marker.COLON && roundBracketLevel === 0 && !wasColon) { + buffer = []; + buffer.push(character); + isPseudo = true; + } else if (character == Marker.SPACE && roundBracketLevel === 0 && isPseudo) { + list.push(buffer.join('')); + buffer = []; + isPseudo = false; + } else if (isRelation && roundBracketLevel === 0 && isPseudo) { + list.push(buffer.join('')); + buffer = []; + isPseudo = false; + } else { + buffer.push(character); + } + + isEscaped = character == Marker.BACK_SLASH; + wasColon = character == Marker.COLON; + } + + if (buffer.length > 0 && isPseudo) { + list.push(buffer.join('')); + } + + return list; +} + +function areMergeable(selector, matches, mergeablePseudoClasses, mergeablePseudoElements) { + return areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) && + needArguments(matches) && + (matches.length < 2 || !someIncorrectlyChained(selector, matches)) && + (matches.length < 2 || !someMixed(matches)); +} + +function areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) { + var match; + var name; + var i, l; + + for (i = 0, l = matches.length; i < l; i++) { + match = matches[i]; + name = match.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 ? + match.substring(0, match.indexOf(Marker.OPEN_ROUND_BRACKET)) : + match; + + if (mergeablePseudoClasses.indexOf(name) === -1 && mergeablePseudoElements.indexOf(name) === -1) { + return false; + } + } + + return true; +} + +function needArguments(matches) { + var match; + var name; + var bracketOpensAt; + var hasArguments; + var i, l; + + for (i = 0, l = matches.length; i < l; i++) { + match = matches[i]; + + bracketOpensAt = match.indexOf(Marker.OPEN_ROUND_BRACKET); + hasArguments = bracketOpensAt > -1; + name = hasArguments ? + match.substring(0, bracketOpensAt) : + match; + + if (hasArguments && PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) == -1) { + return false; + } + + if (!hasArguments && PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) > -1) { + return false; + } + } + + return true; +} + +function someIncorrectlyChained(selector, matches) { + var positionInSelector = 0; + var match; + var matchAt; + var nextMatch; + var nextMatchAt; + var name; + var nextName; + var areChained; + var i, l; + + for (i = 0, l = matches.length; i < l; i++) { + match = matches[i]; + nextMatch = matches[i + 1]; + + if (!nextMatch) { + break; + } + + matchAt = selector.indexOf(match, positionInSelector); + nextMatchAt = selector.indexOf(match, matchAt + 1); + positionInSelector = nextMatchAt; + areChained = matchAt + match.length == nextMatchAt; + + if (areChained) { + name = match.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 ? + match.substring(0, match.indexOf(Marker.OPEN_ROUND_BRACKET)) : + match; + nextName = nextMatch.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 ? + nextMatch.substring(0, nextMatch.indexOf(Marker.OPEN_ROUND_BRACKET)) : + nextMatch; + + if (name != NOT_PSEUDO || nextName != NOT_PSEUDO) { + return true; + } + } + } + + return false; +} + +function someMixed(matches) { + var firstIsPseudoElement = DOUBLE_COLON_PATTERN.test(matches[0]); + var match; + var i, l; + + for (i = 0, l = matches.length; i < l; i++) { + match = matches[i]; + + if (DOUBLE_COLON_PATTERN.test(match) != firstIsPseudoElement) { + return true; + } + } + + return false; +} + +module.exports = isMergeable; diff --git a/node_modules/clean-css/lib/optimizer/level-2/merge-adjacent.js b/node_modules/clean-css/lib/optimizer/level-2/merge-adjacent.js new file mode 100644 index 000000000..7c0537501 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/merge-adjacent.js @@ -0,0 +1,49 @@ +var isMergeable = require('./is-mergeable'); + +var optimizeProperties = require('./properties/optimize'); + +var sortSelectors = require('../level-1/sort-selectors'); +var tidyRules = require('../level-1/tidy-rules'); + +var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +var Token = require('../../tokenizer/token'); + +function mergeAdjacent(tokens, context) { + var lastToken = [null, [], []]; + var options = context.options; + var adjacentSpace = options.compatibility.selectors.adjacentSpace; + var selectorsSortingMethod = options.level[OptimizationLevel.One].selectorsSortingMethod; + var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; + var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; + var mergeLimit = 8191; + + for (var i = 0, l = tokens.length; i < l; i++) { + var token = tokens[i]; + + if (token[0] != Token.RULE) { + lastToken = [null, [], []]; + continue; + } + + if (lastToken[0] == Token.RULE && serializeRules(token[1]) == serializeRules(lastToken[1])) { + Array.prototype.push.apply(lastToken[2], token[2]); + optimizeProperties(lastToken[2], true, true, context); + token[2] = []; + } else if (lastToken[0] == Token.RULE && serializeBody(token[2]) == serializeBody(lastToken[2]) && + isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements) && + isMergeable(serializeRules(lastToken[1]), mergeablePseudoClasses, mergeablePseudoElements) && + lastToken[1].length < mergeLimit) { + lastToken[1] = tidyRules(lastToken[1].concat(token[1]), false, adjacentSpace, false, context.warnings); + lastToken[1] = lastToken.length > 1 ? sortSelectors(lastToken[1], selectorsSortingMethod) : lastToken[1]; + token[2] = []; + } else { + lastToken = token; + } + } +} + +module.exports = mergeAdjacent; diff --git a/node_modules/clean-css/lib/optimizer/level-2/merge-media-queries.js b/node_modules/clean-css/lib/optimizer/level-2/merge-media-queries.js new file mode 100644 index 000000000..c3c60dc24 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/merge-media-queries.js @@ -0,0 +1,103 @@ +var canReorder = require('./reorderable').canReorder; +var canReorderSingle = require('./reorderable').canReorderSingle; +var extractProperties = require('./extract-properties'); +var rulesOverlap = require('./rules-overlap'); + +var serializeRules = require('../../writer/one-time').rules; +var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; +var Token = require('../../tokenizer/token'); + +function mergeMediaQueries(tokens, context) { + var mergeSemantically = context.options.level[OptimizationLevel.Two].mergeSemantically; + var specificityCache = context.cache.specificity; + var candidates = {}; + var reduced = []; + + for (var i = tokens.length - 1; i >= 0; i--) { + var token = tokens[i]; + if (token[0] != Token.NESTED_BLOCK) { + continue; + } + + var key = serializeRules(token[1]); + var candidate = candidates[key]; + if (!candidate) { + candidate = []; + candidates[key] = candidate; + } + + candidate.push(i); + } + + for (var name in candidates) { + var positions = candidates[name]; + + positionLoop: + for (var j = positions.length - 1; j > 0; j--) { + var positionOne = positions[j]; + var tokenOne = tokens[positionOne]; + var positionTwo = positions[j - 1]; + var tokenTwo = tokens[positionTwo]; + + directionLoop: + for (var direction = 1; direction >= -1; direction -= 2) { + var topToBottom = direction == 1; + var from = topToBottom ? positionOne + 1 : positionTwo - 1; + var to = topToBottom ? positionTwo : positionOne; + var delta = topToBottom ? 1 : -1; + var source = topToBottom ? tokenOne : tokenTwo; + var target = topToBottom ? tokenTwo : tokenOne; + var movedProperties = extractProperties(source); + + while (from != to) { + var traversedProperties = extractProperties(tokens[from]); + from += delta; + + if (mergeSemantically && allSameRulePropertiesCanBeReordered(movedProperties, traversedProperties, specificityCache)) { + continue; + } + + if (!canReorder(movedProperties, traversedProperties, specificityCache)) + continue directionLoop; + } + + target[2] = topToBottom ? + source[2].concat(target[2]) : + target[2].concat(source[2]); + source[2] = []; + + reduced.push(target); + continue positionLoop; + } + } + } + + return reduced; +} + +function allSameRulePropertiesCanBeReordered(movedProperties, traversedProperties, specificityCache) { + var movedProperty; + var movedRule; + var traversedProperty; + var traversedRule; + var i, l; + var j, m; + + for (i = 0, l = movedProperties.length; i < l; i++) { + movedProperty = movedProperties[i]; + movedRule = movedProperty[5]; + + for (j = 0, m = traversedProperties.length; j < m; j++) { + traversedProperty = traversedProperties[j]; + traversedRule = traversedProperty[5]; + + if (rulesOverlap(movedRule, traversedRule, true) && !canReorderSingle(movedProperty, traversedProperty, specificityCache)) { + return false; + } + } + } + + return true; +} + +module.exports = mergeMediaQueries; diff --git a/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-body.js b/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-body.js new file mode 100644 index 000000000..bdcf53ec0 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-body.js @@ -0,0 +1,79 @@ +var isMergeable = require('./is-mergeable'); + +var sortSelectors = require('../level-1/sort-selectors'); +var tidyRules = require('../level-1/tidy-rules'); + +var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +var Token = require('../../tokenizer/token'); + +function unsafeSelector(value) { + return /\.|\*| :/.test(value); +} + +function isBemElement(token) { + var asString = serializeRules(token[1]); + return asString.indexOf('__') > -1 || asString.indexOf('--') > -1; +} + +function withoutModifier(selector) { + return selector.replace(/--[^ ,>\+~:]+/g, ''); +} + +function removeAnyUnsafeElements(left, candidates) { + var leftSelector = withoutModifier(serializeRules(left[1])); + + for (var body in candidates) { + var right = candidates[body]; + var rightSelector = withoutModifier(serializeRules(right[1])); + + if (rightSelector.indexOf(leftSelector) > -1 || leftSelector.indexOf(rightSelector) > -1) + delete candidates[body]; + } +} + +function mergeNonAdjacentByBody(tokens, context) { + var options = context.options; + var mergeSemantically = options.level[OptimizationLevel.Two].mergeSemantically; + var adjacentSpace = options.compatibility.selectors.adjacentSpace; + var selectorsSortingMethod = options.level[OptimizationLevel.One].selectorsSortingMethod; + var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; + var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; + var candidates = {}; + + for (var i = tokens.length - 1; i >= 0; i--) { + var token = tokens[i]; + if (token[0] != Token.RULE) + continue; + + if (token[2].length > 0 && (!mergeSemantically && unsafeSelector(serializeRules(token[1])))) + candidates = {}; + + if (token[2].length > 0 && mergeSemantically && isBemElement(token)) + removeAnyUnsafeElements(token, candidates); + + var candidateBody = serializeBody(token[2]); + var oldToken = candidates[candidateBody]; + if (oldToken && + isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements) && + isMergeable(serializeRules(oldToken[1]), mergeablePseudoClasses, mergeablePseudoElements)) { + + if (token[2].length > 0) { + token[1] = tidyRules(oldToken[1].concat(token[1]), false, adjacentSpace, false, context.warnings); + token[1] = token[1].length > 1 ? sortSelectors(token[1], selectorsSortingMethod) : token[1]; + } else { + token[1] = oldToken[1].concat(token[1]); + } + + oldToken[2] = []; + candidates[candidateBody] = null; + } + + candidates[serializeBody(token[2])] = token; + } +} + +module.exports = mergeNonAdjacentByBody; diff --git a/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-selector.js b/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-selector.js new file mode 100644 index 000000000..5e23064eb --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-selector.js @@ -0,0 +1,78 @@ +var canReorder = require('./reorderable').canReorder; +var extractProperties = require('./extract-properties'); + +var optimizeProperties = require('./properties/optimize'); + +var serializeRules = require('../../writer/one-time').rules; + +var Token = require('../../tokenizer/token'); + +function mergeNonAdjacentBySelector(tokens, context) { + var specificityCache = context.cache.specificity; + var allSelectors = {}; + var repeatedSelectors = []; + var i; + + for (i = tokens.length - 1; i >= 0; i--) { + if (tokens[i][0] != Token.RULE) + continue; + if (tokens[i][2].length === 0) + continue; + + var selector = serializeRules(tokens[i][1]); + allSelectors[selector] = [i].concat(allSelectors[selector] || []); + + if (allSelectors[selector].length == 2) + repeatedSelectors.push(selector); + } + + for (i = repeatedSelectors.length - 1; i >= 0; i--) { + var positions = allSelectors[repeatedSelectors[i]]; + + selectorIterator: + for (var j = positions.length - 1; j > 0; j--) { + var positionOne = positions[j - 1]; + var tokenOne = tokens[positionOne]; + var positionTwo = positions[j]; + var tokenTwo = tokens[positionTwo]; + + directionIterator: + for (var direction = 1; direction >= -1; direction -= 2) { + var topToBottom = direction == 1; + var from = topToBottom ? positionOne + 1 : positionTwo - 1; + var to = topToBottom ? positionTwo : positionOne; + var delta = topToBottom ? 1 : -1; + var moved = topToBottom ? tokenOne : tokenTwo; + var target = topToBottom ? tokenTwo : tokenOne; + var movedProperties = extractProperties(moved); + + while (from != to) { + var traversedProperties = extractProperties(tokens[from]); + from += delta; + + // traversed then moved as we move selectors towards the start + var reorderable = topToBottom ? + canReorder(movedProperties, traversedProperties, specificityCache) : + canReorder(traversedProperties, movedProperties, specificityCache); + + if (!reorderable && !topToBottom) + continue selectorIterator; + if (!reorderable && topToBottom) + continue directionIterator; + } + + if (topToBottom) { + Array.prototype.push.apply(moved[2], target[2]); + target[2] = moved[2]; + } else { + Array.prototype.push.apply(target[2], moved[2]); + } + + optimizeProperties(target[2], true, true, context); + moved[2] = []; + } + } + } +} + +module.exports = mergeNonAdjacentBySelector; diff --git a/node_modules/clean-css/lib/optimizer/level-2/optimize.js b/node_modules/clean-css/lib/optimizer/level-2/optimize.js new file mode 100644 index 000000000..78cd44666 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/optimize.js @@ -0,0 +1,124 @@ +var mergeAdjacent = require('./merge-adjacent'); +var mergeMediaQueries = require('./merge-media-queries'); +var mergeNonAdjacentByBody = require('./merge-non-adjacent-by-body'); +var mergeNonAdjacentBySelector = require('./merge-non-adjacent-by-selector'); +var reduceNonAdjacent = require('./reduce-non-adjacent'); +var removeDuplicateFontAtRules = require('./remove-duplicate-font-at-rules'); +var removeDuplicateMediaQueries = require('./remove-duplicate-media-queries'); +var removeDuplicates = require('./remove-duplicates'); +var restructure = require('./restructure'); + +var optimizeProperties = require('./properties/optimize'); + +var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; + +var Token = require('../../tokenizer/token'); + +function removeEmpty(tokens) { + for (var i = 0, l = tokens.length; i < l; i++) { + var token = tokens[i]; + var isEmpty = false; + + switch (token[0]) { + case Token.RULE: + isEmpty = token[1].length === 0 || token[2].length === 0; + break; + case Token.NESTED_BLOCK: + removeEmpty(token[2]); + isEmpty = token[2].length === 0; + break; + case Token.AT_RULE_BLOCK: + isEmpty = token[2].length === 0; + } + + if (isEmpty) { + tokens.splice(i, 1); + i--; + l--; + } + } +} + +function recursivelyOptimizeBlocks(tokens, context) { + for (var i = 0, l = tokens.length; i < l; i++) { + var token = tokens[i]; + + if (token[0] == Token.NESTED_BLOCK) { + var isKeyframes = /@(-moz-|-o-|-webkit-)?keyframes/.test(token[1][0][1]); + level2Optimize(token[2], context, !isKeyframes); + } + } +} + +function recursivelyOptimizeProperties(tokens, context) { + for (var i = 0, l = tokens.length; i < l; i++) { + var token = tokens[i]; + + switch (token[0]) { + case Token.RULE: + optimizeProperties(token[2], true, true, context); + break; + case Token.NESTED_BLOCK: + recursivelyOptimizeProperties(token[2], context); + } + } +} + +function level2Optimize(tokens, context, withRestructuring) { + var levelOptions = context.options.level[OptimizationLevel.Two]; + var reduced; + var i; + + recursivelyOptimizeBlocks(tokens, context); + recursivelyOptimizeProperties(tokens, context); + + if (levelOptions.removeDuplicateRules) { + removeDuplicates(tokens, context); + } + + if (levelOptions.mergeAdjacentRules) { + mergeAdjacent(tokens, context); + } + + if (levelOptions.reduceNonAdjacentRules) { + reduceNonAdjacent(tokens, context); + } + + if (levelOptions.mergeNonAdjacentRules && levelOptions.mergeNonAdjacentRules != 'body') { + mergeNonAdjacentBySelector(tokens, context); + } + + if (levelOptions.mergeNonAdjacentRules && levelOptions.mergeNonAdjacentRules != 'selector') { + mergeNonAdjacentByBody(tokens, context); + } + + if (levelOptions.restructureRules && levelOptions.mergeAdjacentRules && withRestructuring) { + restructure(tokens, context); + mergeAdjacent(tokens, context); + } + + if (levelOptions.restructureRules && !levelOptions.mergeAdjacentRules && withRestructuring) { + restructure(tokens, context); + } + + if (levelOptions.removeDuplicateFontRules) { + removeDuplicateFontAtRules(tokens, context); + } + + if (levelOptions.removeDuplicateMediaBlocks) { + removeDuplicateMediaQueries(tokens, context); + } + + if (levelOptions.mergeMedia) { + reduced = mergeMediaQueries(tokens, context); + for (i = reduced.length - 1; i >= 0; i--) { + level2Optimize(reduced[i][2], context, false); + } + } + + removeEmpty(tokens); + + return tokens; +} + +module.exports = level2Optimize; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/every-values-pair.js b/node_modules/clean-css/lib/optimizer/level-2/properties/every-values-pair.js new file mode 100644 index 000000000..44fcb7d53 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/every-values-pair.js @@ -0,0 +1,28 @@ +var Marker = require('../../../tokenizer/marker'); + +function everyValuesPair(fn, left, right) { + var leftSize = left.value.length; + var rightSize = right.value.length; + var total = Math.max(leftSize, rightSize); + var lowerBound = Math.min(leftSize, rightSize) - 1; + var leftValue; + var rightValue; + var position; + + for (position = 0; position < total; position++) { + leftValue = left.value[position] && left.value[position][1] || leftValue; + rightValue = right.value[position] && right.value[position][1] || rightValue; + + if (leftValue == Marker.COMMA || rightValue == Marker.COMMA) { + continue; + } + + if (!fn(leftValue, rightValue, position, position <= lowerBound)) { + return false; + } + } + + return true; +} + +module.exports = everyValuesPair; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/find-component-in.js b/node_modules/clean-css/lib/optimizer/level-2/properties/find-component-in.js new file mode 100644 index 000000000..dd7c719d5 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/find-component-in.js @@ -0,0 +1,40 @@ +var compactable = require('../compactable'); + +function findComponentIn(shorthand, longhand) { + var comparator = nameComparator(longhand); + + return findInDirectComponents(shorthand, comparator) || findInSubComponents(shorthand, comparator); +} + +function nameComparator(to) { + return function (property) { + return to.name === property.name; + }; +} + +function findInDirectComponents(shorthand, comparator) { + return shorthand.components.filter(comparator)[0]; +} + +function findInSubComponents(shorthand, comparator) { + var shorthandComponent; + var longhandMatch; + var i, l; + + if (!compactable[shorthand.name].shorthandComponents) { + return; + } + + for (i = 0, l = shorthand.components.length; i < l; i++) { + shorthandComponent = shorthand.components[i]; + longhandMatch = findInDirectComponents(shorthandComponent, comparator); + + if (longhandMatch) { + return longhandMatch; + } + } + + return; +} + +module.exports = findComponentIn; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/has-inherit.js b/node_modules/clean-css/lib/optimizer/level-2/properties/has-inherit.js new file mode 100644 index 000000000..84f220d32 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/has-inherit.js @@ -0,0 +1,10 @@ +function hasInherit(property) { + for (var i = property.value.length - 1; i >= 0; i--) { + if (property.value[i][1] == 'inherit') + return true; + } + + return false; +} + +module.exports = hasInherit; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/is-component-of.js b/node_modules/clean-css/lib/optimizer/level-2/properties/is-component-of.js new file mode 100644 index 000000000..237de7d10 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/is-component-of.js @@ -0,0 +1,22 @@ +var compactable = require('../compactable'); + +function isComponentOf(property1, property2, shallow) { + return isDirectComponentOf(property1, property2) || + !shallow && !!compactable[property1.name].shorthandComponents && isSubComponentOf(property1, property2); +} + +function isDirectComponentOf(property1, property2) { + var descriptor = compactable[property1.name]; + + return 'components' in descriptor && descriptor.components.indexOf(property2.name) > -1; +} + +function isSubComponentOf(property1, property2) { + return property1 + .components + .some(function (component) { + return isDirectComponentOf(component, property2); + }); +} + +module.exports = isComponentOf; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/merge-into-shorthands.js b/node_modules/clean-css/lib/optimizer/level-2/properties/merge-into-shorthands.js new file mode 100644 index 000000000..a75c4ed63 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/merge-into-shorthands.js @@ -0,0 +1,157 @@ +var everyValuesPair = require('./every-values-pair'); +var hasInherit = require('./has-inherit'); +var populateComponents = require('./populate-components'); + +var compactable = require('../compactable'); +var deepClone = require('../clone').deep; + +var wrapSingle = require('../../wrap-for-optimizing').single; + +var Token = require('../../../tokenizer/token'); + +function mixedImportance(components) { + var important; + + for (var name in components) { + if (undefined !== important && components[name].important != important) + return true; + + important = components[name].important; + } + + return false; +} + +function joinMetadata(components, at) { + var metadata = []; + var component; + var originalValue; + var componentMetadata; + var name; + + for (name in components) { + component = components[name]; + originalValue = component.all[component.position]; + componentMetadata = originalValue[at][originalValue[at].length - 1]; + + Array.prototype.push.apply(metadata, componentMetadata); + } + + return metadata; +} + +function replaceWithShorthand(properties, candidateComponents, name, validator) { + var descriptor = compactable[name]; + var nameMetadata; + var valueMetadata; + var newValuePlaceholder = [ + Token.PROPERTY, + [Token.PROPERTY_NAME, name], + [Token.PROPERTY_VALUE, descriptor.defaultValue] + ]; + var mayOverride; + var all; + + var newProperty = wrapSingle(newValuePlaceholder); + newProperty.shorthand = true; + newProperty.dirty = true; + + populateComponents([newProperty], validator, []); + + for (var i = 0, l = descriptor.components.length; i < l; i++) { + var component = candidateComponents[descriptor.components[i]]; + + if (hasInherit(component)) + return; + + mayOverride = compactable[component.name].canOverride; + if (!everyValuesPair(mayOverride.bind(null, validator), newProperty.components[i], component)) + return; + + newProperty.components[i] = deepClone(component); + newProperty.important = component.important; + + all = component.all; + } + + for (var componentName in candidateComponents) { + candidateComponents[componentName].unused = true; + } + + nameMetadata = joinMetadata(candidateComponents, 1); + newValuePlaceholder[1].push(nameMetadata); + + valueMetadata = joinMetadata(candidateComponents, 2); + newValuePlaceholder[2].push(valueMetadata); + + newProperty.position = all.length; + newProperty.all = all; + newProperty.all.push(newValuePlaceholder); + + properties.push(newProperty); +} + +function invalidateOrCompact(properties, position, candidates, validator) { + var property = properties[position]; + + for (var name in candidates) { + if (undefined !== property && name == property.name) + continue; + + var descriptor = compactable[name]; + var candidateComponents = candidates[name]; + if (descriptor.components.length > Object.keys(candidateComponents).length) { + delete candidates[name]; + continue; + } + + if (mixedImportance(candidateComponents)) + continue; + + replaceWithShorthand(properties, candidateComponents, name, validator); + } +} + +function mergeIntoShorthands(properties, validator) { + var candidates = {}; + var descriptor; + var componentOf; + var property; + var i, l; + var j, m; + + if (properties.length < 3) + return; + + for (i = 0, l = properties.length; i < l; i++) { + property = properties[i]; + + if (property.unused) + continue; + + if (property.hack) + continue; + + if (property.block) + continue; + + descriptor = compactable[property.name]; + if (!descriptor || !descriptor.componentOf) + continue; + + if (property.shorthand) { + invalidateOrCompact(properties, i, candidates, validator); + } else { + for (j = 0, m = descriptor.componentOf.length; j < m; j++) { + componentOf = descriptor.componentOf[j]; + + candidates[componentOf] = candidates[componentOf] || {}; + candidates[componentOf][property.name] = property; + } + } + } + + invalidateOrCompact(properties, i, candidates, validator); +} + +module.exports = mergeIntoShorthands; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/optimize.js b/node_modules/clean-css/lib/optimizer/level-2/properties/optimize.js new file mode 100644 index 000000000..250b05fc4 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/optimize.js @@ -0,0 +1,39 @@ +var mergeIntoShorthands = require('./merge-into-shorthands'); +var overrideProperties = require('./override-properties'); +var populateComponents = require('./populate-components'); + +var restoreWithComponents = require('../restore-with-components'); + +var wrapForOptimizing = require('../../wrap-for-optimizing').all; +var removeUnused = require('../../remove-unused'); +var restoreFromOptimizing = require('../../restore-from-optimizing'); + +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +function optimizeProperties(properties, withOverriding, withMerging, context) { + var _properties = wrapForOptimizing(properties, false); + var _property; + var i, l; + + populateComponents(_properties, context.validator, context.warnings); + + for (i = 0, l = _properties.length; i < l; i++) { + _property = _properties[i]; + if (_property.block) { + optimizeProperties(_property.value[0][1], withOverriding, withMerging, context); + } + } + + if (withOverriding && context.options.level[OptimizationLevel.Two].overrideProperties) { + overrideProperties(_properties, withMerging, context.options.compatibility, context.validator); + } + + if (withMerging && context.options.level[OptimizationLevel.Two].mergeIntoShorthands) { + mergeIntoShorthands(_properties, context.validator); + } + + restoreFromOptimizing(_properties, restoreWithComponents); + removeUnused(_properties); +} + +module.exports = optimizeProperties; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/override-properties.js b/node_modules/clean-css/lib/optimizer/level-2/properties/override-properties.js new file mode 100644 index 000000000..3c9d8d2af --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/override-properties.js @@ -0,0 +1,441 @@ +var hasInherit = require('./has-inherit'); +var everyValuesPair = require('./every-values-pair'); +var findComponentIn = require('./find-component-in'); +var isComponentOf = require('./is-component-of'); +var overridesNonComponentShorthand = require('./overrides-non-component-shorthand'); +var sameVendorPrefixesIn = require('./vendor-prefixes').same; + +var compactable = require('../compactable'); +var deepClone = require('../clone').deep; +var deepClone = require('../clone').deep; +var restoreWithComponents = require('../restore-with-components'); +var shallowClone = require('../clone').shallow; + +var restoreFromOptimizing = require('../../restore-from-optimizing'); + +var Token = require('../../../tokenizer/token'); +var Marker = require('../../../tokenizer/marker'); + +var serializeProperty = require('../../../writer/one-time').property; + +function wouldBreakCompatibility(property, validator) { + for (var i = 0; i < property.components.length; i++) { + var component = property.components[i]; + var descriptor = compactable[component.name]; + var canOverride = descriptor && descriptor.canOverride || canOverride.sameValue; + + var _component = shallowClone(component); + _component.value = [[Token.PROPERTY_VALUE, descriptor.defaultValue]]; + + if (!everyValuesPair(canOverride.bind(null, validator), _component, component)) { + return true; + } + } + + return false; +} + +function overrideIntoMultiplex(property, by) { + by.unused = true; + + turnIntoMultiplex(by, multiplexSize(property)); + property.value = by.value; +} + +function overrideByMultiplex(property, by) { + by.unused = true; + property.multiplex = true; + property.value = by.value; +} + +function overrideSimple(property, by) { + by.unused = true; + property.value = by.value; +} + +function override(property, by) { + if (by.multiplex) + overrideByMultiplex(property, by); + else if (property.multiplex) + overrideIntoMultiplex(property, by); + else + overrideSimple(property, by); +} + +function overrideShorthand(property, by) { + by.unused = true; + + for (var i = 0, l = property.components.length; i < l; i++) { + override(property.components[i], by.components[i], property.multiplex); + } +} + +function turnIntoMultiplex(property, size) { + property.multiplex = true; + + for (var i = 0, l = property.components.length; i < l; i++) { + var component = property.components[i]; + if (component.multiplex) + continue; + + var value = component.value.slice(0); + + for (var j = 1; j < size; j++) { + component.value.push([Token.PROPERTY_VALUE, Marker.COMMA]); + Array.prototype.push.apply(component.value, value); + } + } +} + +function multiplexSize(component) { + var size = 0; + + for (var i = 0, l = component.value.length; i < l; i++) { + if (component.value[i][1] == Marker.COMMA) + size++; + } + + return size + 1; +} + +function lengthOf(property) { + var fakeAsArray = [ + Token.PROPERTY, + [Token.PROPERTY_NAME, property.name] + ].concat(property.value); + return serializeProperty([fakeAsArray], 0).length; +} + +function moreSameShorthands(properties, startAt, name) { + // Since we run the main loop in `compactOverrides` backwards, at this point some + // properties may not be marked as unused. + // We should consider reverting the order if possible + var count = 0; + + for (var i = startAt; i >= 0; i--) { + if (properties[i].name == name && !properties[i].unused) + count++; + if (count > 1) + break; + } + + return count > 1; +} + +function overridingFunction(shorthand, validator) { + for (var i = 0, l = shorthand.components.length; i < l; i++) { + if (anyValue(validator.isValidFunction, shorthand.components[i])) + return true; + } + + return false; +} + +function anyValue(fn, property) { + for (var i = 0, l = property.value.length; i < l; i++) { + if (property.value[i][1] == Marker.COMMA) + continue; + + if (fn(property.value[i][1])) + return true; + } + + return false; +} + +function wouldResultInLongerValue(left, right) { + if (!left.multiplex && !right.multiplex || left.multiplex && right.multiplex) + return false; + + var multiplex = left.multiplex ? left : right; + var simple = left.multiplex ? right : left; + var component; + + var multiplexClone = deepClone(multiplex); + restoreFromOptimizing([multiplexClone], restoreWithComponents); + + var simpleClone = deepClone(simple); + restoreFromOptimizing([simpleClone], restoreWithComponents); + + var lengthBefore = lengthOf(multiplexClone) + 1 + lengthOf(simpleClone); + + if (left.multiplex) { + component = findComponentIn(multiplexClone, simpleClone); + overrideIntoMultiplex(component, simpleClone); + } else { + component = findComponentIn(simpleClone, multiplexClone); + turnIntoMultiplex(simpleClone, multiplexSize(multiplexClone)); + overrideByMultiplex(component, multiplexClone); + } + + restoreFromOptimizing([simpleClone], restoreWithComponents); + + var lengthAfter = lengthOf(simpleClone); + + return lengthBefore <= lengthAfter; +} + +function isCompactable(property) { + return property.name in compactable; +} + +function noneOverrideHack(left, right) { + return !left.multiplex && + (left.name == 'background' || left.name == 'background-image') && + right.multiplex && + (right.name == 'background' || right.name == 'background-image') && + anyLayerIsNone(right.value); +} + +function anyLayerIsNone(values) { + var layers = intoLayers(values); + + for (var i = 0, l = layers.length; i < l; i++) { + if (layers[i].length == 1 && layers[i][0][1] == 'none') + return true; + } + + return false; +} + +function intoLayers(values) { + var layers = []; + + for (var i = 0, layer = [], l = values.length; i < l; i++) { + var value = values[i]; + if (value[1] == Marker.COMMA) { + layers.push(layer); + layer = []; + } else { + layer.push(value); + } + } + + layers.push(layer); + return layers; +} + +function overrideProperties(properties, withMerging, compatibility, validator) { + var mayOverride, right, left, component; + var overriddenComponents; + var overriddenComponent; + var overridingComponent; + var overridable; + var i, j, k; + + propertyLoop: + for (i = properties.length - 1; i >= 0; i--) { + right = properties[i]; + + if (!isCompactable(right)) + continue; + + if (right.block) + continue; + + mayOverride = compactable[right.name].canOverride; + + traverseLoop: + for (j = i - 1; j >= 0; j--) { + left = properties[j]; + + if (!isCompactable(left)) + continue; + + if (left.block) + continue; + + if (left.unused || right.unused) + continue; + + if (left.hack && !right.hack && !right.important || !left.hack && !left.important && right.hack) + continue; + + if (left.important == right.important && left.hack[0] != right.hack[0]) + continue; + + if (left.important == right.important && (left.hack[0] != right.hack[0] || (left.hack[1] && left.hack[1] != right.hack[1]))) + continue; + + if (hasInherit(right)) + continue; + + if (noneOverrideHack(left, right)) + continue; + + if (right.shorthand && isComponentOf(right, left)) { + // maybe `left` can be overridden by `right` which is a shorthand? + if (!right.important && left.important) + continue; + + if (!sameVendorPrefixesIn([left], right.components)) + continue; + + if (!anyValue(validator.isValidFunction, left) && overridingFunction(right, validator)) + continue; + + component = findComponentIn(right, left); + mayOverride = compactable[left.name].canOverride; + if (everyValuesPair(mayOverride.bind(null, validator), left, component)) { + left.unused = true; + } + } else if (right.shorthand && overridesNonComponentShorthand(right, left)) { + // `right` is a shorthand while `left` can be overriden by it, think `border` and `border-top` + if (!right.important && left.important) { + continue; + } + + if (!sameVendorPrefixesIn([left], right.components)) { + continue; + } + + if (!anyValue(validator.isValidFunction, left) && overridingFunction(right, validator)) { + continue; + } + + overriddenComponents = left.shorthand ? + left.components: + [left]; + + for (k = overriddenComponents.length - 1; k >= 0; k--) { + overriddenComponent = overriddenComponents[k]; + overridingComponent = findComponentIn(right, overriddenComponent); + mayOverride = compactable[overriddenComponent.name].canOverride; + + if (!everyValuesPair(mayOverride.bind(null, validator), left, overridingComponent)) { + continue traverseLoop; + } + } + + left.unused = true; + } else if (withMerging && left.shorthand && !right.shorthand && isComponentOf(left, right, true)) { + // maybe `right` can be pulled into `left` which is a shorthand? + if (right.important && !left.important) + continue; + + if (!right.important && left.important) { + right.unused = true; + continue; + } + + // Pending more clever algorithm in #527 + if (moreSameShorthands(properties, i - 1, left.name)) + continue; + + if (overridingFunction(left, validator)) + continue; + + component = findComponentIn(left, right); + if (everyValuesPair(mayOverride.bind(null, validator), component, right)) { + var disabledBackgroundMerging = + !compatibility.properties.backgroundClipMerging && component.name.indexOf('background-clip') > -1 || + !compatibility.properties.backgroundOriginMerging && component.name.indexOf('background-origin') > -1 || + !compatibility.properties.backgroundSizeMerging && component.name.indexOf('background-size') > -1; + var nonMergeableValue = compactable[right.name].nonMergeableValue === right.value[0][1]; + + if (disabledBackgroundMerging || nonMergeableValue) + continue; + + if (!compatibility.properties.merging && wouldBreakCompatibility(left, validator)) + continue; + + if (component.value[0][1] != right.value[0][1] && (hasInherit(left) || hasInherit(right))) + continue; + + if (wouldResultInLongerValue(left, right)) + continue; + + if (!left.multiplex && right.multiplex) + turnIntoMultiplex(left, multiplexSize(right)); + + override(component, right); + left.dirty = true; + } + } else if (withMerging && left.shorthand && right.shorthand && left.name == right.name) { + // merge if all components can be merged + + if (!left.multiplex && right.multiplex) + continue; + + if (!right.important && left.important) { + right.unused = true; + continue propertyLoop; + } + + if (right.important && !left.important) { + left.unused = true; + continue; + } + + for (k = left.components.length - 1; k >= 0; k--) { + var leftComponent = left.components[k]; + var rightComponent = right.components[k]; + + mayOverride = compactable[leftComponent.name].canOverride; + if (!everyValuesPair(mayOverride.bind(null, validator), leftComponent, rightComponent)) + continue propertyLoop; + } + + overrideShorthand(left, right); + left.dirty = true; + } else if (withMerging && left.shorthand && right.shorthand && isComponentOf(left, right)) { + // border is a shorthand but any of its components is a shorthand too + + if (!left.important && right.important) + continue; + + component = findComponentIn(left, right); + mayOverride = compactable[right.name].canOverride; + if (!everyValuesPair(mayOverride.bind(null, validator), component, right)) + continue; + + if (left.important && !right.important) { + right.unused = true; + continue; + } + + var rightRestored = compactable[right.name].restore(right, compactable); + if (rightRestored.length > 1) + continue; + + component = findComponentIn(left, right); + override(component, right); + right.dirty = true; + } else if (left.name == right.name) { + // two non-shorthands should be merged based on understandability + overridable = true; + + if (right.shorthand) { + for (k = right.components.length - 1; k >= 0 && overridable; k--) { + overriddenComponent = left.components[k]; + overridingComponent = right.components[k]; + mayOverride = compactable[overridingComponent.name].canOverride; + + overridable = overridable && everyValuesPair(mayOverride.bind(null, validator), overriddenComponent, overridingComponent); + } + } else { + mayOverride = compactable[right.name].canOverride; + overridable = everyValuesPair(mayOverride.bind(null, validator), left, right); + } + + if (left.important && !right.important && overridable) { + right.unused = true; + continue; + } + + if (!left.important && right.important && overridable) { + left.unused = true; + continue; + } + + if (!overridable) { + continue; + } + + left.unused = true; + } + } + } +} + +module.exports = overrideProperties; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/overrides-non-component-shorthand.js b/node_modules/clean-css/lib/optimizer/level-2/properties/overrides-non-component-shorthand.js new file mode 100644 index 000000000..c385218a9 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/overrides-non-component-shorthand.js @@ -0,0 +1,9 @@ +var compactable = require('../compactable'); + +function overridesNonComponentShorthand(property1, property2) { + return property1.name in compactable && + 'overridesShorthands' in compactable[property1.name] && + compactable[property1.name].overridesShorthands.indexOf(property2.name) > -1; +} + +module.exports = overridesNonComponentShorthand; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/populate-components.js b/node_modules/clean-css/lib/optimizer/level-2/properties/populate-components.js new file mode 100644 index 000000000..c587e83c4 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/populate-components.js @@ -0,0 +1,42 @@ +var compactable = require('../compactable'); +var InvalidPropertyError = require('../invalid-property-error'); + +function populateComponents(properties, validator, warnings) { + var component; + var j, m; + + for (var i = properties.length - 1; i >= 0; i--) { + var property = properties[i]; + var descriptor = compactable[property.name]; + + if (descriptor && descriptor.shorthand) { + property.shorthand = true; + property.dirty = true; + + try { + property.components = descriptor.breakUp(property, compactable, validator); + + if (descriptor.shorthandComponents) { + for (j = 0, m = property.components.length; j < m; j++) { + component = property.components[j]; + component.components = compactable[component.name].breakUp(component, compactable, validator); + } + } + } catch (e) { + if (e instanceof InvalidPropertyError) { + property.components = []; // this will set property.unused to true below + warnings.push(e.message); + } else { + throw e; + } + } + + if (property.components.length > 0) + property.multiplex = property.components[0].multiplex; + else + property.unused = true; + } + } +} + +module.exports = populateComponents; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/understandable.js b/node_modules/clean-css/lib/optimizer/level-2/properties/understandable.js new file mode 100644 index 000000000..6c77db50d --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/understandable.js @@ -0,0 +1,15 @@ +var sameVendorPrefixes = require('./vendor-prefixes').same; + +function understandable(validator, value1, value2, _position, isPaired) { + if (!sameVendorPrefixes(value1, value2)) { + return false; + } + + if (isPaired && validator.isValidVariable(value1) !== validator.isValidVariable(value2)) { + return false; + } + + return true; +} + +module.exports = understandable; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/vendor-prefixes.js b/node_modules/clean-css/lib/optimizer/level-2/properties/vendor-prefixes.js new file mode 100644 index 000000000..f9ab52728 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/vendor-prefixes.js @@ -0,0 +1,23 @@ +var VENDOR_PREFIX_PATTERN = /(?:^|\W)(\-\w+\-)/g; + +function unique(value) { + var prefixes = []; + var match; + + while ((match = VENDOR_PREFIX_PATTERN.exec(value)) !== null) { + if (prefixes.indexOf(match[0]) == -1) { + prefixes.push(match[0]); + } + } + + return prefixes; +} + +function same(value1, value2) { + return unique(value1).sort().join(',') == unique(value2).sort().join(','); +} + +module.exports = { + unique: unique, + same: same +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/reduce-non-adjacent.js b/node_modules/clean-css/lib/optimizer/level-2/reduce-non-adjacent.js new file mode 100644 index 000000000..3461bb268 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/reduce-non-adjacent.js @@ -0,0 +1,178 @@ +var isMergeable = require('./is-mergeable'); + +var optimizeProperties = require('./properties/optimize'); + +var cloneArray = require('../../utils/clone-array'); + +var Token = require('../../tokenizer/token'); + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +function reduceNonAdjacent(tokens, context) { + var options = context.options; + var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; + var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; + var candidates = {}; + var repeated = []; + + for (var i = tokens.length - 1; i >= 0; i--) { + var token = tokens[i]; + + if (token[0] != Token.RULE) { + continue; + } else if (token[2].length === 0) { + continue; + } + + var selectorAsString = serializeRules(token[1]); + var isComplexAndNotSpecial = token[1].length > 1 && + isMergeable(selectorAsString, mergeablePseudoClasses, mergeablePseudoElements); + var wrappedSelectors = wrappedSelectorsFrom(token[1]); + var selectors = isComplexAndNotSpecial ? + [selectorAsString].concat(wrappedSelectors) : + [selectorAsString]; + + for (var j = 0, m = selectors.length; j < m; j++) { + var selector = selectors[j]; + + if (!candidates[selector]) + candidates[selector] = []; + else + repeated.push(selector); + + candidates[selector].push({ + where: i, + list: wrappedSelectors, + isPartial: isComplexAndNotSpecial && j > 0, + isComplex: isComplexAndNotSpecial && j === 0 + }); + } + } + + reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, context); + reduceComplexNonAdjacentCases(tokens, candidates, options, context); +} + +function wrappedSelectorsFrom(list) { + var wrapped = []; + + for (var i = 0; i < list.length; i++) { + wrapped.push([list[i][1]]); + } + + return wrapped; +} + +function reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, context) { + function filterOut(idx, bodies) { + return data[idx].isPartial && bodies.length === 0; + } + + function reduceBody(token, newBody, processedCount, tokenIdx) { + if (!data[processedCount - tokenIdx - 1].isPartial) + token[2] = newBody; + } + + for (var i = 0, l = repeated.length; i < l; i++) { + var selector = repeated[i]; + var data = candidates[selector]; + + reduceSelector(tokens, data, { + filterOut: filterOut, + callback: reduceBody + }, options, context); + } +} + +function reduceComplexNonAdjacentCases(tokens, candidates, options, context) { + var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; + var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; + var localContext = {}; + + function filterOut(idx) { + return localContext.data[idx].where < localContext.intoPosition; + } + + function collectReducedBodies(token, newBody, processedCount, tokenIdx) { + if (tokenIdx === 0) + localContext.reducedBodies.push(newBody); + } + + allSelectors: + for (var complexSelector in candidates) { + var into = candidates[complexSelector]; + if (!into[0].isComplex) + continue; + + var intoPosition = into[into.length - 1].where; + var intoToken = tokens[intoPosition]; + var reducedBodies = []; + + var selectors = isMergeable(complexSelector, mergeablePseudoClasses, mergeablePseudoElements) ? + into[0].list : + [complexSelector]; + + localContext.intoPosition = intoPosition; + localContext.reducedBodies = reducedBodies; + + for (var j = 0, m = selectors.length; j < m; j++) { + var selector = selectors[j]; + var data = candidates[selector]; + + if (data.length < 2) + continue allSelectors; + + localContext.data = data; + + reduceSelector(tokens, data, { + filterOut: filterOut, + callback: collectReducedBodies + }, options, context); + + if (serializeBody(reducedBodies[reducedBodies.length - 1]) != serializeBody(reducedBodies[0])) + continue allSelectors; + } + + intoToken[2] = reducedBodies[0]; + } +} + +function reduceSelector(tokens, data, context, options, outerContext) { + var bodies = []; + var bodiesAsList = []; + var processedTokens = []; + + for (var j = data.length - 1; j >= 0; j--) { + if (context.filterOut(j, bodies)) + continue; + + var where = data[j].where; + var token = tokens[where]; + var clonedBody = cloneArray(token[2]); + + bodies = bodies.concat(clonedBody); + bodiesAsList.push(clonedBody); + processedTokens.push(where); + } + + optimizeProperties(bodies, true, false, outerContext); + + var processedCount = processedTokens.length; + var propertyIdx = bodies.length - 1; + var tokenIdx = processedCount - 1; + + while (tokenIdx >= 0) { + if ((tokenIdx === 0 || (bodies[propertyIdx] && bodiesAsList[tokenIdx].indexOf(bodies[propertyIdx]) > -1)) && propertyIdx > -1) { + propertyIdx--; + continue; + } + + var newBody = bodies.splice(propertyIdx + 1); + context.callback(tokens[processedTokens[tokenIdx]], newBody, processedCount, tokenIdx); + + tokenIdx--; + } +} + +module.exports = reduceNonAdjacent; diff --git a/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-font-at-rules.js b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-font-at-rules.js new file mode 100644 index 000000000..bc85d5d67 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-font-at-rules.js @@ -0,0 +1,30 @@ +var Token = require('../../tokenizer/token'); + +var serializeAll = require('../../writer/one-time').all; + +var FONT_FACE_SCOPE = '@font-face'; + +function removeDuplicateFontAtRules(tokens) { + var fontAtRules = []; + var token; + var key; + var i, l; + + for (i = 0, l = tokens.length; i < l; i++) { + token = tokens[i]; + + if (token[0] != Token.AT_RULE_BLOCK && token[1][0][1] != FONT_FACE_SCOPE) { + continue; + } + + key = serializeAll([token]); + + if (fontAtRules.indexOf(key) > -1) { + token[2] = []; + } else { + fontAtRules.push(key); + } + } +} + +module.exports = removeDuplicateFontAtRules; diff --git a/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-media-queries.js b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-media-queries.js new file mode 100644 index 000000000..2c8f31d9d --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-media-queries.js @@ -0,0 +1,30 @@ +var Token = require('../../tokenizer/token'); + +var serializeAll = require('../../writer/one-time').all; +var serializeRules = require('../../writer/one-time').rules; + +function removeDuplicateMediaQueries(tokens) { + var candidates = {}; + var candidate; + var token; + var key; + var i, l; + + for (i = 0, l = tokens.length; i < l; i++) { + token = tokens[i]; + if (token[0] != Token.NESTED_BLOCK) { + continue; + } + + key = serializeRules(token[1]) + '%' + serializeAll(token[2]); + candidate = candidates[key]; + + if (candidate) { + candidate[2] = []; + } + + candidates[key] = token; + } +} + +module.exports = removeDuplicateMediaQueries; diff --git a/node_modules/clean-css/lib/optimizer/level-2/remove-duplicates.js b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicates.js new file mode 100644 index 000000000..9aa6ace93 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicates.js @@ -0,0 +1,43 @@ +var Token = require('../../tokenizer/token'); + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +function removeDuplicates(tokens) { + var matched = {}; + var moreThanOnce = []; + var id, token; + var body, bodies; + + for (var i = 0, l = tokens.length; i < l; i++) { + token = tokens[i]; + if (token[0] != Token.RULE) + continue; + + id = serializeRules(token[1]); + + if (matched[id] && matched[id].length == 1) + moreThanOnce.push(id); + else + matched[id] = matched[id] || []; + + matched[id].push(i); + } + + for (i = 0, l = moreThanOnce.length; i < l; i++) { + id = moreThanOnce[i]; + bodies = []; + + for (var j = matched[id].length - 1; j >= 0; j--) { + token = tokens[matched[id][j]]; + body = serializeBody(token[2]); + + if (bodies.indexOf(body) > -1) + token[2] = []; + else + bodies.push(body); + } + } +} + +module.exports = removeDuplicates; diff --git a/node_modules/clean-css/lib/optimizer/level-2/reorderable.js b/node_modules/clean-css/lib/optimizer/level-2/reorderable.js new file mode 100644 index 000000000..4a3747a08 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/reorderable.js @@ -0,0 +1,93 @@ +// TODO: it'd be great to merge it with the other canReorder functionality + +var rulesOverlap = require('./rules-overlap'); +var specificitiesOverlap = require('./specificities-overlap'); + +var FLEX_PROPERTIES = /align\-items|box\-align|box\-pack|flex|justify/; +var BORDER_PROPERTIES = /^border\-(top|right|bottom|left|color|style|width|radius)/; + +function canReorder(left, right, cache) { + for (var i = right.length - 1; i >= 0; i--) { + for (var j = left.length - 1; j >= 0; j--) { + if (!canReorderSingle(left[j], right[i], cache)) + return false; + } + } + + return true; +} + +function canReorderSingle(left, right, cache) { + var leftName = left[0]; + var leftValue = left[1]; + var leftNameRoot = left[2]; + var leftSelector = left[5]; + var leftInSpecificSelector = left[6]; + var rightName = right[0]; + var rightValue = right[1]; + var rightNameRoot = right[2]; + var rightSelector = right[5]; + var rightInSpecificSelector = right[6]; + + if (leftName == 'font' && rightName == 'line-height' || rightName == 'font' && leftName == 'line-height') + return false; + if (FLEX_PROPERTIES.test(leftName) && FLEX_PROPERTIES.test(rightName)) + return false; + if (leftNameRoot == rightNameRoot && unprefixed(leftName) == unprefixed(rightName) && (vendorPrefixed(leftName) ^ vendorPrefixed(rightName))) + return false; + if (leftNameRoot == 'border' && BORDER_PROPERTIES.test(rightNameRoot) && (leftName == 'border' || leftName == rightNameRoot || (leftValue != rightValue && sameBorderComponent(leftName, rightName)))) + return false; + if (rightNameRoot == 'border' && BORDER_PROPERTIES.test(leftNameRoot) && (rightName == 'border' || rightName == leftNameRoot || (leftValue != rightValue && sameBorderComponent(leftName, rightName)))) + return false; + if (leftNameRoot == 'border' && rightNameRoot == 'border' && leftName != rightName && (isSideBorder(leftName) && isStyleBorder(rightName) || isStyleBorder(leftName) && isSideBorder(rightName))) + return false; + if (leftNameRoot != rightNameRoot) + return true; + if (leftName == rightName && leftNameRoot == rightNameRoot && (leftValue == rightValue || withDifferentVendorPrefix(leftValue, rightValue))) + return true; + if (leftName != rightName && leftNameRoot == rightNameRoot && leftName != leftNameRoot && rightName != rightNameRoot) + return true; + if (leftName != rightName && leftNameRoot == rightNameRoot && leftValue == rightValue) + return true; + if (rightInSpecificSelector && leftInSpecificSelector && !inheritable(leftNameRoot) && !inheritable(rightNameRoot) && !rulesOverlap(rightSelector, leftSelector, false)) + return true; + if (!specificitiesOverlap(leftSelector, rightSelector, cache)) + return true; + + return false; +} + +function vendorPrefixed(name) { + return /^\-(?:moz|webkit|ms|o)\-/.test(name); +} + +function unprefixed(name) { + return name.replace(/^\-(?:moz|webkit|ms|o)\-/, ''); +} + +function sameBorderComponent(name1, name2) { + return name1.split('-').pop() == name2.split('-').pop(); +} + +function isSideBorder(name) { + return name == 'border-top' || name == 'border-right' || name == 'border-bottom' || name == 'border-left'; +} + +function isStyleBorder(name) { + return name == 'border-color' || name == 'border-style' || name == 'border-width'; +} + +function withDifferentVendorPrefix(value1, value2) { + return vendorPrefixed(value1) && vendorPrefixed(value2) && value1.split('-')[1] != value2.split('-')[2]; +} + +function inheritable(name) { + // According to http://www.w3.org/TR/CSS21/propidx.html + // Others will be catched by other, preceeding rules + return name == 'font' || name == 'line-height' || name == 'list-style'; +} + +module.exports = { + canReorder: canReorder, + canReorderSingle: canReorderSingle +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/restore-with-components.js b/node_modules/clean-css/lib/optimizer/level-2/restore-with-components.js new file mode 100644 index 000000000..caf7c4c94 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/restore-with-components.js @@ -0,0 +1,13 @@ +var compactable = require('./compactable'); + +function restoreWithComponents(property) { + var descriptor = compactable[property.name]; + + if (descriptor && descriptor.shorthand) { + return descriptor.restore(property, compactable); + } else { + return property.value; + } +} + +module.exports = restoreWithComponents; diff --git a/node_modules/clean-css/lib/optimizer/level-2/restore.js b/node_modules/clean-css/lib/optimizer/level-2/restore.js new file mode 100644 index 000000000..149688c5b --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/restore.js @@ -0,0 +1,233 @@ +var shallowClone = require('./clone').shallow; + +var Token = require('../../tokenizer/token'); +var Marker = require('../../tokenizer/marker'); + +function isInheritOnly(values) { + for (var i = 0, l = values.length; i < l; i++) { + var value = values[i][1]; + + if (value != 'inherit' && value != Marker.COMMA && value != Marker.FORWARD_SLASH) + return false; + } + + return true; +} + +function background(property, compactable, lastInMultiplex) { + var components = property.components; + var restored = []; + var needsOne, needsBoth; + + function restoreValue(component) { + Array.prototype.unshift.apply(restored, component.value); + } + + function isDefaultValue(component) { + var descriptor = compactable[component.name]; + + if (descriptor.doubleValues && descriptor.defaultValue.length == 1) { + return component.value[0][1] == descriptor.defaultValue[0] && (component.value[1] ? component.value[1][1] == descriptor.defaultValue[0] : true); + } else if (descriptor.doubleValues && descriptor.defaultValue.length != 1) { + return component.value[0][1] == descriptor.defaultValue[0] && (component.value[1] ? component.value[1][1] : component.value[0][1]) == descriptor.defaultValue[1]; + } else { + return component.value[0][1] == descriptor.defaultValue; + } + } + + for (var i = components.length - 1; i >= 0; i--) { + var component = components[i]; + var isDefault = isDefaultValue(component); + + if (component.name == 'background-clip') { + var originComponent = components[i - 1]; + var isOriginDefault = isDefaultValue(originComponent); + + needsOne = component.value[0][1] == originComponent.value[0][1]; + + needsBoth = !needsOne && ( + (isOriginDefault && !isDefault) || + (!isOriginDefault && !isDefault) || + (!isOriginDefault && isDefault && component.value[0][1] != originComponent.value[0][1])); + + if (needsOne) { + restoreValue(originComponent); + } else if (needsBoth) { + restoreValue(component); + restoreValue(originComponent); + } + + i--; + } else if (component.name == 'background-size') { + var positionComponent = components[i - 1]; + var isPositionDefault = isDefaultValue(positionComponent); + + needsOne = !isPositionDefault && isDefault; + + needsBoth = !needsOne && + (isPositionDefault && !isDefault || !isPositionDefault && !isDefault); + + if (needsOne) { + restoreValue(positionComponent); + } else if (needsBoth) { + restoreValue(component); + restored.unshift([Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]); + restoreValue(positionComponent); + } else if (positionComponent.value.length == 1) { + restoreValue(positionComponent); + } + + i--; + } else { + if (isDefault || compactable[component.name].multiplexLastOnly && !lastInMultiplex) + continue; + + restoreValue(component); + } + } + + if (restored.length === 0 && property.value.length == 1 && property.value[0][1] == '0') + restored.push(property.value[0]); + + if (restored.length === 0) + restored.push([Token.PROPERTY_VALUE, compactable[property.name].defaultValue]); + + if (isInheritOnly(restored)) + return [restored[0]]; + + return restored; +} + +function borderRadius(property, compactable) { + if (property.multiplex) { + var horizontal = shallowClone(property); + var vertical = shallowClone(property); + + for (var i = 0; i < 4; i++) { + var component = property.components[i]; + + var horizontalComponent = shallowClone(property); + horizontalComponent.value = [component.value[0]]; + horizontal.components.push(horizontalComponent); + + var verticalComponent = shallowClone(property); + // FIXME: only shorthand compactor (see breakup#borderRadius) knows that border radius + // longhands have two values, whereas tokenizer does not care about populating 2nd value + // if it's missing, hence this fallback + verticalComponent.value = [component.value[1] || component.value[0]]; + vertical.components.push(verticalComponent); + } + + var horizontalValues = fourValues(horizontal, compactable); + var verticalValues = fourValues(vertical, compactable); + + if (horizontalValues.length == verticalValues.length && + horizontalValues[0][1] == verticalValues[0][1] && + (horizontalValues.length > 1 ? horizontalValues[1][1] == verticalValues[1][1] : true) && + (horizontalValues.length > 2 ? horizontalValues[2][1] == verticalValues[2][1] : true) && + (horizontalValues.length > 3 ? horizontalValues[3][1] == verticalValues[3][1] : true)) { + return horizontalValues; + } else { + return horizontalValues.concat([[Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]]).concat(verticalValues); + } + } else { + return fourValues(property, compactable); + } +} + +function fourValues(property) { + var components = property.components; + var value1 = components[0].value[0]; + var value2 = components[1].value[0]; + var value3 = components[2].value[0]; + var value4 = components[3].value[0]; + + if (value1[1] == value2[1] && value1[1] == value3[1] && value1[1] == value4[1]) { + return [value1]; + } else if (value1[1] == value3[1] && value2[1] == value4[1]) { + return [value1, value2]; + } else if (value2[1] == value4[1]) { + return [value1, value2, value3]; + } else { + return [value1, value2, value3, value4]; + } +} + +function multiplex(restoreWith) { + return function (property, compactable) { + if (!property.multiplex) + return restoreWith(property, compactable, true); + + var multiplexSize = 0; + var restored = []; + var componentMultiplexSoFar = {}; + var i, l; + + // At this point we don't know what's the multiplex size, e.g. how many background layers are there + for (i = 0, l = property.components[0].value.length; i < l; i++) { + if (property.components[0].value[i][1] == Marker.COMMA) + multiplexSize++; + } + + for (i = 0; i <= multiplexSize; i++) { + var _property = shallowClone(property); + + // We split multiplex into parts and restore them one by one + for (var j = 0, m = property.components.length; j < m; j++) { + var componentToClone = property.components[j]; + var _component = shallowClone(componentToClone); + _property.components.push(_component); + + // The trick is some properties has more than one value, so we iterate over values looking for + // a multiplex separator - a comma + for (var k = componentMultiplexSoFar[_component.name] || 0, n = componentToClone.value.length; k < n; k++) { + if (componentToClone.value[k][1] == Marker.COMMA) { + componentMultiplexSoFar[_component.name] = k + 1; + break; + } + + _component.value.push(componentToClone.value[k]); + } + } + + // No we can restore shorthand value + var lastInMultiplex = i == multiplexSize; + var _restored = restoreWith(_property, compactable, lastInMultiplex); + Array.prototype.push.apply(restored, _restored); + + if (i < multiplexSize) + restored.push([Token.PROPERTY_VALUE, Marker.COMMA]); + } + + return restored; + }; +} + +function withoutDefaults(property, compactable) { + var components = property.components; + var restored = []; + + for (var i = components.length - 1; i >= 0; i--) { + var component = components[i]; + var descriptor = compactable[component.name]; + + if (component.value[0][1] != descriptor.defaultValue) + restored.unshift(component.value[0]); + } + + if (restored.length === 0) + restored.push([Token.PROPERTY_VALUE, compactable[property.name].defaultValue]); + + if (isInheritOnly(restored)) + return [restored[0]]; + + return restored; +} + +module.exports = { + background: background, + borderRadius: borderRadius, + fourValues: fourValues, + multiplex: multiplex, + withoutDefaults: withoutDefaults +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/restructure.js b/node_modules/clean-css/lib/optimizer/level-2/restructure.js new file mode 100644 index 000000000..2635415ae --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/restructure.js @@ -0,0 +1,388 @@ +var canReorderSingle = require('./reorderable').canReorderSingle; +var extractProperties = require('./extract-properties'); +var isMergeable = require('./is-mergeable'); +var tidyRuleDuplicates = require('./tidy-rule-duplicates'); + +var Token = require('../../tokenizer/token'); + +var cloneArray = require('../../utils/clone-array'); + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +function naturalSorter(a, b) { + return a > b ? 1 : -1; +} + +function cloneAndMergeSelectors(propertyA, propertyB) { + var cloned = cloneArray(propertyA); + cloned[5] = cloned[5].concat(propertyB[5]); + + return cloned; +} + +function restructure(tokens, context) { + var options = context.options; + var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; + var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; + var mergeLimit = 8191; + var specificityCache = context.cache.specificity; + var movableTokens = {}; + var movedProperties = []; + var multiPropertyMoveCache = {}; + var movedToBeDropped = []; + var maxCombinationsLevel = 2; + var ID_JOIN_CHARACTER = '%'; + + function sendToMultiPropertyMoveCache(position, movedProperty, allFits) { + for (var i = allFits.length - 1; i >= 0; i--) { + var fit = allFits[i][0]; + var id = addToCache(movedProperty, fit); + + if (multiPropertyMoveCache[id].length > 1 && processMultiPropertyMove(position, multiPropertyMoveCache[id])) { + removeAllMatchingFromCache(id); + break; + } + } + } + + function addToCache(movedProperty, fit) { + var id = cacheId(fit); + multiPropertyMoveCache[id] = multiPropertyMoveCache[id] || []; + multiPropertyMoveCache[id].push([movedProperty, fit]); + return id; + } + + function removeAllMatchingFromCache(matchId) { + var matchSelectors = matchId.split(ID_JOIN_CHARACTER); + var forRemoval = []; + var i; + + for (var id in multiPropertyMoveCache) { + var selectors = id.split(ID_JOIN_CHARACTER); + for (i = selectors.length - 1; i >= 0; i--) { + if (matchSelectors.indexOf(selectors[i]) > -1) { + forRemoval.push(id); + break; + } + } + } + + for (i = forRemoval.length - 1; i >= 0; i--) { + delete multiPropertyMoveCache[forRemoval[i]]; + } + } + + function cacheId(cachedTokens) { + var id = []; + for (var i = 0, l = cachedTokens.length; i < l; i++) { + id.push(serializeRules(cachedTokens[i][1])); + } + return id.join(ID_JOIN_CHARACTER); + } + + function tokensToMerge(sourceTokens) { + var uniqueTokensWithBody = []; + var mergeableTokens = []; + + for (var i = sourceTokens.length - 1; i >= 0; i--) { + if (!isMergeable(serializeRules(sourceTokens[i][1]), mergeablePseudoClasses, mergeablePseudoElements)) { + continue; + } + + mergeableTokens.unshift(sourceTokens[i]); + if (sourceTokens[i][2].length > 0 && uniqueTokensWithBody.indexOf(sourceTokens[i]) == -1) + uniqueTokensWithBody.push(sourceTokens[i]); + } + + return uniqueTokensWithBody.length > 1 ? + mergeableTokens : + []; + } + + function shortenIfPossible(position, movedProperty) { + var name = movedProperty[0]; + var value = movedProperty[1]; + var key = movedProperty[4]; + var valueSize = name.length + value.length + 1; + var allSelectors = []; + var qualifiedTokens = []; + + var mergeableTokens = tokensToMerge(movableTokens[key]); + if (mergeableTokens.length < 2) + return; + + var allFits = findAllFits(mergeableTokens, valueSize, 1); + var bestFit = allFits[0]; + if (bestFit[1] > 0) + return sendToMultiPropertyMoveCache(position, movedProperty, allFits); + + for (var i = bestFit[0].length - 1; i >=0; i--) { + allSelectors = bestFit[0][i][1].concat(allSelectors); + qualifiedTokens.unshift(bestFit[0][i]); + } + + allSelectors = tidyRuleDuplicates(allSelectors); + dropAsNewTokenAt(position, [movedProperty], allSelectors, qualifiedTokens); + } + + function fitSorter(fit1, fit2) { + return fit1[1] > fit2[1] ? 1 : (fit1[1] == fit2[1] ? 0 : -1); + } + + function findAllFits(mergeableTokens, propertySize, propertiesCount) { + var combinations = allCombinations(mergeableTokens, propertySize, propertiesCount, maxCombinationsLevel - 1); + return combinations.sort(fitSorter); + } + + function allCombinations(tokensVariant, propertySize, propertiesCount, level) { + var differenceVariants = [[tokensVariant, sizeDifference(tokensVariant, propertySize, propertiesCount)]]; + if (tokensVariant.length > 2 && level > 0) { + for (var i = tokensVariant.length - 1; i >= 0; i--) { + var subVariant = Array.prototype.slice.call(tokensVariant, 0); + subVariant.splice(i, 1); + differenceVariants = differenceVariants.concat(allCombinations(subVariant, propertySize, propertiesCount, level - 1)); + } + } + + return differenceVariants; + } + + function sizeDifference(tokensVariant, propertySize, propertiesCount) { + var allSelectorsSize = 0; + for (var i = tokensVariant.length - 1; i >= 0; i--) { + allSelectorsSize += tokensVariant[i][2].length > propertiesCount ? serializeRules(tokensVariant[i][1]).length : -1; + } + return allSelectorsSize - (tokensVariant.length - 1) * propertySize + 1; + } + + function dropAsNewTokenAt(position, properties, allSelectors, mergeableTokens) { + var i, j, k, m; + var allProperties = []; + + for (i = mergeableTokens.length - 1; i >= 0; i--) { + var mergeableToken = mergeableTokens[i]; + + for (j = mergeableToken[2].length - 1; j >= 0; j--) { + var mergeableProperty = mergeableToken[2][j]; + + for (k = 0, m = properties.length; k < m; k++) { + var property = properties[k]; + + var mergeablePropertyName = mergeableProperty[1][1]; + var propertyName = property[0]; + var propertyBody = property[4]; + if (mergeablePropertyName == propertyName && serializeBody([mergeableProperty]) == propertyBody) { + mergeableToken[2].splice(j, 1); + break; + } + } + } + } + + for (i = properties.length - 1; i >= 0; i--) { + allProperties.unshift(properties[i][3]); + } + + var newToken = [Token.RULE, allSelectors, allProperties]; + tokens.splice(position, 0, newToken); + } + + function dropPropertiesAt(position, movedProperty) { + var key = movedProperty[4]; + var toMove = movableTokens[key]; + + if (toMove && toMove.length > 1) { + if (!shortenMultiMovesIfPossible(position, movedProperty)) + shortenIfPossible(position, movedProperty); + } + } + + function shortenMultiMovesIfPossible(position, movedProperty) { + var candidates = []; + var propertiesAndMergableTokens = []; + var key = movedProperty[4]; + var j, k; + + var mergeableTokens = tokensToMerge(movableTokens[key]); + if (mergeableTokens.length < 2) + return; + + movableLoop: + for (var value in movableTokens) { + var tokensList = movableTokens[value]; + + for (j = mergeableTokens.length - 1; j >= 0; j--) { + if (tokensList.indexOf(mergeableTokens[j]) == -1) + continue movableLoop; + } + + candidates.push(value); + } + + if (candidates.length < 2) + return false; + + for (j = candidates.length - 1; j >= 0; j--) { + for (k = movedProperties.length - 1; k >= 0; k--) { + if (movedProperties[k][4] == candidates[j]) { + propertiesAndMergableTokens.unshift([movedProperties[k], mergeableTokens]); + break; + } + } + } + + return processMultiPropertyMove(position, propertiesAndMergableTokens); + } + + function processMultiPropertyMove(position, propertiesAndMergableTokens) { + var valueSize = 0; + var properties = []; + var property; + + for (var i = propertiesAndMergableTokens.length - 1; i >= 0; i--) { + property = propertiesAndMergableTokens[i][0]; + var fullValue = property[4]; + valueSize += fullValue.length + (i > 0 ? 1 : 0); + + properties.push(property); + } + + var mergeableTokens = propertiesAndMergableTokens[0][1]; + var bestFit = findAllFits(mergeableTokens, valueSize, properties.length)[0]; + if (bestFit[1] > 0) + return false; + + var allSelectors = []; + var qualifiedTokens = []; + for (i = bestFit[0].length - 1; i >= 0; i--) { + allSelectors = bestFit[0][i][1].concat(allSelectors); + qualifiedTokens.unshift(bestFit[0][i]); + } + + allSelectors = tidyRuleDuplicates(allSelectors); + dropAsNewTokenAt(position, properties, allSelectors, qualifiedTokens); + + for (i = properties.length - 1; i >= 0; i--) { + property = properties[i]; + var index = movedProperties.indexOf(property); + + delete movableTokens[property[4]]; + + if (index > -1 && movedToBeDropped.indexOf(index) == -1) + movedToBeDropped.push(index); + } + + return true; + } + + function boundToAnotherPropertyInCurrrentToken(property, movedProperty, token) { + var propertyName = property[0]; + var movedPropertyName = movedProperty[0]; + if (propertyName != movedPropertyName) + return false; + + var key = movedProperty[4]; + var toMove = movableTokens[key]; + return toMove && toMove.indexOf(token) > -1; + } + + for (var i = tokens.length - 1; i >= 0; i--) { + var token = tokens[i]; + var isRule; + var j, k, m; + var samePropertyAt; + + if (token[0] == Token.RULE) { + isRule = true; + } else if (token[0] == Token.NESTED_BLOCK) { + isRule = false; + } else { + continue; + } + + // We cache movedProperties.length as it may change in the loop + var movedCount = movedProperties.length; + + var properties = extractProperties(token); + movedToBeDropped = []; + + var unmovableInCurrentToken = []; + for (j = properties.length - 1; j >= 0; j--) { + for (k = j - 1; k >= 0; k--) { + if (!canReorderSingle(properties[j], properties[k], specificityCache)) { + unmovableInCurrentToken.push(j); + break; + } + } + } + + for (j = properties.length - 1; j >= 0; j--) { + var property = properties[j]; + var movedSameProperty = false; + + for (k = 0; k < movedCount; k++) { + var movedProperty = movedProperties[k]; + + if (movedToBeDropped.indexOf(k) == -1 && (!canReorderSingle(property, movedProperty, specificityCache) && !boundToAnotherPropertyInCurrrentToken(property, movedProperty, token) || + movableTokens[movedProperty[4]] && movableTokens[movedProperty[4]].length === mergeLimit)) { + dropPropertiesAt(i + 1, movedProperty, token); + + if (movedToBeDropped.indexOf(k) == -1) { + movedToBeDropped.push(k); + delete movableTokens[movedProperty[4]]; + } + } + + if (!movedSameProperty) { + movedSameProperty = property[0] == movedProperty[0] && property[1] == movedProperty[1]; + + if (movedSameProperty) { + samePropertyAt = k; + } + } + } + + if (!isRule || unmovableInCurrentToken.indexOf(j) > -1) + continue; + + var key = property[4]; + + if (movedSameProperty && movedProperties[samePropertyAt][5].length + property[5].length > mergeLimit) { + dropPropertiesAt(i + 1, movedProperties[samePropertyAt]); + movedProperties.splice(samePropertyAt, 1); + movableTokens[key] = [token]; + movedSameProperty = false; + } else { + movableTokens[key] = movableTokens[key] || []; + movableTokens[key].push(token); + } + + if (movedSameProperty) { + movedProperties[samePropertyAt] = cloneAndMergeSelectors(movedProperties[samePropertyAt], property); + } else { + movedProperties.push(property); + } + } + + movedToBeDropped = movedToBeDropped.sort(naturalSorter); + for (j = 0, m = movedToBeDropped.length; j < m; j++) { + var dropAt = movedToBeDropped[j] - j; + movedProperties.splice(dropAt, 1); + } + } + + var position = tokens[0] && tokens[0][0] == Token.AT_RULE && tokens[0][1].indexOf('@charset') === 0 ? 1 : 0; + for (; position < tokens.length - 1; position++) { + var isImportRule = tokens[position][0] === Token.AT_RULE && tokens[position][1].indexOf('@import') === 0; + var isComment = tokens[position][0] === Token.COMMENT; + if (!(isImportRule || isComment)) + break; + } + + for (i = 0; i < movedProperties.length; i++) { + dropPropertiesAt(position, movedProperties[i]); + } +} + +module.exports = restructure; diff --git a/node_modules/clean-css/lib/optimizer/level-2/rules-overlap.js b/node_modules/clean-css/lib/optimizer/level-2/rules-overlap.js new file mode 100644 index 000000000..811a517b2 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/rules-overlap.js @@ -0,0 +1,32 @@ +var MODIFIER_PATTERN = /\-\-.+$/; + +function rulesOverlap(rule1, rule2, bemMode) { + var scope1; + var scope2; + var i, l; + var j, m; + + for (i = 0, l = rule1.length; i < l; i++) { + scope1 = rule1[i][1]; + + for (j = 0, m = rule2.length; j < m; j++) { + scope2 = rule2[j][1]; + + if (scope1 == scope2) { + return true; + } + + if (bemMode && withoutModifiers(scope1) == withoutModifiers(scope2)) { + return true; + } + } + } + + return false; +} + +function withoutModifiers(scope) { + return scope.replace(MODIFIER_PATTERN, ''); +} + +module.exports = rulesOverlap; diff --git a/node_modules/clean-css/lib/optimizer/level-2/specificities-overlap.js b/node_modules/clean-css/lib/optimizer/level-2/specificities-overlap.js new file mode 100644 index 000000000..bde037446 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/specificities-overlap.js @@ -0,0 +1,34 @@ +var specificity = require('./specificity'); + +function specificitiesOverlap(selector1, selector2, cache) { + var specificity1; + var specificity2; + var i, l; + var j, m; + + for (i = 0, l = selector1.length; i < l; i++) { + specificity1 = findSpecificity(selector1[i][1], cache); + + for (j = 0, m = selector2.length; j < m; j++) { + specificity2 = findSpecificity(selector2[j][1], cache); + + if (specificity1[0] === specificity2[0] && specificity1[1] === specificity2[1] && specificity1[2] === specificity2[2]) { + return true; + } + } + } + + return false; +} + +function findSpecificity(selector, cache) { + var value; + + if (!(selector in cache)) { + cache[selector] = value = specificity(selector); + } + + return value || cache[selector]; +} + +module.exports = specificitiesOverlap; diff --git a/node_modules/clean-css/lib/optimizer/level-2/specificity.js b/node_modules/clean-css/lib/optimizer/level-2/specificity.js new file mode 100644 index 000000000..bbd224f15 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/specificity.js @@ -0,0 +1,77 @@ +var Marker = require('../../tokenizer/marker'); + +var Selector = { + ADJACENT_SIBLING: '+', + DESCENDANT: '>', + DOT: '.', + HASH: '#', + NON_ADJACENT_SIBLING: '~', + PSEUDO: ':' +}; + +var LETTER_PATTERN = /[a-zA-Z]/; +var NOT_PREFIX = ':not('; +var SEPARATOR_PATTERN = /[\s,\(>~\+]/; + +function specificity(selector) { + var result = [0, 0, 0]; + var character; + var isEscaped; + var isSingleQuoted; + var isDoubleQuoted; + var roundBracketLevel = 0; + var couldIntroduceNewTypeSelector; + var withinNotPseudoClass = false; + var wasPseudoClass = false; + var i, l; + + for (i = 0, l = selector.length; i < l; i++) { + character = selector[i]; + + if (isEscaped) { + // noop + } else if (character == Marker.SINGLE_QUOTE && !isDoubleQuoted && !isSingleQuoted) { + isSingleQuoted = true; + } else if (character == Marker.SINGLE_QUOTE && !isDoubleQuoted && isSingleQuoted) { + isSingleQuoted = false; + } else if (character == Marker.DOUBLE_QUOTE && !isDoubleQuoted && !isSingleQuoted) { + isDoubleQuoted = true; + } else if (character == Marker.DOUBLE_QUOTE && isDoubleQuoted && !isSingleQuoted) { + isDoubleQuoted = false; + } else if (isSingleQuoted || isDoubleQuoted) { + continue; + } else if (roundBracketLevel > 0 && !withinNotPseudoClass) { + // noop + } else if (character == Marker.OPEN_ROUND_BRACKET) { + roundBracketLevel++; + } else if (character == Marker.CLOSE_ROUND_BRACKET && roundBracketLevel == 1) { + roundBracketLevel--; + withinNotPseudoClass = false; + } else if (character == Marker.CLOSE_ROUND_BRACKET) { + roundBracketLevel--; + } else if (character == Selector.HASH) { + result[0]++; + } else if (character == Selector.DOT || character == Marker.OPEN_SQUARE_BRACKET) { + result[1]++; + } else if (character == Selector.PSEUDO && !wasPseudoClass && !isNotPseudoClass(selector, i)) { + result[1]++; + withinNotPseudoClass = false; + } else if (character == Selector.PSEUDO) { + withinNotPseudoClass = true; + } else if ((i === 0 || couldIntroduceNewTypeSelector) && LETTER_PATTERN.test(character)) { + result[2]++; + } + + isEscaped = character == Marker.BACK_SLASH; + wasPseudoClass = character == Selector.PSEUDO; + couldIntroduceNewTypeSelector = !isEscaped && SEPARATOR_PATTERN.test(character); + } + + return result; +} + +function isNotPseudoClass(selector, index) { + return selector.indexOf(NOT_PREFIX, index) === index; +} + +module.exports = specificity; diff --git a/node_modules/clean-css/lib/optimizer/level-2/tidy-rule-duplicates.js b/node_modules/clean-css/lib/optimizer/level-2/tidy-rule-duplicates.js new file mode 100644 index 000000000..30a9c2177 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/tidy-rule-duplicates.js @@ -0,0 +1,21 @@ +function ruleSorter(s1, s2) { + return s1[1] > s2[1] ? 1 : -1; +} + +function tidyRuleDuplicates(rules) { + var list = []; + var repeated = []; + + for (var i = 0, l = rules.length; i < l; i++) { + var rule = rules[i]; + + if (repeated.indexOf(rule[1]) == -1) { + repeated.push(rule[1]); + list.push(rule); + } + } + + return list.sort(ruleSorter); +} + +module.exports = tidyRuleDuplicates; diff --git a/node_modules/clean-css/lib/optimizer/remove-unused.js b/node_modules/clean-css/lib/optimizer/remove-unused.js new file mode 100644 index 000000000..7b90c4079 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/remove-unused.js @@ -0,0 +1,11 @@ +function removeUnused(properties) { + for (var i = properties.length - 1; i >= 0; i--) { + var property = properties[i]; + + if (property.unused) { + property.all.splice(property.position, 1); + } + } +} + +module.exports = removeUnused; diff --git a/node_modules/clean-css/lib/optimizer/restore-from-optimizing.js b/node_modules/clean-css/lib/optimizer/restore-from-optimizing.js new file mode 100644 index 000000000..ebd69c2ac --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/restore-from-optimizing.js @@ -0,0 +1,69 @@ +var Hack = require('./hack'); + +var Marker = require('../tokenizer/marker'); + +var ASTERISK_HACK = '*'; +var BACKSLASH_HACK = '\\'; +var IMPORTANT_TOKEN = '!important'; +var UNDERSCORE_HACK = '_'; +var BANG_HACK = '!ie'; + +function restoreFromOptimizing(properties, restoreCallback) { + var property; + var restored; + var current; + var i; + + for (i = properties.length - 1; i >= 0; i--) { + property = properties[i]; + + if (property.unused) { + continue; + } + + if (!property.dirty && !property.important && !property.hack) { + continue; + } + + if (restoreCallback) { + restored = restoreCallback(property); + property.value = restored; + } else { + restored = property.value; + } + + if (property.important) { + restoreImportant(property); + } + + if (property.hack) { + restoreHack(property); + } + + if ('all' in property) { + current = property.all[property.position]; + current[1][1] = property.name; + + current.splice(2, current.length - 1); + Array.prototype.push.apply(current, restored); + } + } +} + +function restoreImportant(property) { + property.value[property.value.length - 1][1] += IMPORTANT_TOKEN; +} + +function restoreHack(property) { + if (property.hack[0] == Hack.UNDERSCORE) { + property.name = UNDERSCORE_HACK + property.name; + } else if (property.hack[0] == Hack.ASTERISK) { + property.name = ASTERISK_HACK + property.name; + } else if (property.hack[0] == Hack.BACKSLASH) { + property.value[property.value.length - 1][1] += BACKSLASH_HACK + property.hack[1]; + } else if (property.hack[0] == Hack.BANG) { + property.value[property.value.length - 1][1] += Marker.SPACE + BANG_HACK; + } +} + +module.exports = restoreFromOptimizing; diff --git a/node_modules/clean-css/lib/optimizer/validator.js b/node_modules/clean-css/lib/optimizer/validator.js new file mode 100644 index 000000000..35f041ea9 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/validator.js @@ -0,0 +1,470 @@ +var Units = [ + '%', + 'ch', + 'cm', + 'em', + 'ex', + 'in', + 'mm', + 'pc', + 'pt', + 'px', + 'rem', + 'vh', + 'vm', + 'vmax', + 'vmin', + 'vw' +]; +var cssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(' + Units.join('|') + '|)|auto|inherit)'; +var cssCalcRegexStr = '(\\-moz\\-|\\-webkit\\-)?calc\\([^\\)]+\\)'; +var cssFunctionNoVendorRegexStr = '[A-Z]+(\\-|[A-Z]|[0-9])+\\(.*?\\)'; +var cssFunctionVendorRegexStr = '\\-(\\-|[A-Z]|[0-9])+\\(.*?\\)'; +var cssVariableRegexStr = 'var\\(\\-\\-[^\\)]+\\)'; +var cssFunctionAnyRegexStr = '(' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')'; +var cssUnitOrCalcRegexStr = '(' + cssUnitRegexStr + '|' + cssCalcRegexStr + ')'; + +var cssFunctionNoVendorRegex = new RegExp('^' + cssFunctionNoVendorRegexStr + '$', 'i'); +var cssVariableRegex = new RegExp('^' + cssVariableRegexStr + '$', 'i'); +var cssFunctionAnyRegex = new RegExp('^' + cssFunctionAnyRegexStr + '$', 'i'); +var cssUnitRegex = new RegExp('^' + cssUnitRegexStr + '$', 'i'); +var cssUnitOrCalcRegex = new RegExp('^' + cssUnitOrCalcRegexStr + '$', 'i'); + +var urlRegex = /^url\([\s\S]+\)$/i; + +var globalKeywords = [ + 'inherit', + 'initial', + 'unset' +]; + +var Keywords = { + '*-style': [ + 'auto', + 'dashed', + 'dotted', + 'double', + 'groove', + 'hidden', + 'inset', + 'none', + 'outset', + 'ridge', + 'solid' + ], + 'background-attachment': [ + 'fixed', + 'inherit', + 'local', + 'scroll' + ], + 'background-clip': [ + 'border-box', + 'content-box', + 'inherit', + 'padding-box', + 'text' + ], + 'background-origin': [ + 'border-box', + 'content-box', + 'inherit', + 'padding-box' + ], + 'background-position': [ + 'bottom', + 'center', + 'left', + 'right', + 'top' + ], + 'background-repeat': [ + 'no-repeat', + 'inherit', + 'repeat', + 'repeat-x', + 'repeat-y', + 'round', + 'space' + ], + 'background-size': [ + 'auto', + 'cover', + 'contain' + ], + 'border-collapse': [ + 'collapse', + 'inherit', + 'separate' + ], + 'bottom': [ + 'auto' + ], + 'clear': [ + 'both', + 'left', + 'none', + 'right' + ], + 'cursor': [ + 'all-scroll', + 'auto', + 'col-resize', + 'crosshair', + 'default', + 'e-resize', + 'help', + 'move', + 'n-resize', + 'ne-resize', + 'no-drop', + 'not-allowed', + 'nw-resize', + 'pointer', + 'progress', + 'row-resize', + 's-resize', + 'se-resize', + 'sw-resize', + 'text', + 'vertical-text', + 'w-resize', + 'wait' + ], + 'display': [ + 'block', + 'inline', + 'inline-block', + 'inline-table', + 'list-item', + 'none', + 'table', + 'table-caption', + 'table-cell', + 'table-column', + 'table-column-group', + 'table-footer-group', + 'table-header-group', + 'table-row', + 'table-row-group' + ], + 'float': [ + 'left', + 'none', + 'right' + ], + 'left': [ + 'auto' + ], + 'font-style': [ + 'italic', + 'normal', + 'oblique' + ], + 'font-weight': [ + '100', + '200', + '300', + '400', + '500', + '600', + '700', + '800', + '900', + 'bold', + 'bolder', + 'lighter', + 'normal' + ], + 'list-style-position': [ + 'inside', + 'outside' + ], + 'list-style-type': [ + 'armenian', + 'circle', + 'decimal', + 'decimal-leading-zero', + 'disc', + 'decimal|disc', // this is the default value of list-style-type, see comment in compactable.js + 'georgian', + 'lower-alpha', + 'lower-greek', + 'lower-latin', + 'lower-roman', + 'none', + 'square', + 'upper-alpha', + 'upper-latin', + 'upper-roman' + ], + 'overflow': [ + 'auto', + 'hidden', + 'scroll', + 'visible' + ], + 'position': [ + 'absolute', + 'fixed', + 'relative', + 'static' + ], + 'right': [ + 'auto' + ], + 'text-align': [ + 'center', + 'justify', + 'left', + 'left|right', // this is the default value of list-style-type, see comment in compactable.js + 'right' + ], + 'text-decoration': [ + 'line-through', + 'none', + 'overline', + 'underline' + ], + 'text-overflow': [ + 'clip', + 'ellipsis' + ], + 'top': [ + 'auto' + ], + 'vertical-align': [ + 'baseline', + 'bottom', + 'middle', + 'sub', + 'super', + 'text-bottom', + 'text-top', + 'top' + ], + 'visibility': [ + 'collapse', + 'hidden', + 'visible' + ], + 'white-space': [ + 'normal', + 'nowrap', + 'pre' + ], + 'width': [ + 'inherit', + 'initial', + 'medium', + 'thick', + 'thin' + ] +}; + +var VENDOR_PREFIX_PATTERN = /(^|\W)-\w+\-/; + +function areSameFunction(value1, value2) { + if (!isValidFunction(value1) || !isValidFunction(value2)) { + return false; + } + + var function1Name = value1.substring(0, value1.indexOf('(')); + var function2Name = value2.substring(0, value2.indexOf('(')); + + return function1Name === function2Name; +} + +function hasNoVendorPrefix(value) { + return VENDOR_PREFIX_PATTERN.test(value); +} + +function isValidBackgroundAttachment(value) { + return Keywords['background-attachment'].indexOf(value) > -1; +} + +function isValidBackgroundClip(value) { + return Keywords['background-clip'].indexOf(value) > -1; +} + +function isValidBackgroundRepeat(value) { + return Keywords['background-repeat'].indexOf(value) > -1; +} + +function isValidBackgroundOrigin(value) { + return Keywords['background-origin'].indexOf(value) > -1; +} + +function isValidBackgroundPosition(value) { + var parts; + var i, l; + + if (value === 'inherit') { + return true; + } + + parts = value.split(' '); + for (i = 0, l = parts.length; i < l; i++) { + if (parts[i] === '') { + continue; + } else if (isValidBackgroundPositionPart(parts[i])) { + continue; + } + + return false; + } + + return true; +} + +function isValidBackgroundPositionPart(value) { + return Keywords['background-position'].indexOf(value) > -1 || cssUnitOrCalcRegex.test(value); +} + +function isValidBackgroundSizePart(value) { + return Keywords['background-size'].indexOf(value) > -1 || cssUnitRegex.test(value); +} + +function isValidColor(value) { + return isValidNamedColor(value) || + isValidColorValue(value); +} + +function isValidColorValue(value) { + return isValidHexColor(value) || + isValidRgbaColor(value) || + isValidHslaColor(value); +} + +function isValidFunction(value) { + return !urlRegex.test(value) && cssFunctionAnyRegex.test(value); +} + +function isValidFunctionWithoutVendorPrefix(value) { + return !urlRegex.test(value) && cssFunctionNoVendorRegex.test(value); +} + +function isValidGlobalValue(value) { + return globalKeywords.indexOf(value) > -1; +} + +function isValidHexColor(value) { + return (value.length === 4 || value.length === 7) && value[0] === '#'; +} + +function isValidHslaColor(value) { + return value.length > 0 && value.indexOf('hsla(') === 0 && value.indexOf(')') === value.length - 1; +} + +function isValidImage(value) { + return value == 'none' || value == 'inherit' || isValidUrl(value); +} + +function isValidKeywordValue(propertyName, value, includeGlobal) { + return Keywords[propertyName].indexOf(value) > -1 || includeGlobal && isValidGlobalValue(value); +} + +function isValidListStyleType(value) { + return Keywords['list-style-type'].indexOf(value) > -1; +} + +function isValidListStylePosition(value) { + return Keywords['list-style-position'].indexOf(value) > -1; +} + +function isValidNamedColor(value) { + // We don't really check if it's a valid color value, but allow any letters in it + return value !== 'auto' && (value === 'transparent' || value === 'inherit' || /^[a-zA-Z]+$/.test(value)); +} + +function isValidRgbaColor(value) { + return value.length > 0 && value.indexOf('rgba(') === 0 && value.indexOf(')') === value.length - 1; +} + +function isValidStyle(value) { + return Keywords['*-style'].indexOf(value) > -1; +} + +function isValidTextShadow(compatibleCssUnitRegex, value) { + return isValidUnitWithoutFunction(compatibleCssUnitRegex, value) || + isValidColor(value) || + isValidGlobalValue(value); +} + +function isValidUnit(compatibleCssUnitAnyRegex, value) { + return compatibleCssUnitAnyRegex.test(value); +} + +function isValidUnitWithoutFunction(compatibleCssUnitRegex, value) { + return compatibleCssUnitRegex.test(value); +} + +function isValidUrl(value) { + return urlRegex.test(value); +} + +function isValidVariable(value) { + return cssVariableRegex.test(value); +} + +function isValidVendorPrefixedValue(value) { + return /^-([A-Za-z0-9]|-)*$/gi.test(value); +} + +function isValidWidth(compatibleCssUnitRegex, value) { + return isValidUnit(compatibleCssUnitRegex, value) || Keywords.width.indexOf(value) > -1; +} + +function isValidZIndex(value) { + return value == 'auto' || + isValidGlobalValue(value) || + value.length > 0 && value == ('' + parseInt(value)); +} + +function validator(compatibility) { + var validUnits = Units.slice(0).filter(function (value) { + return !(value in compatibility.units) || compatibility.units[value] === true; + }); + + var compatibleCssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(' + validUnits.join('|') + '|)|auto|inherit)'; + var compatibleCssUnitRegex = new RegExp('^' + compatibleCssUnitRegexStr + '$', 'i'); + var compatibleCssUnitAnyRegex = new RegExp('^(none|' + Keywords.width.join('|') + '|' + compatibleCssUnitRegexStr + '|' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')$', 'i'); + var colorOpacity = compatibility.colors.opacity; + + return { + areSameFunction: areSameFunction, + colorOpacity: colorOpacity, + hasNoVendorPrefix: hasNoVendorPrefix, + isValidBackgroundAttachment: isValidBackgroundAttachment, + isValidBackgroundClip: isValidBackgroundClip, + isValidBackgroundOrigin: isValidBackgroundOrigin, + isValidBackgroundPosition: isValidBackgroundPosition, + isValidBackgroundPositionPart: isValidBackgroundPositionPart, + isValidBackgroundRepeat: isValidBackgroundRepeat, + isValidBackgroundSizePart: isValidBackgroundSizePart, + isValidColor: isValidColor, + isValidColorValue: isValidColorValue, + isValidFunction: isValidFunction, + isValidFunctionWithoutVendorPrefix: isValidFunctionWithoutVendorPrefix, + isValidGlobalValue: isValidGlobalValue, + isValidHexColor: isValidHexColor, + isValidHslaColor: isValidHslaColor, + isValidImage: isValidImage, + isValidKeywordValue: isValidKeywordValue, + isValidListStylePosition: isValidListStylePosition, + isValidListStyleType: isValidListStyleType, + isValidNamedColor: isValidNamedColor, + isValidRgbaColor: isValidRgbaColor, + isValidStyle: isValidStyle, + isValidTextShadow: isValidTextShadow.bind(null, compatibleCssUnitRegex), + isValidUnit: isValidUnit.bind(null, compatibleCssUnitAnyRegex), + isValidUnitWithoutFunction: isValidUnitWithoutFunction.bind(null, compatibleCssUnitRegex), + isValidUrl: isValidUrl, + isValidVariable: isValidVariable, + isValidVendorPrefixedValue: isValidVendorPrefixedValue, + isValidWidth: isValidWidth.bind(null, compatibleCssUnitRegex), + isValidZIndex: isValidZIndex + }; +} + +module.exports = validator; diff --git a/node_modules/clean-css/lib/optimizer/wrap-for-optimizing.js b/node_modules/clean-css/lib/optimizer/wrap-for-optimizing.js new file mode 100644 index 000000000..0fbfdb056 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/wrap-for-optimizing.js @@ -0,0 +1,187 @@ +var Hack = require('./hack'); + +var Marker = require('../tokenizer/marker'); +var Token = require('../tokenizer/token'); + +var Match = { + ASTERISK: '*', + BACKSLASH: '\\', + BANG: '!', + BANG_SUFFIX_PATTERN: /!\w+$/, + IMPORTANT_TOKEN: '!important', + IMPORTANT_TOKEN_PATTERN: new RegExp('!important$', 'i'), + IMPORTANT_WORD: 'important', + IMPORTANT_WORD_PATTERN: new RegExp('important$', 'i'), + SUFFIX_BANG_PATTERN: /!$/, + UNDERSCORE: '_', + VARIABLE_REFERENCE_PATTERN: /var\(--.+\)$/ +}; + +function wrapAll(properties, includeVariable) { + var wrapped = []; + var single; + var property; + var i; + + for (i = properties.length - 1; i >= 0; i--) { + property = properties[i]; + + if (property[0] != Token.PROPERTY) { + continue; + } + + if (!includeVariable && someVariableReferences(property)) { + continue; + } + + single = wrapSingle(property); + single.all = properties; + single.position = i; + wrapped.unshift(single); + } + + return wrapped; +} + +function someVariableReferences(property) { + var i, l; + var value; + + // skipping `property` and property name tokens + for (i = 2, l = property.length; i < l; i++) { + value = property[i]; + + if (value[0] != Token.PROPERTY_VALUE) { + continue; + } + + if (isVariableReference(value[1])) { + return true; + } + } + + return false; +} + +function isVariableReference(value) { + return Match.VARIABLE_REFERENCE_PATTERN.test(value); +} + +function isMultiplex(property) { + var value; + var i, l; + + for (i = 3, l = property.length; i < l; i++) { + value = property[i]; + + if (value[0] == Token.PROPERTY_VALUE && (value[1] == Marker.COMMA || value[1] == Marker.FORWARD_SLASH)) { + return true; + } + } + + return false; +} + +function hackFrom(property) { + var match = false; + var name = property[1][1]; + var lastValue = property[property.length - 1]; + + if (name[0] == Match.UNDERSCORE) { + match = [Hack.UNDERSCORE]; + } else if (name[0] == Match.ASTERISK) { + match = [Hack.ASTERISK]; + } else if (lastValue[1][0] == Match.BANG && !lastValue[1].match(Match.IMPORTANT_WORD_PATTERN)) { + match = [Hack.BANG]; + } else if (lastValue[1].indexOf(Match.BANG) > 0 && !lastValue[1].match(Match.IMPORTANT_WORD_PATTERN) && Match.BANG_SUFFIX_PATTERN.test(lastValue[1])) { + match = [Hack.BANG]; + } else if (lastValue[1].indexOf(Match.BACKSLASH) > 0 && lastValue[1].indexOf(Match.BACKSLASH) == lastValue[1].length - Match.BACKSLASH.length - 1) { + match = [Hack.BACKSLASH, lastValue[1].substring(lastValue[1].indexOf(Match.BACKSLASH) + 1)]; + } else if (lastValue[1].indexOf(Match.BACKSLASH) === 0 && lastValue[1].length == 2) { + match = [Hack.BACKSLASH, lastValue[1].substring(1)]; + } + + return match; +} + +function isImportant(property) { + if (property.length < 3) + return false; + + var lastValue = property[property.length - 1]; + if (Match.IMPORTANT_TOKEN_PATTERN.test(lastValue[1])) { + return true; + } else if (Match.IMPORTANT_WORD_PATTERN.test(lastValue[1]) && Match.SUFFIX_BANG_PATTERN.test(property[property.length - 2][1])) { + return true; + } + + return false; +} + +function stripImportant(property) { + var lastValue = property[property.length - 1]; + var oneButLastValue = property[property.length - 2]; + + if (Match.IMPORTANT_TOKEN_PATTERN.test(lastValue[1])) { + lastValue[1] = lastValue[1].replace(Match.IMPORTANT_TOKEN_PATTERN, ''); + } else { + lastValue[1] = lastValue[1].replace(Match.IMPORTANT_WORD_PATTERN, ''); + oneButLastValue[1] = oneButLastValue[1].replace(Match.SUFFIX_BANG_PATTERN, ''); + } + + if (lastValue[1].length === 0) { + property.pop(); + } + + if (oneButLastValue[1].length === 0) { + property.pop(); + } +} + +function stripPrefixHack(property) { + property[1][1] = property[1][1].substring(1); +} + +function stripSuffixHack(property, hackFrom) { + var lastValue = property[property.length - 1]; + lastValue[1] = lastValue[1] + .substring(0, lastValue[1].indexOf(hackFrom[0] == Hack.BACKSLASH ? Match.BACKSLASH : Match.BANG)) + .trim(); + + if (lastValue[1].length === 0) { + property.pop(); + } +} + +function wrapSingle(property) { + var importantProperty = isImportant(property); + if (importantProperty) { + stripImportant(property); + } + + var whichHack = hackFrom(property); + if (whichHack[0] == Hack.ASTERISK || whichHack[0] == Hack.UNDERSCORE) { + stripPrefixHack(property); + } else if (whichHack[0] == Hack.BACKSLASH || whichHack[0] == Hack.BANG) { + stripSuffixHack(property, whichHack); + } + + return { + block: property[2] && property[2][0] == Token.PROPERTY_BLOCK, + components: [], + dirty: false, + hack: whichHack, + important: importantProperty, + name: property[1][1], + multiplex: property.length > 3 ? isMultiplex(property) : false, + position: 0, + shorthand: false, + unused: false, + value: property.slice(2) + }; +} + +module.exports = { + all: wrapAll, + single: wrapSingle +}; diff --git a/node_modules/clean-css/lib/options/compatibility.js b/node_modules/clean-css/lib/options/compatibility.js new file mode 100644 index 000000000..ca881c627 --- /dev/null +++ b/node_modules/clean-css/lib/options/compatibility.js @@ -0,0 +1,181 @@ +var DEFAULTS = { + '*': { + colors: { + opacity: true // rgba / hsla + }, + properties: { + backgroundClipMerging: true, // background-clip to shorthand + backgroundOriginMerging: true, // background-origin to shorthand + backgroundSizeMerging: true, // background-size to shorthand + colors: true, // any kind of color transformations, like `#ff00ff` to `#f0f` or `#fff` into `red` + ieBangHack: false, // !ie suffix hacks on IE<8 + ieFilters: false, // whether to preserve `filter` and `-ms-filter` properties + iePrefixHack: false, // underscore / asterisk prefix hacks on IE + ieSuffixHack: false, // \9 suffix hacks on IE6-9 + merging: true, // merging properties into one + shorterLengthUnits: false, // optimize pixel units into `pt`, `pc` or `in` units + spaceAfterClosingBrace: true, // 'url() no-repeat' to 'url()no-repeat' + urlQuotes: false, // whether to wrap content of `url()` into quotes or not + zeroUnits: true // 0[unit] -> 0 + }, + selectors: { + adjacentSpace: false, // div+ nav Android stock browser hack + ie7Hack: false, // *+html hack + mergeablePseudoClasses: [ + ':active', + ':after', + ':before', + ':empty', + ':checked', + ':disabled', + ':empty', + ':enabled', + ':first-child', + ':first-letter', + ':first-line', + ':first-of-type', + ':focus', + ':hover', + ':lang', + ':last-child', + ':last-of-type', + ':link', + ':not', + ':nth-child', + ':nth-last-child', + ':nth-last-of-type', + ':nth-of-type', + ':only-child', + ':only-of-type', + ':root', + ':target', + ':visited' + ], // selectors with these pseudo-classes can be merged as these are universally supported + mergeablePseudoElements: [ + '::after', + '::before', + '::first-letter', + '::first-line' + ] // selectors with these pseudo-elements can be merged as these are universally supported + }, + units: { + ch: true, + in: true, + pc: true, + pt: true, + rem: true, + vh: true, + vm: true, // vm is vmin on IE9+ see https://developer.mozilla.org/en-US/docs/Web/CSS/length + vmax: true, + vmin: true, + vw: true + } + } +}; + +DEFAULTS.ie11 = DEFAULTS['*']; + +DEFAULTS.ie10 = DEFAULTS['*']; + +DEFAULTS.ie9 = merge(DEFAULTS['*'], { + properties: { + ieFilters: true, + ieSuffixHack: true + } +}); + +DEFAULTS.ie8 = merge(DEFAULTS.ie9, { + colors: { + opacity: false + }, + properties: { + backgroundClipMerging: false, + backgroundOriginMerging: false, + backgroundSizeMerging: false, + iePrefixHack: true, + merging: false + }, + selectors: { + mergeablePseudoClasses: [ + ':after', + ':before', + ':first-child', + ':first-letter', + ':focus', + ':hover', + ':visited' + ], + mergeablePseudoElements: [] + }, + units: { + ch: false, + rem: false, + vh: false, + vm: false, + vmax: false, + vmin: false, + vw: false + } +}); + +DEFAULTS.ie7 = merge(DEFAULTS.ie8, { + properties: { + ieBangHack: true + }, + selectors: { + ie7Hack: true, + mergeablePseudoClasses: [ + ':first-child', + ':first-letter', + ':hover', + ':visited' + ] + }, +}); + +function compatibilityFrom(source) { + return merge(DEFAULTS['*'], calculateSource(source)); +} + +function merge(source, target) { + for (var key in source) { + var value = source[key]; + + if (typeof value === 'object' && !Array.isArray(value)) { + target[key] = merge(value, target[key] || {}); + } else { + target[key] = key in target ? target[key] : value; + } + } + + return target; +} + +function calculateSource(source) { + if (typeof source == 'object') + return source; + + if (!/[,\+\-]/.test(source)) + return DEFAULTS[source] || DEFAULTS['*']; + + var parts = source.split(','); + var template = parts[0] in DEFAULTS ? + DEFAULTS[parts.shift()] : + DEFAULTS['*']; + + source = {}; + + parts.forEach(function (part) { + var isAdd = part[0] == '+'; + var key = part.substring(1).split('.'); + var group = key[0]; + var option = key[1]; + + source[group] = source[group] || {}; + source[group][option] = isAdd; + }); + + return merge(template, source); +} + +module.exports = compatibilityFrom; diff --git a/node_modules/clean-css/lib/options/format.js b/node_modules/clean-css/lib/options/format.js new file mode 100644 index 000000000..afcf5c967 --- /dev/null +++ b/node_modules/clean-css/lib/options/format.js @@ -0,0 +1,185 @@ +var override = require('../utils/override'); + +var Breaks = { + AfterAtRule: 'afterAtRule', + AfterBlockBegins: 'afterBlockBegins', + AfterBlockEnds: 'afterBlockEnds', + AfterComment: 'afterComment', + AfterProperty: 'afterProperty', + AfterRuleBegins: 'afterRuleBegins', + AfterRuleEnds: 'afterRuleEnds', + BeforeBlockEnds: 'beforeBlockEnds', + BetweenSelectors: 'betweenSelectors' +}; + +var IndentWith = { + Space: ' ', + Tab: '\t' +}; + +var Spaces = { + AroundSelectorRelation: 'aroundSelectorRelation', + BeforeBlockBegins: 'beforeBlockBegins', + BeforeValue: 'beforeValue' +}; + +var DEFAULTS = { + breaks: breaks(false), + indentBy: 0, + indentWith: IndentWith.Space, + spaces: spaces(false), + wrapAt: false +}; + +var BEAUTIFY_ALIAS = 'beautify'; +var KEEP_BREAKS_ALIAS = 'keep-breaks'; + +var OPTION_SEPARATOR = ';'; +var OPTION_NAME_VALUE_SEPARATOR = ':'; +var HASH_VALUES_OPTION_SEPARATOR = ','; +var HASH_VALUES_NAME_VALUE_SEPARATOR = '='; + +var FALSE_KEYWORD_1 = 'false'; +var FALSE_KEYWORD_2 = 'off'; +var TRUE_KEYWORD_1 = 'true'; +var TRUE_KEYWORD_2 = 'on'; + +function breaks(value) { + var breakOptions = {}; + + breakOptions[Breaks.AfterAtRule] = value; + breakOptions[Breaks.AfterBlockBegins] = value; + breakOptions[Breaks.AfterBlockEnds] = value; + breakOptions[Breaks.AfterComment] = value; + breakOptions[Breaks.AfterProperty] = value; + breakOptions[Breaks.AfterRuleBegins] = value; + breakOptions[Breaks.AfterRuleEnds] = value; + breakOptions[Breaks.BeforeBlockEnds] = value; + breakOptions[Breaks.BetweenSelectors] = value; + + return breakOptions; +} + +function spaces(value) { + var spaceOptions = {}; + + spaceOptions[Spaces.AroundSelectorRelation] = value; + spaceOptions[Spaces.BeforeBlockBegins] = value; + spaceOptions[Spaces.BeforeValue] = value; + + return spaceOptions; +} + +function formatFrom(source) { + if (source === undefined || source === false) { + return false; + } + + if (typeof source == 'object' && 'indentBy' in source) { + source = override(source, { indentBy: parseInt(source.indentBy) }); + } + + if (typeof source == 'object' && 'indentWith' in source) { + source = override(source, { indentWith: mapIndentWith(source.indentWith) }); + } + + if (typeof source == 'object') { + return override(DEFAULTS, source); + } + + if (typeof source == 'object') { + return override(DEFAULTS, source); + } + + if (typeof source == 'string' && source == BEAUTIFY_ALIAS) { + return override(DEFAULTS, { + breaks: breaks(true), + indentBy: 2, + spaces: spaces(true) + }); + } + + if (typeof source == 'string' && source == KEEP_BREAKS_ALIAS) { + return override(DEFAULTS, { + breaks: { + afterAtRule: true, + afterBlockBegins: true, + afterBlockEnds: true, + afterComment: true, + afterRuleEnds: true, + beforeBlockEnds: true + } + }); + } + + if (typeof source == 'string') { + return override(DEFAULTS, toHash(source)); + } + + return DEFAULTS; +} + +function toHash(string) { + return string + .split(OPTION_SEPARATOR) + .reduce(function (accumulator, directive) { + var parts = directive.split(OPTION_NAME_VALUE_SEPARATOR); + var name = parts[0]; + var value = parts[1]; + + if (name == 'breaks' || name == 'spaces') { + accumulator[name] = hashValuesToHash(value); + } else if (name == 'indentBy' || name == 'wrapAt') { + accumulator[name] = parseInt(value); + } else if (name == 'indentWith') { + accumulator[name] = mapIndentWith(value); + } + + return accumulator; + }, {}); +} + +function hashValuesToHash(string) { + return string + .split(HASH_VALUES_OPTION_SEPARATOR) + .reduce(function (accumulator, directive) { + var parts = directive.split(HASH_VALUES_NAME_VALUE_SEPARATOR); + var name = parts[0]; + var value = parts[1]; + + accumulator[name] = normalizeValue(value); + + return accumulator; + }, {}); +} + + +function normalizeValue(value) { + switch (value) { + case FALSE_KEYWORD_1: + case FALSE_KEYWORD_2: + return false; + case TRUE_KEYWORD_1: + case TRUE_KEYWORD_2: + return true; + default: + return value; + } +} + +function mapIndentWith(value) { + switch (value) { + case 'space': + return IndentWith.Space; + case 'tab': + return IndentWith.Tab; + default: + return value; + } +} + +module.exports = { + Breaks: Breaks, + Spaces: Spaces, + formatFrom: formatFrom +}; diff --git a/node_modules/clean-css/lib/options/inline-request.js b/node_modules/clean-css/lib/options/inline-request.js new file mode 100644 index 000000000..1e14c6393 --- /dev/null +++ b/node_modules/clean-css/lib/options/inline-request.js @@ -0,0 +1,22 @@ +var url = require('url'); + +var override = require('../utils/override'); + +function inlineRequestFrom(option) { + return override( + /* jshint camelcase: false */ + proxyOptionsFrom(process.env.HTTP_PROXY || process.env.http_proxy), + option || {} + ); +} + +function proxyOptionsFrom(httpProxy) { + return httpProxy ? + { + hostname: url.parse(httpProxy).hostname, + port: parseInt(url.parse(httpProxy).port) + } : + {}; +} + +module.exports = inlineRequestFrom; diff --git a/node_modules/clean-css/lib/options/inline-timeout.js b/node_modules/clean-css/lib/options/inline-timeout.js new file mode 100644 index 000000000..c7fb454d5 --- /dev/null +++ b/node_modules/clean-css/lib/options/inline-timeout.js @@ -0,0 +1,7 @@ +var DEFAULT_TIMEOUT = 5000; + +function inlineTimeoutFrom(option) { + return option || DEFAULT_TIMEOUT; +} + +module.exports = inlineTimeoutFrom; diff --git a/node_modules/clean-css/lib/options/inline.js b/node_modules/clean-css/lib/options/inline.js new file mode 100644 index 000000000..4a6997075 --- /dev/null +++ b/node_modules/clean-css/lib/options/inline.js @@ -0,0 +1,11 @@ +function inlineOptionsFrom(rules) { + if (Array.isArray(rules)) { + return rules; + } + + return undefined === rules ? + ['local'] : + rules.split(','); +} + +module.exports = inlineOptionsFrom; diff --git a/node_modules/clean-css/lib/options/optimization-level.js b/node_modules/clean-css/lib/options/optimization-level.js new file mode 100644 index 000000000..d9828287b --- /dev/null +++ b/node_modules/clean-css/lib/options/optimization-level.js @@ -0,0 +1,213 @@ +var roundingPrecisionFrom = require('./rounding-precision').roundingPrecisionFrom; + +var override = require('../utils/override'); + +var OptimizationLevel = { + Zero: '0', + One: '1', + Two: '2' +}; + +var DEFAULTS = {}; + +DEFAULTS[OptimizationLevel.Zero] = {}; +DEFAULTS[OptimizationLevel.One] = { + cleanupCharsets: true, + normalizeUrls: true, + optimizeBackground: true, + optimizeBorderRadius: true, + optimizeFilter: true, + optimizeFont: true, + optimizeFontWeight: true, + optimizeOutline: true, + removeNegativePaddings: true, + removeQuotes: true, + removeWhitespace: true, + replaceMultipleZeros: true, + replaceTimeUnits: true, + replaceZeroUnits: true, + roundingPrecision: roundingPrecisionFrom(undefined), + selectorsSortingMethod: 'standard', + specialComments: 'all', + tidyAtRules: true, + tidyBlockScopes: true, + tidySelectors: true, + transform: noop +}; +DEFAULTS[OptimizationLevel.Two] = { + mergeAdjacentRules: true, + mergeIntoShorthands: true, + mergeMedia: true, + mergeNonAdjacentRules: true, + mergeSemantically: false, + overrideProperties: true, + reduceNonAdjacentRules: true, + removeDuplicateFontRules: true, + removeDuplicateMediaBlocks: true, + removeDuplicateRules: true, + restructureRules: false +}; + +var ALL_KEYWORD_1 = '*'; +var ALL_KEYWORD_2 = 'all'; +var FALSE_KEYWORD_1 = 'false'; +var FALSE_KEYWORD_2 = 'off'; +var TRUE_KEYWORD_1 = 'true'; +var TRUE_KEYWORD_2 = 'on'; + +var OPTION_SEPARATOR = ';'; +var OPTION_VALUE_SEPARATOR = ':'; + +function noop() {} + +function optimizationLevelFrom(source) { + var level = override(DEFAULTS, {}); + var Zero = OptimizationLevel.Zero; + var One = OptimizationLevel.One; + var Two = OptimizationLevel.Two; + + + if (undefined === source) { + delete level[Two]; + return level; + } + + if (typeof source == 'string') { + source = parseInt(source); + } + + if (typeof source == 'number' && source === parseInt(Two)) { + return level; + } + + if (typeof source == 'number' && source === parseInt(One)) { + delete level[Two]; + return level; + } + + if (typeof source == 'number' && source === parseInt(Zero)) { + delete level[Two]; + delete level[One]; + return level; + } + + if (typeof source == 'object') { + source = covertValuesToHashes(source); + } + + if (One in source && 'roundingPrecision' in source[One]) { + source[One].roundingPrecision = roundingPrecisionFrom(source[One].roundingPrecision); + } + + if (Zero in source || One in source || Two in source) { + level[Zero] = override(level[Zero], source[Zero]); + } + + if (One in source && ALL_KEYWORD_1 in source[One]) { + level[One] = override(level[One], defaults(One, normalizeValue(source[One][ALL_KEYWORD_1]))); + delete source[One][ALL_KEYWORD_1]; + } + + if (One in source && ALL_KEYWORD_2 in source[One]) { + level[One] = override(level[One], defaults(One, normalizeValue(source[One][ALL_KEYWORD_2]))); + delete source[One][ALL_KEYWORD_2]; + } + + if (One in source || Two in source) { + level[One] = override(level[One], source[One]); + } else { + delete level[One]; + } + + if (Two in source && ALL_KEYWORD_1 in source[Two]) { + level[Two] = override(level[Two], defaults(Two, normalizeValue(source[Two][ALL_KEYWORD_1]))); + delete source[Two][ALL_KEYWORD_1]; + } + + if (Two in source && ALL_KEYWORD_2 in source[Two]) { + level[Two] = override(level[Two], defaults(Two, normalizeValue(source[Two][ALL_KEYWORD_2]))); + delete source[Two][ALL_KEYWORD_2]; + } + + if (Two in source) { + level[Two] = override(level[Two], source[Two]); + } else { + delete level[Two]; + } + + return level; +} + +function defaults(level, value) { + var options = override(DEFAULTS[level], {}); + var key; + + for (key in options) { + if (typeof options[key] == 'boolean') { + options[key] = value; + } + } + + return options; +} + +function normalizeValue(value) { + switch (value) { + case FALSE_KEYWORD_1: + case FALSE_KEYWORD_2: + return false; + case TRUE_KEYWORD_1: + case TRUE_KEYWORD_2: + return true; + default: + return value; + } +} + +function covertValuesToHashes(source) { + var clonedSource = override(source, {}); + var level; + var i; + + for (i = 0; i <= 2; i++) { + level = '' + i; + + if (level in clonedSource && (clonedSource[level] === undefined || clonedSource[level] === false)) { + delete clonedSource[level]; + } + + if (level in clonedSource && clonedSource[level] === true) { + clonedSource[level] = {}; + } + + if (level in clonedSource && typeof clonedSource[level] == 'string') { + clonedSource[level] = covertToHash(clonedSource[level], level); + } + } + + return clonedSource; +} + +function covertToHash(asString, level) { + return asString + .split(OPTION_SEPARATOR) + .reduce(function (accumulator, directive) { + var parts = directive.split(OPTION_VALUE_SEPARATOR); + var name = parts[0]; + var value = parts[1]; + var normalizedValue = normalizeValue(value); + + if (ALL_KEYWORD_1 == name || ALL_KEYWORD_2 == name) { + accumulator = override(accumulator, defaults(level, normalizedValue)); + } else { + accumulator[name] = normalizedValue; + } + + return accumulator; + }, {}); +} + +module.exports = { + OptimizationLevel: OptimizationLevel, + optimizationLevelFrom: optimizationLevelFrom, +}; diff --git a/node_modules/clean-css/lib/options/rebase-to.js b/node_modules/clean-css/lib/options/rebase-to.js new file mode 100644 index 000000000..134b4a382 --- /dev/null +++ b/node_modules/clean-css/lib/options/rebase-to.js @@ -0,0 +1,7 @@ +var path = require('path'); + +function rebaseToFrom(option) { + return option ? path.resolve(option) : process.cwd(); +} + +module.exports = rebaseToFrom; diff --git a/node_modules/clean-css/lib/options/rebase.js b/node_modules/clean-css/lib/options/rebase.js new file mode 100644 index 000000000..999c83f50 --- /dev/null +++ b/node_modules/clean-css/lib/options/rebase.js @@ -0,0 +1,5 @@ +function rebaseFrom(rebaseOption) { + return undefined === rebaseOption ? true : !!rebaseOption; +} + +module.exports = rebaseFrom; diff --git a/node_modules/clean-css/lib/options/rounding-precision.js b/node_modules/clean-css/lib/options/rounding-precision.js new file mode 100644 index 000000000..42ecf1b4d --- /dev/null +++ b/node_modules/clean-css/lib/options/rounding-precision.js @@ -0,0 +1,88 @@ +var override = require('../utils/override'); + +var INTEGER_PATTERN = /^\d+$/; + +var ALL_UNITS = ['*', 'all']; +var DEFAULT_PRECISION = 'off'; // all precision changes are disabled +var DIRECTIVES_SEPARATOR = ','; // e.g. *=5,px=3 +var DIRECTIVE_VALUE_SEPARATOR = '='; // e.g. *=5 + +function roundingPrecisionFrom(source) { + return override(defaults(DEFAULT_PRECISION), buildPrecisionFrom(source)); +} + +function defaults(value) { + return { + 'ch': value, + 'cm': value, + 'em': value, + 'ex': value, + 'in': value, + 'mm': value, + 'pc': value, + 'pt': value, + 'px': value, + 'q': value, + 'rem': value, + 'vh': value, + 'vmax': value, + 'vmin': value, + 'vw': value, + '%': value + }; +} + +function buildPrecisionFrom(source) { + if (source === null || source === undefined) { + return {}; + } + + if (typeof source == 'boolean') { + return {}; + } + + if (typeof source == 'number' && source == -1) { + return defaults(DEFAULT_PRECISION); + } + + if (typeof source == 'number') { + return defaults(source); + } + + if (typeof source == 'string' && INTEGER_PATTERN.test(source)) { + return defaults(parseInt(source)); + } + + if (typeof source == 'string' && source == DEFAULT_PRECISION) { + return defaults(DEFAULT_PRECISION); + } + + if (typeof source == 'object') { + return source; + } + + return source + .split(DIRECTIVES_SEPARATOR) + .reduce(function (accumulator, directive) { + var directiveParts = directive.split(DIRECTIVE_VALUE_SEPARATOR); + var name = directiveParts[0]; + var value = parseInt(directiveParts[1]); + + if (isNaN(value) || value == -1) { + value = DEFAULT_PRECISION; + } + + if (ALL_UNITS.indexOf(name) > -1) { + accumulator = override(accumulator, defaults(value)); + } else { + accumulator[name] = value; + } + + return accumulator; + }, {}); +} + +module.exports = { + DEFAULT: DEFAULT_PRECISION, + roundingPrecisionFrom: roundingPrecisionFrom +}; diff --git a/node_modules/clean-css/lib/reader/apply-source-maps.js b/node_modules/clean-css/lib/reader/apply-source-maps.js new file mode 100644 index 000000000..d2f87250e --- /dev/null +++ b/node_modules/clean-css/lib/reader/apply-source-maps.js @@ -0,0 +1,245 @@ +var fs = require('fs'); +var path = require('path'); + +var isAllowedResource = require('./is-allowed-resource'); +var loadRemoteResource = require('./load-remote-resource'); +var matchDataUri = require('./match-data-uri'); +var rebaseLocalMap = require('./rebase-local-map'); +var rebaseRemoteMap = require('./rebase-remote-map'); + +var Token = require('../tokenizer/token'); +var hasProtocol = require('../utils/has-protocol'); +var isDataUriResource = require('../utils/is-data-uri-resource'); +var isRemoteResource = require('../utils/is-remote-resource'); + +var MAP_MARKER_PATTERN = /^\/\*# sourceMappingURL=(\S+) \*\/$/; + +function applySourceMaps(tokens, context, callback) { + var applyContext = { + callback: callback, + index: 0, + inline: context.options.inline, + inlineRequest: context.options.inlineRequest, + inlineTimeout: context.options.inlineTimeout, + inputSourceMapTracker: context.inputSourceMapTracker, + localOnly: context.localOnly, + processedTokens: [], + rebaseTo: context.options.rebaseTo, + sourceTokens: tokens, + warnings: context.warnings + }; + + return context.options.sourceMap && tokens.length > 0 ? + doApplySourceMaps(applyContext) : + callback(tokens); +} + +function doApplySourceMaps(applyContext) { + var singleSourceTokens = []; + var lastSource = findTokenSource(applyContext.sourceTokens[0]); + var source; + var token; + var l; + + for (l = applyContext.sourceTokens.length; applyContext.index < l; applyContext.index++) { + token = applyContext.sourceTokens[applyContext.index]; + source = findTokenSource(token); + + if (source != lastSource) { + singleSourceTokens = []; + lastSource = source; + } + + singleSourceTokens.push(token); + applyContext.processedTokens.push(token); + + if (token[0] == Token.COMMENT && MAP_MARKER_PATTERN.test(token[1])) { + return fetchAndApplySourceMap(token[1], source, singleSourceTokens, applyContext); + } + } + + return applyContext.callback(applyContext.processedTokens); +} + +function findTokenSource(token) { + var scope; + var metadata; + + if (token[0] == Token.AT_RULE || token[0] == Token.COMMENT) { + metadata = token[2][0]; + } else { + scope = token[1][0]; + metadata = scope[2][0]; + } + + return metadata[2]; +} + +function fetchAndApplySourceMap(sourceMapComment, source, singleSourceTokens, applyContext) { + return extractInputSourceMapFrom(sourceMapComment, applyContext, function (inputSourceMap) { + if (inputSourceMap) { + applyContext.inputSourceMapTracker.track(source, inputSourceMap); + applySourceMapRecursively(singleSourceTokens, applyContext.inputSourceMapTracker); + } + + applyContext.index++; + return doApplySourceMaps(applyContext); + }); +} + +function extractInputSourceMapFrom(sourceMapComment, applyContext, whenSourceMapReady) { + var uri = MAP_MARKER_PATTERN.exec(sourceMapComment)[1]; + var absoluteUri; + var sourceMap; + var rebasedMap; + + if (isDataUriResource(uri)) { + sourceMap = extractInputSourceMapFromDataUri(uri); + return whenSourceMapReady(sourceMap); + } else if (isRemoteResource(uri)) { + return loadInputSourceMapFromRemoteUri(uri, applyContext, function (sourceMap) { + var parsedMap; + + if (sourceMap) { + parsedMap = JSON.parse(sourceMap); + rebasedMap = rebaseRemoteMap(parsedMap, uri); + whenSourceMapReady(rebasedMap); + } else { + whenSourceMapReady(null); + } + }); + } else { + // at this point `uri` is already rebased, see lib/reader/rebase.js#rebaseSourceMapComment + // it is rebased to be consistent with rebasing other URIs + // however here we need to resolve it back to read it from disk + absoluteUri = path.resolve(applyContext.rebaseTo, uri); + sourceMap = loadInputSourceMapFromLocalUri(absoluteUri, applyContext); + + if (sourceMap) { + rebasedMap = rebaseLocalMap(sourceMap, absoluteUri, applyContext.rebaseTo); + return whenSourceMapReady(rebasedMap); + } else { + return whenSourceMapReady(null); + } + } +} + +function extractInputSourceMapFromDataUri(uri) { + var dataUriMatch = matchDataUri(uri); + var charset = dataUriMatch[2] ? dataUriMatch[2].split(/[=;]/)[2] : 'us-ascii'; + var encoding = dataUriMatch[3] ? dataUriMatch[3].split(';')[1] : 'utf8'; + var data = encoding == 'utf8' ? global.unescape(dataUriMatch[4]) : dataUriMatch[4]; + + var buffer = new Buffer(data, encoding); + buffer.charset = charset; + + return JSON.parse(buffer.toString()); +} + +function loadInputSourceMapFromRemoteUri(uri, applyContext, whenLoaded) { + var isAllowed = isAllowedResource(uri, true, applyContext.inline); + var isRuntimeResource = !hasProtocol(uri); + + if (applyContext.localOnly) { + applyContext.warnings.push('Cannot fetch remote resource from "' + uri + '" as no callback given.'); + return whenLoaded(null); + } else if (isRuntimeResource) { + applyContext.warnings.push('Cannot fetch "' + uri + '" as no protocol given.'); + return whenLoaded(null); + } else if (!isAllowed) { + applyContext.warnings.push('Cannot fetch "' + uri + '" as resource is not allowed.'); + return whenLoaded(null); + } + + loadRemoteResource(uri, applyContext.inlineRequest, applyContext.inlineTimeout, function (error, body) { + if (error) { + applyContext.warnings.push('Missing source map at "' + uri + '" - ' + error); + return whenLoaded(null); + } + + whenLoaded(body); + }); +} + +function loadInputSourceMapFromLocalUri(uri, applyContext) { + var isAllowed = isAllowedResource(uri, false, applyContext.inline); + var sourceMap; + + if (!fs.existsSync(uri) || !fs.statSync(uri).isFile()) { + applyContext.warnings.push('Ignoring local source map at "' + uri + '" as resource is missing.'); + return null; + } else if (!isAllowed) { + applyContext.warnings.push('Cannot fetch "' + uri + '" as resource is not allowed.'); + return null; + } + + sourceMap = fs.readFileSync(uri, 'utf-8'); + return JSON.parse(sourceMap); +} + +function applySourceMapRecursively(tokens, inputSourceMapTracker) { + var token; + var i, l; + + for (i = 0, l = tokens.length; i < l; i++) { + token = tokens[i]; + + switch (token[0]) { + case Token.AT_RULE: + applySourceMapTo(token, inputSourceMapTracker); + break; + case Token.AT_RULE_BLOCK: + applySourceMapRecursively(token[1], inputSourceMapTracker); + applySourceMapRecursively(token[2], inputSourceMapTracker); + break; + case Token.AT_RULE_BLOCK_SCOPE: + applySourceMapTo(token, inputSourceMapTracker); + break; + case Token.NESTED_BLOCK: + applySourceMapRecursively(token[1], inputSourceMapTracker); + applySourceMapRecursively(token[2], inputSourceMapTracker); + break; + case Token.NESTED_BLOCK_SCOPE: + applySourceMapTo(token, inputSourceMapTracker); + break; + case Token.COMMENT: + applySourceMapTo(token, inputSourceMapTracker); + break; + case Token.PROPERTY: + applySourceMapRecursively(token, inputSourceMapTracker); + break; + case Token.PROPERTY_BLOCK: + applySourceMapRecursively(token[1], inputSourceMapTracker); + break; + case Token.PROPERTY_NAME: + applySourceMapTo(token, inputSourceMapTracker); + break; + case Token.PROPERTY_VALUE: + applySourceMapTo(token, inputSourceMapTracker); + break; + case Token.RULE: + applySourceMapRecursively(token[1], inputSourceMapTracker); + applySourceMapRecursively(token[2], inputSourceMapTracker); + break; + case Token.RULE_SCOPE: + applySourceMapTo(token, inputSourceMapTracker); + } + } + + return tokens; +} + +function applySourceMapTo(token, inputSourceMapTracker) { + var value = token[1]; + var metadata = token[2]; + var newMetadata = []; + var i, l; + + for (i = 0, l = metadata.length; i < l; i++) { + newMetadata.push(inputSourceMapTracker.originalPositionFor(metadata[i], value.length)); + } + + token[2] = newMetadata; +} + +module.exports = applySourceMaps; diff --git a/node_modules/clean-css/lib/reader/extract-import-url-and-media.js b/node_modules/clean-css/lib/reader/extract-import-url-and-media.js new file mode 100644 index 000000000..e309c2f71 --- /dev/null +++ b/node_modules/clean-css/lib/reader/extract-import-url-and-media.js @@ -0,0 +1,35 @@ +var split = require('../utils/split'); + +var BRACE_PREFIX = /^\(/; +var BRACE_SUFFIX = /\)$/; +var IMPORT_PREFIX_PATTERN = /^@import/i; +var QUOTE_PREFIX_PATTERN = /['"]\s*/; +var QUOTE_SUFFIX_PATTERN = /\s*['"]/; +var URL_PREFIX_PATTERN = /^url\(\s*/i; +var URL_SUFFIX_PATTERN = /\s*\)/i; + +function extractImportUrlAndMedia(atRuleValue) { + var uri; + var mediaQuery; + var stripped; + var parts; + + stripped = atRuleValue + .replace(IMPORT_PREFIX_PATTERN, '') + .trim() + .replace(URL_PREFIX_PATTERN, '(') + .replace(URL_SUFFIX_PATTERN, ')') + .replace(QUOTE_PREFIX_PATTERN, '') + .replace(QUOTE_SUFFIX_PATTERN, ''); + + parts = split(stripped, ' '); + + uri = parts[0] + .replace(BRACE_PREFIX, '') + .replace(BRACE_SUFFIX, ''); + mediaQuery = parts.slice(1).join(' '); + + return [uri, mediaQuery]; +} + +module.exports = extractImportUrlAndMedia; diff --git a/node_modules/clean-css/lib/reader/input-source-map-tracker.js b/node_modules/clean-css/lib/reader/input-source-map-tracker.js new file mode 100644 index 000000000..ea2c03467 --- /dev/null +++ b/node_modules/clean-css/lib/reader/input-source-map-tracker.js @@ -0,0 +1,54 @@ +var SourceMapConsumer = require('source-map').SourceMapConsumer; + +function inputSourceMapTracker() { + var maps = {}; + + return { + all: all.bind(null, maps), + isTracking: isTracking.bind(null, maps), + originalPositionFor: originalPositionFor.bind(null, maps), + track: track.bind(null, maps) + }; +} + +function all(maps) { + return maps; +} + +function isTracking(maps, source) { + return source in maps; +} + +function originalPositionFor(maps, metadata, range, selectorFallbacks) { + var line = metadata[0]; + var column = metadata[1]; + var source = metadata[2]; + var position = { + line: line, + column: column + range + }; + var originalPosition; + + while (!originalPosition && position.column > column) { + position.column--; + originalPosition = maps[source].originalPositionFor(position); + } + + if (originalPosition.line === null && line > 1 && selectorFallbacks > 0) { + return originalPositionFor(maps, [line - 1, column, source], range, selectorFallbacks - 1); + } + + return originalPosition.line !== null ? + toMetadata(originalPosition) : + metadata; +} + +function toMetadata(asHash) { + return [asHash.line, asHash.column, asHash.source]; +} + +function track(maps, source, data) { + maps[source] = new SourceMapConsumer(data); +} + +module.exports = inputSourceMapTracker; diff --git a/node_modules/clean-css/lib/reader/is-allowed-resource.js b/node_modules/clean-css/lib/reader/is-allowed-resource.js new file mode 100644 index 000000000..043066e42 --- /dev/null +++ b/node_modules/clean-css/lib/reader/is-allowed-resource.js @@ -0,0 +1,77 @@ +var path = require('path'); +var url = require('url'); + +var isRemoteResource = require('../utils/is-remote-resource'); +var hasProtocol = require('../utils/has-protocol'); + +var HTTP_PROTOCOL = 'http:'; + +function isAllowedResource(uri, isRemote, rules) { + var match; + var absoluteUri; + var allowed = isRemote ? false : true; + var rule; + var isNegated; + var normalizedRule; + var i; + + if (rules.length === 0) { + return false; + } + + if (isRemote && !hasProtocol(uri)) { + uri = HTTP_PROTOCOL + uri; + } + + match = isRemote ? + url.parse(uri).host : + uri; + + absoluteUri = isRemote ? + uri : + path.resolve(uri); + + for (i = 0; i < rules.length; i++) { + rule = rules[i]; + isNegated = rule[0] == '!'; + normalizedRule = rule.substring(1); + + if (isNegated && isRemote && isRemoteRule(normalizedRule)) { + allowed = allowed && !isAllowedResource(uri, true, [normalizedRule]); + } else if (isNegated && !isRemote && !isRemoteRule(normalizedRule)) { + allowed = allowed && !isAllowedResource(uri, false, [normalizedRule]); + } else if (isNegated) { + allowed = allowed && true; + } else if (rule == 'all') { + allowed = true; + } else if (isRemote && rule == 'local') { + allowed = allowed || false; + } else if (isRemote && rule == 'remote') { + allowed = true; + } else if (!isRemote && rule == 'remote') { + allowed = false; + } else if (!isRemote && rule == 'local') { + allowed = true; + } else if (rule === match) { + allowed = true; + } else if (rule === uri) { + allowed = true; + } else if (isRemote && absoluteUri.indexOf(rule) === 0) { + allowed = true; + } else if (!isRemote && absoluteUri.indexOf(path.resolve(rule)) === 0) { + allowed = true; + } else if (isRemote != isRemoteRule(normalizedRule)) { + allowed = allowed && true; + } else { + allowed = false; + } + } + + return allowed; +} + +function isRemoteRule(rule) { + return isRemoteResource(rule) || url.parse(HTTP_PROTOCOL + '//' + rule).host == rule; +} + +module.exports = isAllowedResource; diff --git a/node_modules/clean-css/lib/reader/load-original-sources.js b/node_modules/clean-css/lib/reader/load-original-sources.js new file mode 100644 index 000000000..dbe2cad09 --- /dev/null +++ b/node_modules/clean-css/lib/reader/load-original-sources.js @@ -0,0 +1,126 @@ +var fs = require('fs'); +var path = require('path'); + +var isAllowedResource = require('./is-allowed-resource'); +var loadRemoteResource = require('./load-remote-resource'); + +var hasProtocol = require('../utils/has-protocol'); +var isRemoteResource = require('../utils/is-remote-resource'); + +function loadOriginalSources(context, callback) { + var loadContext = { + callback: callback, + index: 0, + inline: context.options.inline, + inlineRequest: context.options.inlineRequest, + inlineTimeout: context.options.inlineTimeout, + localOnly: context.localOnly, + rebaseTo: context.options.rebaseTo, + sourcesContent: context.sourcesContent, + uriToSource: uriToSourceMapping(context.inputSourceMapTracker.all()), + warnings: context.warnings + }; + + return context.options.sourceMap && context.options.sourceMapInlineSources ? + doLoadOriginalSources(loadContext) : + callback(); +} + +function uriToSourceMapping(allSourceMapConsumers) { + var mapping = {}; + var consumer; + var uri; + var source; + var i, l; + + for (source in allSourceMapConsumers) { + consumer = allSourceMapConsumers[source]; + + for (i = 0, l = consumer.sources.length; i < l; i++) { + uri = consumer.sources[i]; + source = consumer.sourceContentFor(uri, true); + + mapping[uri] = source; + } + } + + return mapping; +} + +function doLoadOriginalSources(loadContext) { + var uris = Object.keys(loadContext.uriToSource); + var uri; + var source; + var total; + + for (total = uris.length; loadContext.index < total; loadContext.index++) { + uri = uris[loadContext.index]; + source = loadContext.uriToSource[uri]; + + if (source) { + loadContext.sourcesContent[uri] = source; + } else { + return loadOriginalSource(uri, loadContext); + } + } + + return loadContext.callback(); +} + +function loadOriginalSource(uri, loadContext) { + var content; + + if (isRemoteResource(uri)) { + return loadOriginalSourceFromRemoteUri(uri, loadContext, function (content) { + loadContext.index++; + loadContext.sourcesContent[uri] = content; + return doLoadOriginalSources(loadContext); + }); + } else { + content = loadOriginalSourceFromLocalUri(uri, loadContext); + loadContext.index++; + loadContext.sourcesContent[uri] = content; + return doLoadOriginalSources(loadContext); + } +} + +function loadOriginalSourceFromRemoteUri(uri, loadContext, whenLoaded) { + var isAllowed = isAllowedResource(uri, true, loadContext.inline); + var isRuntimeResource = !hasProtocol(uri); + + if (loadContext.localOnly) { + loadContext.warnings.push('Cannot fetch remote resource from "' + uri + '" as no callback given.'); + return whenLoaded(null); + } else if (isRuntimeResource) { + loadContext.warnings.push('Cannot fetch "' + uri + '" as no protocol given.'); + return whenLoaded(null); + } else if (!isAllowed) { + loadContext.warnings.push('Cannot fetch "' + uri + '" as resource is not allowed.'); + return whenLoaded(null); + } + + loadRemoteResource(uri, loadContext.inlineRequest, loadContext.inlineTimeout, function (error, content) { + if (error) { + loadContext.warnings.push('Missing original source at "' + uri + '" - ' + error); + } + + whenLoaded(content); + }); +} + +function loadOriginalSourceFromLocalUri(relativeUri, loadContext) { + var isAllowed = isAllowedResource(relativeUri, false, loadContext.inline); + var absoluteUri = path.resolve(loadContext.rebaseTo, relativeUri); + + if (!fs.existsSync(absoluteUri) || !fs.statSync(absoluteUri).isFile()) { + loadContext.warnings.push('Ignoring local source map at "' + absoluteUri + '" as resource is missing.'); + return null; + } else if (!isAllowed) { + loadContext.warnings.push('Cannot fetch "' + absoluteUri + '" as resource is not allowed.'); + return null; + } + + return fs.readFileSync(absoluteUri, 'utf8'); +} + +module.exports = loadOriginalSources; diff --git a/node_modules/clean-css/lib/reader/load-remote-resource.js b/node_modules/clean-css/lib/reader/load-remote-resource.js new file mode 100644 index 000000000..0133c78bf --- /dev/null +++ b/node_modules/clean-css/lib/reader/load-remote-resource.js @@ -0,0 +1,74 @@ +var http = require('http'); +var https = require('https'); +var url = require('url'); + +var isHttpResource = require('../utils/is-http-resource'); +var isHttpsResource = require('../utils/is-https-resource'); +var override = require('../utils/override'); + +var HTTP_PROTOCOL = 'http:'; + +function loadRemoteResource(uri, inlineRequest, inlineTimeout, callback) { + var proxyProtocol = inlineRequest.protocol || inlineRequest.hostname; + var errorHandled = false; + var requestOptions; + var fetch; + + requestOptions = override( + url.parse(uri), + inlineRequest || {} + ); + + if (inlineRequest.hostname !== undefined) { + // overwrite as we always expect a http proxy currently + requestOptions.protocol = inlineRequest.protocol || HTTP_PROTOCOL; + requestOptions.path = requestOptions.href; + } + + fetch = (proxyProtocol && !isHttpsResource(proxyProtocol)) || isHttpResource(uri) ? + http.get : + https.get; + + fetch(requestOptions, function (res) { + var chunks = []; + var movedUri; + + if (errorHandled) { + return; + } + + if (res.statusCode < 200 || res.statusCode > 399) { + return callback(res.statusCode, null); + } else if (res.statusCode > 299) { + movedUri = url.resolve(uri, res.headers.location); + return loadRemoteResource(movedUri, inlineRequest, inlineTimeout, callback); + } + + res.on('data', function (chunk) { + chunks.push(chunk.toString()); + }); + res.on('end', function () { + var body = chunks.join(''); + callback(null, body); + }); + }) + .on('error', function (res) { + if (errorHandled) { + return; + } + + errorHandled = true; + callback(res.message, null); + }) + .on('timeout', function () { + if (errorHandled) { + return; + } + + errorHandled = true; + callback('timeout', null); + }) + .setTimeout(inlineTimeout); +} + +module.exports = loadRemoteResource; diff --git a/node_modules/clean-css/lib/reader/match-data-uri.js b/node_modules/clean-css/lib/reader/match-data-uri.js new file mode 100644 index 000000000..d0d5a4c74 --- /dev/null +++ b/node_modules/clean-css/lib/reader/match-data-uri.js @@ -0,0 +1,7 @@ +var DATA_URI_PATTERN = /^data:(\S*?)?(;charset=[^;]+)?(;[^,]+?)?,(.+)/; + +function matchDataUri(uri) { + return DATA_URI_PATTERN.exec(uri); +} + +module.exports = matchDataUri; diff --git a/node_modules/clean-css/lib/reader/normalize-path.js b/node_modules/clean-css/lib/reader/normalize-path.js new file mode 100644 index 000000000..a9eca38cb --- /dev/null +++ b/node_modules/clean-css/lib/reader/normalize-path.js @@ -0,0 +1,8 @@ +var UNIX_SEPARATOR = '/'; +var WINDOWS_SEPARATOR_PATTERN = /\\/g; + +function normalizePath(path) { + return path.replace(WINDOWS_SEPARATOR_PATTERN, UNIX_SEPARATOR); +} + +module.exports = normalizePath; diff --git a/node_modules/clean-css/lib/reader/read-sources.js b/node_modules/clean-css/lib/reader/read-sources.js new file mode 100644 index 000000000..e12e23514 --- /dev/null +++ b/node_modules/clean-css/lib/reader/read-sources.js @@ -0,0 +1,327 @@ +var fs = require('fs'); +var path = require('path'); + +var applySourceMaps = require('./apply-source-maps'); +var extractImportUrlAndMedia = require('./extract-import-url-and-media'); +var isAllowedResource = require('./is-allowed-resource'); +var loadOriginalSources = require('./load-original-sources'); +var loadRemoteResource = require('./load-remote-resource'); +var normalizePath = require('./normalize-path'); +var rebase = require('./rebase'); +var rebaseLocalMap = require('./rebase-local-map'); +var rebaseRemoteMap = require('./rebase-remote-map'); +var restoreImport = require('./restore-import'); + +var tokenize = require('../tokenizer/tokenize'); +var Token = require('../tokenizer/token'); +var Marker = require('../tokenizer/marker'); +var hasProtocol = require('../utils/has-protocol'); +var isImport = require('../utils/is-import'); +var isRemoteResource = require('../utils/is-remote-resource'); + +var UNKNOWN_URI = 'uri:unknown'; + +function readSources(input, context, callback) { + return doReadSources(input, context, function (tokens) { + return applySourceMaps(tokens, context, function () { + return loadOriginalSources(context, function () { return callback(tokens); }); + }); + }); +} + +function doReadSources(input, context, callback) { + if (typeof input == 'string') { + return fromString(input, context, callback); + } else if (Buffer.isBuffer(input)) { + return fromString(input.toString(), context, callback); + } else if (Array.isArray(input)) { + return fromArray(input, context, callback); + } else if (typeof input == 'object') { + return fromHash(input, context, callback); + } +} + +function fromString(input, context, callback) { + context.source = undefined; + context.sourcesContent[undefined] = input; + context.stats.originalSize += input.length; + + return fromStyles(input, context, { inline: context.options.inline }, callback); +} + +function fromArray(input, context, callback) { + var inputAsImports = input.reduce(function (accumulator, uri) { + var normalizedUri = normalizeUri(uri); + + accumulator.push(restoreAsImport(normalizedUri)); + return accumulator; + }, []); + + return fromStyles(inputAsImports.join(''), context, { inline: ['all'] }, callback); +} + +function fromHash(input, context, callback) { + var uri; + var normalizedUri; + var source; + var inputAsImports = []; + + for (uri in input) { + source = input[uri]; + normalizedUri = normalizeUri(uri); + + inputAsImports.push(restoreAsImport(normalizedUri)); + + context.sourcesContent[normalizedUri] = source.styles; + + if (source.sourceMap) { + trackSourceMap(source.sourceMap, normalizedUri, context); + } + } + + return fromStyles(inputAsImports.join(''), context, { inline: ['all'] }, callback); +} + +function normalizeUri(uri) { + var currentPath = path.resolve(''); + var absoluteUri; + var relativeToCurrentPath; + var normalizedUri; + + if (isRemoteResource(uri)) { + return uri; + } + + absoluteUri = path.isAbsolute(uri) ? + uri : + path.resolve(uri); + relativeToCurrentPath = path.relative(currentPath, absoluteUri); + normalizedUri = normalizePath(relativeToCurrentPath); + + return normalizedUri; +} + +function trackSourceMap(sourceMap, uri, context) { + var parsedMap = typeof sourceMap == 'string' ? + JSON.parse(sourceMap) : + sourceMap; + var rebasedMap = isRemoteResource(uri) ? + rebaseRemoteMap(parsedMap, uri) : + rebaseLocalMap(parsedMap, uri || UNKNOWN_URI, context.options.rebaseTo); + + context.inputSourceMapTracker.track(uri, rebasedMap); +} + +function restoreAsImport(uri) { + return restoreImport('url(' + uri + ')', '') + Marker.SEMICOLON; +} + +function fromStyles(styles, context, parentInlinerContext, callback) { + var tokens; + var rebaseConfig = {}; + + if (!context.source) { + rebaseConfig.fromBase = path.resolve(''); + rebaseConfig.toBase = context.options.rebaseTo; + } else if (isRemoteResource(context.source)) { + rebaseConfig.fromBase = context.source; + rebaseConfig.toBase = context.source; + } else if (path.isAbsolute(context.source)) { + rebaseConfig.fromBase = path.dirname(context.source); + rebaseConfig.toBase = context.options.rebaseTo; + } else { + rebaseConfig.fromBase = path.dirname(path.resolve(context.source)); + rebaseConfig.toBase = context.options.rebaseTo; + } + + tokens = tokenize(styles, context); + tokens = rebase(tokens, context.options.rebase, context.validator, rebaseConfig); + + return allowsAnyImports(parentInlinerContext.inline) ? + inline(tokens, context, parentInlinerContext, callback) : + callback(tokens); +} + +function allowsAnyImports(inline) { + return !(inline.length == 1 && inline[0] == 'none'); +} + +function inline(tokens, externalContext, parentInlinerContext, callback) { + var inlinerContext = { + afterContent: false, + callback: callback, + errors: externalContext.errors, + externalContext: externalContext, + inlinedStylesheets: parentInlinerContext.inlinedStylesheets || externalContext.inlinedStylesheets, + inline: parentInlinerContext.inline, + inlineRequest: externalContext.options.inlineRequest, + inlineTimeout: externalContext.options.inlineTimeout, + isRemote: parentInlinerContext.isRemote || false, + localOnly: externalContext.localOnly, + outputTokens: [], + rebaseTo: externalContext.options.rebaseTo, + sourceTokens: tokens, + warnings: externalContext.warnings + }; + + return doInlineImports(inlinerContext); +} + +function doInlineImports(inlinerContext) { + var token; + var i, l; + + for (i = 0, l = inlinerContext.sourceTokens.length; i < l; i++) { + token = inlinerContext.sourceTokens[i]; + + if (token[0] == Token.AT_RULE && isImport(token[1])) { + inlinerContext.sourceTokens.splice(0, i); + return inlineStylesheet(token, inlinerContext); + } else if (token[0] == Token.AT_RULE || token[0] == Token.COMMENT) { + inlinerContext.outputTokens.push(token); + } else { + inlinerContext.outputTokens.push(token); + inlinerContext.afterContent = true; + } + } + + inlinerContext.sourceTokens = []; + return inlinerContext.callback(inlinerContext.outputTokens); +} + +function inlineStylesheet(token, inlinerContext) { + var uriAndMediaQuery = extractImportUrlAndMedia(token[1]); + var uri = uriAndMediaQuery[0]; + var mediaQuery = uriAndMediaQuery[1]; + var metadata = token[2]; + + return isRemoteResource(uri) ? + inlineRemoteStylesheet(uri, mediaQuery, metadata, inlinerContext) : + inlineLocalStylesheet(uri, mediaQuery, metadata, inlinerContext); +} + +function inlineRemoteStylesheet(uri, mediaQuery, metadata, inlinerContext) { + var isAllowed = isAllowedResource(uri, true, inlinerContext.inline); + var originalUri = uri; + var isLoaded = uri in inlinerContext.externalContext.sourcesContent; + var isRuntimeResource = !hasProtocol(uri); + + if (inlinerContext.inlinedStylesheets.indexOf(uri) > -1) { + inlinerContext.warnings.push('Ignoring remote @import of "' + uri + '" as it has already been imported.'); + inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1); + return doInlineImports(inlinerContext); + } else if (inlinerContext.localOnly && inlinerContext.afterContent) { + inlinerContext.warnings.push('Ignoring remote @import of "' + uri + '" as no callback given and after other content.'); + inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1); + return doInlineImports(inlinerContext); + } else if (isRuntimeResource) { + inlinerContext.warnings.push('Skipping remote @import of "' + uri + '" as no protocol given.'); + inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1)); + inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1); + return doInlineImports(inlinerContext); + } else if (inlinerContext.localOnly && !isLoaded) { + inlinerContext.warnings.push('Skipping remote @import of "' + uri + '" as no callback given.'); + inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1)); + inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1); + return doInlineImports(inlinerContext); + } else if (!isAllowed && inlinerContext.afterContent) { + inlinerContext.warnings.push('Ignoring remote @import of "' + uri + '" as resource is not allowed and after other content.'); + inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1); + return doInlineImports(inlinerContext); + } else if (!isAllowed) { + inlinerContext.warnings.push('Skipping remote @import of "' + uri + '" as resource is not allowed.'); + inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1)); + inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1); + return doInlineImports(inlinerContext); + } + + inlinerContext.inlinedStylesheets.push(uri); + + function whenLoaded(error, importedStyles) { + if (error) { + inlinerContext.errors.push('Broken @import declaration of "' + uri + '" - ' + error); + + return process.nextTick(function () { + inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1)); + inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1); + doInlineImports(inlinerContext); + }); + } + + inlinerContext.inline = inlinerContext.externalContext.options.inline; + inlinerContext.isRemote = true; + + inlinerContext.externalContext.source = originalUri; + inlinerContext.externalContext.sourcesContent[uri] = importedStyles; + inlinerContext.externalContext.stats.originalSize += importedStyles.length; + + return fromStyles(importedStyles, inlinerContext.externalContext, inlinerContext, function (importedTokens) { + importedTokens = wrapInMedia(importedTokens, mediaQuery, metadata); + + inlinerContext.outputTokens = inlinerContext.outputTokens.concat(importedTokens); + inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1); + + return doInlineImports(inlinerContext); + }); + } + + return isLoaded ? + whenLoaded(null, inlinerContext.externalContext.sourcesContent[uri]) : + loadRemoteResource(uri, inlinerContext.inlineRequest, inlinerContext.inlineTimeout, whenLoaded); +} + +function inlineLocalStylesheet(uri, mediaQuery, metadata, inlinerContext) { + var currentPath = path.resolve(''); + var absoluteUri = path.isAbsolute(uri) ? + path.resolve(currentPath, uri[0] == '/' ? uri.substring(1) : uri) : + path.resolve(inlinerContext.rebaseTo, uri); + var relativeToCurrentPath = path.relative(currentPath, absoluteUri); + var importedStyles; + var importedTokens; + var isAllowed = isAllowedResource(uri, false, inlinerContext.inline); + var normalizedPath = normalizePath(relativeToCurrentPath); + var isLoaded = normalizedPath in inlinerContext.externalContext.sourcesContent; + + if (inlinerContext.inlinedStylesheets.indexOf(absoluteUri) > -1) { + inlinerContext.warnings.push('Ignoring local @import of "' + uri + '" as it has already been imported.'); + } else if (!isLoaded && (!fs.existsSync(absoluteUri) || !fs.statSync(absoluteUri).isFile())) { + inlinerContext.errors.push('Ignoring local @import of "' + uri + '" as resource is missing.'); + } else if (!isAllowed && inlinerContext.afterContent) { + inlinerContext.warnings.push('Ignoring local @import of "' + uri + '" as resource is not allowed and after other content.'); + } else if (inlinerContext.afterContent) { + inlinerContext.warnings.push('Ignoring local @import of "' + uri + '" as after other content.'); + } else if (!isAllowed) { + inlinerContext.warnings.push('Skipping local @import of "' + uri + '" as resource is not allowed.'); + inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1)); + } else { + importedStyles = isLoaded ? + inlinerContext.externalContext.sourcesContent[normalizedPath] : + fs.readFileSync(absoluteUri, 'utf-8'); + + inlinerContext.inlinedStylesheets.push(absoluteUri); + inlinerContext.inline = inlinerContext.externalContext.options.inline; + + inlinerContext.externalContext.source = normalizedPath; + inlinerContext.externalContext.sourcesContent[normalizedPath] = importedStyles; + inlinerContext.externalContext.stats.originalSize += importedStyles.length; + + importedTokens = fromStyles(importedStyles, inlinerContext.externalContext, inlinerContext, function (tokens) { return tokens; }); + importedTokens = wrapInMedia(importedTokens, mediaQuery, metadata); + + inlinerContext.outputTokens = inlinerContext.outputTokens.concat(importedTokens); + } + + inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1); + + return doInlineImports(inlinerContext); +} + +function wrapInMedia(tokens, mediaQuery, metadata) { + if (mediaQuery) { + return [[Token.NESTED_BLOCK, [[Token.NESTED_BLOCK_SCOPE, '@media ' + mediaQuery, metadata]], tokens]]; + } else { + return tokens; + } +} + +module.exports = readSources; diff --git a/node_modules/clean-css/lib/reader/rebase-local-map.js b/node_modules/clean-css/lib/reader/rebase-local-map.js new file mode 100644 index 000000000..aec8d2324 --- /dev/null +++ b/node_modules/clean-css/lib/reader/rebase-local-map.js @@ -0,0 +1,15 @@ +var path = require('path'); + +function rebaseLocalMap(sourceMap, sourceUri, rebaseTo) { + var currentPath = path.resolve(''); + var absoluteUri = path.resolve(currentPath, sourceUri); + var absoluteUriDirectory = path.dirname(absoluteUri); + + sourceMap.sources = sourceMap.sources.map(function(source) { + return path.relative(rebaseTo, path.resolve(absoluteUriDirectory, source)); + }); + + return sourceMap; +} + +module.exports = rebaseLocalMap; diff --git a/node_modules/clean-css/lib/reader/rebase-remote-map.js b/node_modules/clean-css/lib/reader/rebase-remote-map.js new file mode 100644 index 000000000..7b6bb7ac9 --- /dev/null +++ b/node_modules/clean-css/lib/reader/rebase-remote-map.js @@ -0,0 +1,14 @@ +var path = require('path'); +var url = require('url'); + +function rebaseRemoteMap(sourceMap, sourceUri) { + var sourceDirectory = path.dirname(sourceUri); + + sourceMap.sources = sourceMap.sources.map(function(source) { + return url.resolve(sourceDirectory, source); + }); + + return sourceMap; +} + +module.exports = rebaseRemoteMap; diff --git a/node_modules/clean-css/lib/reader/rebase.js b/node_modules/clean-css/lib/reader/rebase.js new file mode 100644 index 000000000..4e0ff9751 --- /dev/null +++ b/node_modules/clean-css/lib/reader/rebase.js @@ -0,0 +1,101 @@ +var extractImportUrlAndMedia = require('./extract-import-url-and-media'); +var restoreImport = require('./restore-import'); +var rewriteUrl = require('./rewrite-url'); + +var Token = require('../tokenizer/token'); +var isImport = require('../utils/is-import'); + +var SOURCE_MAP_COMMENT_PATTERN = /^\/\*# sourceMappingURL=(\S+) \*\/$/; + +function rebase(tokens, rebaseAll, validator, rebaseConfig) { + return rebaseAll ? + rebaseEverything(tokens, validator, rebaseConfig) : + rebaseAtRules(tokens, validator, rebaseConfig); +} + +function rebaseEverything(tokens, validator, rebaseConfig) { + var token; + var i, l; + + for (i = 0, l = tokens.length; i < l; i++) { + token = tokens[i]; + + switch (token[0]) { + case Token.AT_RULE: + rebaseAtRule(token, validator, rebaseConfig); + break; + case Token.AT_RULE_BLOCK: + rebaseProperties(token[2], validator, rebaseConfig); + break; + case Token.COMMENT: + rebaseSourceMapComment(token, rebaseConfig); + break; + case Token.NESTED_BLOCK: + rebaseEverything(token[2], validator, rebaseConfig); + break; + case Token.RULE: + rebaseProperties(token[2], validator, rebaseConfig); + break; + } + } + + return tokens; +} + +function rebaseAtRules(tokens, validator, rebaseConfig) { + var token; + var i, l; + + for (i = 0, l = tokens.length; i < l; i++) { + token = tokens[i]; + + switch (token[0]) { + case Token.AT_RULE: + rebaseAtRule(token, validator, rebaseConfig); + break; + } + } + + return tokens; +} + +function rebaseAtRule(token, validator, rebaseConfig) { + if (!isImport(token[1])) { + return; + } + + var uriAndMediaQuery = extractImportUrlAndMedia(token[1]); + var newUrl = rewriteUrl(uriAndMediaQuery[0], rebaseConfig); + var mediaQuery = uriAndMediaQuery[1]; + + token[1] = restoreImport(newUrl, mediaQuery); +} + +function rebaseSourceMapComment(token, rebaseConfig) { + var matches = SOURCE_MAP_COMMENT_PATTERN.exec(token[1]); + + if (matches && matches[1].indexOf('data:') === -1) { + token[1] = token[1].replace(matches[1], rewriteUrl(matches[1], rebaseConfig, true)); + } +} + +function rebaseProperties(properties, validator, rebaseConfig) { + var property; + var value; + var i, l; + var j, m; + + for (i = 0, l = properties.length; i < l; i++) { + property = properties[i]; + + for (j = 2 /* 0 is Token.PROPERTY, 1 is name */, m = property.length; j < m; j++) { + value = property[j][1]; + + if (validator.isValidUrl(value)) { + property[j][1] = rewriteUrl(value, rebaseConfig); + } + } + } +} + +module.exports = rebase; diff --git a/node_modules/clean-css/lib/reader/restore-import.js b/node_modules/clean-css/lib/reader/restore-import.js new file mode 100644 index 000000000..5bdbd92c8 --- /dev/null +++ b/node_modules/clean-css/lib/reader/restore-import.js @@ -0,0 +1,5 @@ +function restoreImport(uri, mediaQuery) { + return ('@import ' + uri + ' ' + mediaQuery).trim(); +} + +module.exports = restoreImport; diff --git a/node_modules/clean-css/lib/reader/rewrite-url.js b/node_modules/clean-css/lib/reader/rewrite-url.js new file mode 100644 index 000000000..a4793fd1c --- /dev/null +++ b/node_modules/clean-css/lib/reader/rewrite-url.js @@ -0,0 +1,118 @@ +var path = require('path'); +var url = require('url'); + +var DOUBLE_QUOTE = '"'; +var SINGLE_QUOTE = '\''; +var URL_PREFIX = 'url('; +var URL_SUFFIX = ')'; + +var QUOTE_PREFIX_PATTERN = /^["']/; +var QUOTE_SUFFIX_PATTERN = /["']$/; +var ROUND_BRACKETS_PATTERN = /[\(\)]/; +var URL_PREFIX_PATTERN = /^url\(/i; +var URL_SUFFIX_PATTERN = /\)$/; +var WHITESPACE_PATTERN = /\s/; + +var isWindows = process.platform == 'win32'; + +function rebase(uri, rebaseConfig) { + if (!rebaseConfig) { + return uri; + } + + if (isAbsolute(uri) && !isRemote(rebaseConfig.toBase)) { + return uri; + } + + if (isRemote(uri) || isSVGMarker(uri) || isInternal(uri)) { + return uri; + } + + if (isData(uri)) { + return '\'' + uri + '\''; + } + + if (isRemote(rebaseConfig.toBase)) { + return url.resolve(rebaseConfig.toBase, uri); + } + + return rebaseConfig.absolute ? + normalize(absolute(uri, rebaseConfig)) : + normalize(relative(uri, rebaseConfig)); +} + +function isAbsolute(uri) { + return path.isAbsolute(uri); +} + +function isSVGMarker(uri) { + return uri[0] == '#'; +} + +function isInternal(uri) { + return /^\w+:\w+/.test(uri); +} + +function isRemote(uri) { + return /^[^:]+?:\/\//.test(uri) || uri.indexOf('//') === 0; +} + +function isData(uri) { + return uri.indexOf('data:') === 0; +} + +function absolute(uri, rebaseConfig) { + return path + .resolve(path.join(rebaseConfig.fromBase || '', uri)) + .replace(rebaseConfig.toBase, ''); +} + +function relative(uri, rebaseConfig) { + return path.relative(rebaseConfig.toBase, path.join(rebaseConfig.fromBase || '', uri)); +} + +function normalize(uri) { + return isWindows ? uri.replace(/\\/g, '/') : uri; +} + +function quoteFor(unquotedUrl) { + if (unquotedUrl.indexOf(SINGLE_QUOTE) > -1) { + return DOUBLE_QUOTE; + } else if (unquotedUrl.indexOf(DOUBLE_QUOTE) > -1) { + return SINGLE_QUOTE; + } else if (hasWhitespace(unquotedUrl) || hasRoundBrackets(unquotedUrl)) { + return SINGLE_QUOTE; + } else { + return ''; + } +} + +function hasWhitespace(url) { + return WHITESPACE_PATTERN.test(url); +} + +function hasRoundBrackets(url) { + return ROUND_BRACKETS_PATTERN.test(url); +} + +function rewriteUrl(originalUrl, rebaseConfig, pathOnly) { + var strippedUrl = originalUrl + .replace(URL_PREFIX_PATTERN, '') + .replace(URL_SUFFIX_PATTERN, '') + .trim(); + + var unquotedUrl = strippedUrl + .replace(QUOTE_PREFIX_PATTERN, '') + .replace(QUOTE_SUFFIX_PATTERN, '') + .trim(); + + var quote = strippedUrl[0] == SINGLE_QUOTE || strippedUrl[0] == DOUBLE_QUOTE ? + strippedUrl[0] : + quoteFor(unquotedUrl); + + return pathOnly ? + rebase(unquotedUrl, rebaseConfig) : + URL_PREFIX + quote + rebase(unquotedUrl, rebaseConfig) + quote + URL_SUFFIX; +} + +module.exports = rewriteUrl; diff --git a/node_modules/clean-css/lib/tokenizer/marker.js b/node_modules/clean-css/lib/tokenizer/marker.js new file mode 100644 index 000000000..c3f6805a7 --- /dev/null +++ b/node_modules/clean-css/lib/tokenizer/marker.js @@ -0,0 +1,25 @@ +var Marker = { + ASTERISK: '*', + AT: '@', + BACK_SLASH: '\\', + CLOSE_CURLY_BRACKET: '}', + CLOSE_ROUND_BRACKET: ')', + CLOSE_SQUARE_BRACKET: ']', + COLON: ':', + COMMA: ',', + DOUBLE_QUOTE: '"', + EXCLAMATION: '!', + FORWARD_SLASH: '/', + NEW_LINE_NIX: '\n', + NEW_LINE_WIN: '\r', + OPEN_CURLY_BRACKET: '{', + OPEN_ROUND_BRACKET: '(', + OPEN_SQUARE_BRACKET: '[', + SEMICOLON: ';', + SINGLE_QUOTE: '\'', + SPACE: ' ', + TAB: '\t', + UNDERSCORE: '_' +}; + +module.exports = Marker; diff --git a/node_modules/clean-css/lib/tokenizer/token.js b/node_modules/clean-css/lib/tokenizer/token.js new file mode 100644 index 000000000..acd0154ee --- /dev/null +++ b/node_modules/clean-css/lib/tokenizer/token.js @@ -0,0 +1,16 @@ +var Token = { + AT_RULE: 'at-rule', // e.g. `@import`, `@charset` + AT_RULE_BLOCK: 'at-rule-block', // e.g. `@font-face{...}` + AT_RULE_BLOCK_SCOPE: 'at-rule-block-scope', // e.g. `@font-face` + COMMENT: 'comment', // e.g. `/* comment */` + NESTED_BLOCK: 'nested-block', // e.g. `@media screen{...}`, `@keyframes animation {...}` + NESTED_BLOCK_SCOPE: 'nested-block-scope', // e.g. `@media`, `@keyframes` + PROPERTY: 'property', // e.g. `color:red` + PROPERTY_BLOCK: 'property-block', // e.g. `--var:{color:red}` + PROPERTY_NAME: 'property-name', // e.g. `color` + PROPERTY_VALUE: 'property-value', // e.g. `red` + RULE: 'rule', // e.g `div > a{...}` + RULE_SCOPE: 'rule-scope' // e.g `div > a` +}; + +module.exports = Token; diff --git a/node_modules/clean-css/lib/tokenizer/tokenize.js b/node_modules/clean-css/lib/tokenizer/tokenize.js new file mode 100644 index 000000000..15fb584a0 --- /dev/null +++ b/node_modules/clean-css/lib/tokenizer/tokenize.js @@ -0,0 +1,463 @@ +var Marker = require('./marker'); +var Token = require('./token'); + +var formatPosition = require('../utils/format-position'); + +var Level = { + BLOCK: 'block', + COMMENT: 'comment', + DOUBLE_QUOTE: 'double-quote', + RULE: 'rule', + SINGLE_QUOTE: 'single-quote' +}; + +var AT_RULES = [ + '@charset', + '@import' +]; + +var BLOCK_RULES = [ + '@-moz-document', + '@document', + '@-moz-keyframes', + '@-ms-keyframes', + '@-o-keyframes', + '@-webkit-keyframes', + '@keyframes', + '@media', + '@supports' +]; + +var RULE_WORD_SEPARATOR_PATTERN = /[\s\(]/; +var TAIL_BROKEN_VALUE_PATTERN = /[\s|\}]*$/; + +function tokenize(source, externalContext) { + var internalContext = { + level: Level.BLOCK, + position: { + source: externalContext.source || undefined, + line: 1, + column: 0, + index: 0 + } + }; + + return intoTokens(source, externalContext, internalContext, false); +} + +function intoTokens(source, externalContext, internalContext, isNested) { + var allTokens = []; + var newTokens = allTokens; + var lastToken; + var ruleToken; + var ruleTokens = []; + var propertyToken; + var metadata; + var metadatas = []; + var level = internalContext.level; + var levels = []; + var buffer = []; + var buffers = []; + var serializedBuffer; + var roundBracketLevel = 0; + var isQuoted; + var isSpace; + var isNewLineNix; + var isNewLineWin; + var isCommentStart; + var wasCommentStart = false; + var isCommentEnd; + var wasCommentEnd = false; + var isEscaped; + var wasEscaped = false; + var seekingValue = false; + var seekingPropertyBlockClosing = false; + var position = internalContext.position; + + for (; position.index < source.length; position.index++) { + var character = source[position.index]; + + isQuoted = level == Level.SINGLE_QUOTE || level == Level.DOUBLE_QUOTE; + isSpace = character == Marker.SPACE || character == Marker.TAB; + isNewLineNix = character == Marker.NEW_LINE_NIX; + isNewLineWin = character == Marker.NEW_LINE_NIX && source[position.index - 1] == Marker.NEW_LINE_WIN; + isCommentStart = !wasCommentEnd && level != Level.COMMENT && !isQuoted && character == Marker.ASTERISK && source[position.index - 1] == Marker.FORWARD_SLASH; + isCommentEnd = !wasCommentStart && level == Level.COMMENT && character == Marker.FORWARD_SLASH && source[position.index - 1] == Marker.ASTERISK; + + metadata = buffer.length === 0 ? + [position.line, position.column, position.source] : + metadata; + + if (isEscaped) { + // previous character was a backslash + buffer.push(character); + } else if (!isCommentEnd && level == Level.COMMENT) { + buffer.push(character); + } else if (isCommentStart && (level == Level.BLOCK || level == Level.RULE) && buffer.length > 1) { + // comment start within block preceded by some content, e.g. div/*<-- + metadatas.push(metadata); + buffer.push(character); + buffers.push(buffer.slice(0, buffer.length - 2)); + + buffer = buffer.slice(buffer.length - 2); + metadata = [position.line, position.column - 1, position.source]; + + levels.push(level); + level = Level.COMMENT; + } else if (isCommentStart) { + // comment start, e.g. /*<-- + levels.push(level); + level = Level.COMMENT; + buffer.push(character); + } else if (isCommentEnd) { + // comment end, e.g. /* comment */<-- + serializedBuffer = buffer.join('').trim() + character; + lastToken = [Token.COMMENT, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]; + newTokens.push(lastToken); + + level = levels.pop(); + metadata = metadatas.pop() || null; + buffer = buffers.pop() || []; + } else if (character == Marker.SINGLE_QUOTE && !isQuoted) { + // single quotation start, e.g. a[href^='https<-- + levels.push(level); + level = Level.SINGLE_QUOTE; + buffer.push(character); + } else if (character == Marker.SINGLE_QUOTE && level == Level.SINGLE_QUOTE) { + // single quotation end, e.g. a[href^='https'<-- + level = levels.pop(); + buffer.push(character); + } else if (character == Marker.DOUBLE_QUOTE && !isQuoted) { + // double quotation start, e.g. a[href^="<-- + levels.push(level); + level = Level.DOUBLE_QUOTE; + buffer.push(character); + } else if (character == Marker.DOUBLE_QUOTE && level == Level.DOUBLE_QUOTE) { + // double quotation end, e.g. a[href^="https"<-- + level = levels.pop(); + buffer.push(character); + } else if (!isCommentStart && !isCommentEnd && character != Marker.CLOSE_ROUND_BRACKET && character != Marker.OPEN_ROUND_BRACKET && level != Level.COMMENT && !isQuoted && roundBracketLevel > 0) { + // character inside any function, e.g. hsla(.<-- + buffer.push(character); + } else if (character == Marker.OPEN_ROUND_BRACKET && !isQuoted && level != Level.COMMENT && !seekingValue) { + // round open bracket, e.g. @import url(<-- + buffer.push(character); + + roundBracketLevel++; + } else if (character == Marker.CLOSE_ROUND_BRACKET && !isQuoted && level != Level.COMMENT && !seekingValue) { + // round open bracket, e.g. @import url(test.css)<-- + buffer.push(character); + + roundBracketLevel--; + } else if (character == Marker.SEMICOLON && level == Level.BLOCK && buffer[0] == Marker.AT) { + // semicolon ending rule at block level, e.g. @import '...';<-- + serializedBuffer = buffer.join('').trim(); + allTokens.push([Token.AT_RULE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + + buffer = []; + } else if (character == Marker.COMMA && level == Level.BLOCK && ruleToken) { + // comma separator at block level, e.g. a,div,<-- + serializedBuffer = buffer.join('').trim(); + ruleToken[1].push([tokenScopeFrom(ruleToken[0]), serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext, ruleToken[1].length)]]); + + buffer = []; + } else if (character == Marker.COMMA && level == Level.BLOCK && tokenTypeFrom(buffer) == Token.AT_RULE) { + // comma separator at block level, e.g. @import url(...) screen,<-- + // keep iterating as end semicolon will create the token + buffer.push(character); + } else if (character == Marker.COMMA && level == Level.BLOCK) { + // comma separator at block level, e.g. a,<-- + ruleToken = [tokenTypeFrom(buffer), [], []]; + serializedBuffer = buffer.join('').trim(); + ruleToken[1].push([tokenScopeFrom(ruleToken[0]), serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext, 0)]]); + + buffer = []; + } else if (character == Marker.OPEN_CURLY_BRACKET && level == Level.BLOCK && ruleToken && ruleToken[0] == Token.NESTED_BLOCK) { + // open brace opening at-rule at block level, e.g. @media{<-- + serializedBuffer = buffer.join('').trim(); + ruleToken[1].push([Token.NESTED_BLOCK_SCOPE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + allTokens.push(ruleToken); + + levels.push(level); + position.column++; + position.index++; + buffer = []; + + ruleToken[2] = intoTokens(source, externalContext, internalContext, true); + ruleToken = null; + } else if (character == Marker.OPEN_CURLY_BRACKET && level == Level.BLOCK && tokenTypeFrom(buffer) == Token.NESTED_BLOCK) { + // open brace opening at-rule at block level, e.g. @media{<-- + serializedBuffer = buffer.join('').trim(); + ruleToken = ruleToken || [Token.NESTED_BLOCK, [], []]; + ruleToken[1].push([Token.NESTED_BLOCK_SCOPE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + allTokens.push(ruleToken); + + levels.push(level); + position.column++; + position.index++; + buffer = []; + + ruleToken[2] = intoTokens(source, externalContext, internalContext, true); + ruleToken = null; + } else if (character == Marker.OPEN_CURLY_BRACKET && level == Level.BLOCK) { + // open brace opening rule at block level, e.g. div{<-- + serializedBuffer = buffer.join('').trim(); + ruleToken = ruleToken || [tokenTypeFrom(buffer), [], []]; + ruleToken[1].push([tokenScopeFrom(ruleToken[0]), serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext, ruleToken[1].length)]]); + newTokens = ruleToken[2]; + allTokens.push(ruleToken); + + levels.push(level); + level = Level.RULE; + buffer = []; + } else if (character == Marker.OPEN_CURLY_BRACKET && level == Level.RULE && seekingValue) { + // open brace opening rule at rule level, e.g. div{--variable:{<-- + ruleTokens.push(ruleToken); + ruleToken = [Token.PROPERTY_BLOCK, []]; + propertyToken.push(ruleToken); + newTokens = ruleToken[1]; + + levels.push(level); + level = Level.RULE; + seekingValue = false; + } else if (character == Marker.COLON && level == Level.RULE && !seekingValue) { + // colon at rule level, e.g. a{color:<-- + serializedBuffer = buffer.join('').trim(); + propertyToken = [Token.PROPERTY, [Token.PROPERTY_NAME, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]]; + newTokens.push(propertyToken); + + seekingValue = true; + buffer = []; + } else if (character == Marker.SEMICOLON && level == Level.RULE && propertyToken && ruleTokens.length > 0 && buffer.length > 0 && buffer[0] == Marker.AT) { + // semicolon at rule level for at-rule, e.g. a{--color:{@apply(--other-color);<-- + serializedBuffer = buffer.join('').trim(); + ruleToken[1].push([Token.AT_RULE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + + buffer = []; + } else if (character == Marker.SEMICOLON && level == Level.RULE && propertyToken && buffer.length > 0) { + // semicolon at rule level, e.g. a{color:red;<-- + serializedBuffer = buffer.join('').trim(); + propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + + propertyToken = null; + seekingValue = false; + buffer = []; + } else if (character == Marker.SEMICOLON && level == Level.RULE && propertyToken && buffer.length === 0) { + // semicolon after bracketed value at rule level, e.g. a{color:rgb(...);<-- + propertyToken = null; + seekingValue = false; + } else if (character == Marker.SEMICOLON && level == Level.RULE && buffer.length > 0 && buffer[0] == Marker.AT) { + // semicolon for at-rule at rule level, e.g. a{@apply(--variable);<-- + serializedBuffer = buffer.join(''); + newTokens.push([Token.AT_RULE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + + seekingValue = false; + buffer = []; + } else if (character == Marker.SEMICOLON && level == Level.RULE && seekingPropertyBlockClosing) { + // close brace after a property block at rule level, e.g. a{--custom:{color:red;};<-- + seekingPropertyBlockClosing = false; + buffer = []; + } else if (character == Marker.SEMICOLON && level == Level.RULE && buffer.length === 0) { + // stray semicolon at rule level, e.g. a{;<-- + // noop + } else if (character == Marker.CLOSE_CURLY_BRACKET && level == Level.RULE && propertyToken && seekingValue && buffer.length > 0 && ruleTokens.length > 0) { + // close brace at rule level, e.g. a{--color:{color:red}<-- + serializedBuffer = buffer.join(''); + propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + propertyToken = null; + ruleToken = ruleTokens.pop(); + newTokens = ruleToken[2]; + + level = levels.pop(); + seekingValue = false; + buffer = []; + } else if (character == Marker.CLOSE_CURLY_BRACKET && level == Level.RULE && propertyToken && buffer.length > 0 && buffer[0] == Marker.AT && ruleTokens.length > 0) { + // close brace at rule level for at-rule, e.g. a{--color:{@apply(--other-color)}<-- + serializedBuffer = buffer.join(''); + ruleToken[1].push([Token.AT_RULE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + propertyToken = null; + ruleToken = ruleTokens.pop(); + newTokens = ruleToken[2]; + + level = levels.pop(); + seekingValue = false; + buffer = []; + } else if (character == Marker.CLOSE_CURLY_BRACKET && level == Level.RULE && propertyToken && ruleTokens.length > 0) { + // close brace at rule level after space, e.g. a{--color:{color:red }<-- + propertyToken = null; + ruleToken = ruleTokens.pop(); + newTokens = ruleToken[2]; + + level = levels.pop(); + seekingValue = false; + } else if (character == Marker.CLOSE_CURLY_BRACKET && level == Level.RULE && propertyToken && buffer.length > 0) { + // close brace at rule level, e.g. a{color:red}<-- + serializedBuffer = buffer.join(''); + propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + propertyToken = null; + ruleToken = ruleTokens.pop(); + newTokens = allTokens; + + level = levels.pop(); + seekingValue = false; + buffer = []; + } else if (character == Marker.CLOSE_CURLY_BRACKET && level == Level.RULE && buffer.length > 0 && buffer[0] == Marker.AT) { + // close brace after at-rule at rule level, e.g. a{@apply(--variable)}<-- + propertyToken = null; + ruleToken = null; + serializedBuffer = buffer.join('').trim(); + newTokens.push([Token.AT_RULE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + newTokens = allTokens; + + level = levels.pop(); + seekingValue = false; + buffer = []; + } else if (character == Marker.CLOSE_CURLY_BRACKET && level == Level.RULE && levels[levels.length - 1] == Level.RULE) { + // close brace after a property block at rule level, e.g. a{--custom:{color:red;}<-- + propertyToken = null; + ruleToken = ruleTokens.pop(); + newTokens = ruleToken[2]; + + level = levels.pop(); + seekingValue = false; + seekingPropertyBlockClosing = true; + buffer = []; + } else if (character == Marker.CLOSE_CURLY_BRACKET && level == Level.RULE) { + // close brace after a rule, e.g. a{color:red;}<-- + propertyToken = null; + ruleToken = null; + newTokens = allTokens; + + level = levels.pop(); + seekingValue = false; + } else if (character == Marker.CLOSE_CURLY_BRACKET && level == Level.BLOCK && !isNested && position.index <= source.length - 1) { + // stray close brace at block level, e.g. a{color:red}color:blue}<-- + externalContext.warnings.push('Unexpected \'}\' at ' + formatPosition([position.line, position.column, position.source]) + '.'); + buffer.push(character); + } else if (character == Marker.CLOSE_CURLY_BRACKET && level == Level.BLOCK) { + // close brace at block level, e.g. @media screen {...}<-- + break; + } else if (character == Marker.OPEN_ROUND_BRACKET && level == Level.RULE && seekingValue) { + // round open bracket, e.g. a{color:hsla(<-- + buffer.push(character); + roundBracketLevel++; + } else if (character == Marker.CLOSE_ROUND_BRACKET && level == Level.RULE && seekingValue && roundBracketLevel == 1) { + // round close bracket, e.g. a{color:hsla(0,0%,0%)<-- + buffer.push(character); + serializedBuffer = buffer.join('').trim(); + propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + + roundBracketLevel--; + buffer = []; + } else if (character == Marker.CLOSE_ROUND_BRACKET && level == Level.RULE && seekingValue) { + // round close bracket within other brackets, e.g. a{width:calc((10rem / 2)<-- + buffer.push(character); + roundBracketLevel--; + } else if (character == Marker.FORWARD_SLASH && source[position.index + 1] != Marker.ASTERISK && level == Level.RULE && seekingValue && buffer.length > 0) { + // forward slash within a property, e.g. a{background:url(image.png) 0 0/<-- + serializedBuffer = buffer.join('').trim(); + propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + propertyToken.push([Token.PROPERTY_VALUE, character, [[position.line, position.column, position.source]]]); + + buffer = []; + } else if (character == Marker.FORWARD_SLASH && source[position.index + 1] != Marker.ASTERISK && level == Level.RULE && seekingValue) { + // forward slash within a property after space, e.g. a{background:url(image.png) 0 0 /<-- + propertyToken.push([Token.PROPERTY_VALUE, character, [[position.line, position.column, position.source]]]); + + buffer = []; + } else if (character == Marker.COMMA && level == Level.RULE && seekingValue && buffer.length > 0) { + // comma within a property, e.g. a{background:url(image.png),<-- + serializedBuffer = buffer.join('').trim(); + propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + propertyToken.push([Token.PROPERTY_VALUE, character, [[position.line, position.column, position.source]]]); + + buffer = []; + } else if (character == Marker.COMMA && level == Level.RULE && seekingValue) { + // comma within a property after space, e.g. a{background:url(image.png) ,<-- + propertyToken.push([Token.PROPERTY_VALUE, character, [[position.line, position.column, position.source]]]); + + buffer = []; + } else if ((isSpace || (isNewLineNix && !isNewLineWin)) && level == Level.RULE && seekingValue && propertyToken && buffer.length > 0) { + // space or *nix newline within property, e.g. a{margin:0 <-- + serializedBuffer = buffer.join('').trim(); + propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + + buffer = []; + } else if (isNewLineWin && level == Level.RULE && seekingValue && propertyToken && buffer.length > 1) { + // win newline within property, e.g. a{margin:0\r\n<-- + serializedBuffer = buffer.join('').trim(); + propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + + buffer = []; + } else if (isNewLineWin && level == Level.RULE && seekingValue) { + // win newline + buffer = []; + } else if (buffer.length == 1 && isNewLineWin) { + // ignore windows newline which is composed of two characters + buffer.pop(); + } else if (buffer.length > 0 || !isSpace && !isNewLineNix && !isNewLineWin) { + // any character + buffer.push(character); + } + + wasEscaped = isEscaped; + isEscaped = !wasEscaped && character == Marker.BACK_SLASH; + wasCommentStart = isCommentStart; + wasCommentEnd = isCommentEnd; + + position.line = (isNewLineWin || isNewLineNix) ? position.line + 1 : position.line; + position.column = (isNewLineWin || isNewLineNix) ? 0 : position.column + 1; + } + + if (seekingValue) { + externalContext.warnings.push('Missing \'}\' at ' + formatPosition([position.line, position.column, position.source]) + '.'); + } + + if (seekingValue && buffer.length > 0) { + serializedBuffer = buffer.join('').replace(TAIL_BROKEN_VALUE_PATTERN, ''); + propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]); + + buffer = []; + } + + if (buffer.length > 0) { + externalContext.warnings.push('Invalid character(s) \'' + buffer.join('') + '\' at ' + formatPosition(metadata) + '. Ignoring.'); + } + + return allTokens; +} + +function originalMetadata(metadata, value, externalContext, selectorFallbacks) { + var source = metadata[2]; + + return externalContext.inputSourceMapTracker.isTracking(source) ? + externalContext.inputSourceMapTracker.originalPositionFor(metadata, value.length, selectorFallbacks) : + metadata; +} + +function tokenTypeFrom(buffer) { + var isAtRule = buffer[0] == Marker.AT || buffer[0] == Marker.UNDERSCORE; + var ruleWord = buffer.join('').split(RULE_WORD_SEPARATOR_PATTERN)[0]; + + if (isAtRule && BLOCK_RULES.indexOf(ruleWord) > -1) { + return Token.NESTED_BLOCK; + } else if (isAtRule && AT_RULES.indexOf(ruleWord) > -1) { + return Token.AT_RULE; + } else if (isAtRule) { + return Token.AT_RULE_BLOCK; + } else { + return Token.RULE; + } +} + +function tokenScopeFrom(tokenType) { + if (tokenType == Token.RULE) { + return Token.RULE_SCOPE; + } else if (tokenType == Token.NESTED_BLOCK) { + return Token.NESTED_BLOCK_SCOPE; + } else if (tokenType == Token.AT_RULE_BLOCK) { + return Token.AT_RULE_BLOCK_SCOPE; + } +} + +module.exports = tokenize; diff --git a/node_modules/clean-css/lib/utils/clone-array.js b/node_modules/clean-css/lib/utils/clone-array.js new file mode 100644 index 000000000..b95ee6843 --- /dev/null +++ b/node_modules/clean-css/lib/utils/clone-array.js @@ -0,0 +1,12 @@ +function cloneArray(array) { + var cloned = array.slice(0); + + for (var i = 0, l = cloned.length; i < l; i++) { + if (Array.isArray(cloned[i])) + cloned[i] = cloneArray(cloned[i]); + } + + return cloned; +} + +module.exports = cloneArray; diff --git a/node_modules/clean-css/lib/utils/format-position.js b/node_modules/clean-css/lib/utils/format-position.js new file mode 100644 index 000000000..0e3713c19 --- /dev/null +++ b/node_modules/clean-css/lib/utils/format-position.js @@ -0,0 +1,11 @@ +function formatPosition(metadata) { + var line = metadata[0]; + var column = metadata[1]; + var source = metadata[2]; + + return source ? + source + ':' + line + ':' + column : + line + ':' + column; +} + +module.exports = formatPosition; diff --git a/node_modules/clean-css/lib/utils/has-protocol.js b/node_modules/clean-css/lib/utils/has-protocol.js new file mode 100644 index 000000000..fa1b61fd5 --- /dev/null +++ b/node_modules/clean-css/lib/utils/has-protocol.js @@ -0,0 +1,7 @@ +var NO_PROTOCOL_RESOURCE_PATTERN = /^\/\//; + +function hasProtocol(uri) { + return !NO_PROTOCOL_RESOURCE_PATTERN.test(uri); +} + +module.exports = hasProtocol; diff --git a/node_modules/clean-css/lib/utils/is-data-uri-resource.js b/node_modules/clean-css/lib/utils/is-data-uri-resource.js new file mode 100644 index 000000000..58558110f --- /dev/null +++ b/node_modules/clean-css/lib/utils/is-data-uri-resource.js @@ -0,0 +1,7 @@ +var DATA_URI_PATTERN = /^data:(\S*?)?(;charset=[^;]+)?(;[^,]+?)?,(.+)/; + +function isDataUriResource(uri) { + return DATA_URI_PATTERN.test(uri); +} + +module.exports = isDataUriResource; diff --git a/node_modules/clean-css/lib/utils/is-http-resource.js b/node_modules/clean-css/lib/utils/is-http-resource.js new file mode 100644 index 000000000..5179c2ea9 --- /dev/null +++ b/node_modules/clean-css/lib/utils/is-http-resource.js @@ -0,0 +1,7 @@ +var HTTP_RESOURCE_PATTERN = /^http:\/\//; + +function isHttpResource(uri) { + return HTTP_RESOURCE_PATTERN.test(uri); +} + +module.exports = isHttpResource; diff --git a/node_modules/clean-css/lib/utils/is-https-resource.js b/node_modules/clean-css/lib/utils/is-https-resource.js new file mode 100644 index 000000000..c6938f57d --- /dev/null +++ b/node_modules/clean-css/lib/utils/is-https-resource.js @@ -0,0 +1,7 @@ +var HTTPS_RESOURCE_PATTERN = /^https:\/\//; + +function isHttpsResource(uri) { + return HTTPS_RESOURCE_PATTERN.test(uri); +} + +module.exports = isHttpsResource; diff --git a/node_modules/clean-css/lib/utils/is-import.js b/node_modules/clean-css/lib/utils/is-import.js new file mode 100644 index 000000000..72abc3287 --- /dev/null +++ b/node_modules/clean-css/lib/utils/is-import.js @@ -0,0 +1,7 @@ +var IMPORT_PREFIX_PATTERN = /^@import/i; + +function isImport(value) { + return IMPORT_PREFIX_PATTERN.test(value); +} + +module.exports = isImport; diff --git a/node_modules/clean-css/lib/utils/is-remote-resource.js b/node_modules/clean-css/lib/utils/is-remote-resource.js new file mode 100644 index 000000000..fb3b61f3d --- /dev/null +++ b/node_modules/clean-css/lib/utils/is-remote-resource.js @@ -0,0 +1,7 @@ +var REMOTE_RESOURCE_PATTERN = /^(\w+:\/\/|\/\/)/; + +function isRemoteResource(uri) { + return REMOTE_RESOURCE_PATTERN.test(uri); +} + +module.exports = isRemoteResource; diff --git a/node_modules/clean-css/lib/utils/natural-compare.js b/node_modules/clean-css/lib/utils/natural-compare.js new file mode 100644 index 000000000..7a5246762 --- /dev/null +++ b/node_modules/clean-css/lib/utils/natural-compare.js @@ -0,0 +1,31 @@ +// adapted from http://nedbatchelder.com/blog/200712.html#e20071211T054956 + +var NUMBER_PATTERN = /([0-9]+)/; + +function naturalCompare(value1, value2) { + var keys1 = ('' + value1).split(NUMBER_PATTERN).map(tryParseInt); + var keys2 = ('' + value2).split(NUMBER_PATTERN).map(tryParseInt); + var key1; + var key2; + var compareFirst = Math.min(keys1.length, keys2.length); + var i, l; + + for (i = 0, l = compareFirst; i < l; i++) { + key1 = keys1[i]; + key2 = keys2[i]; + + if (key1 != key2) { + return key1 > key2 ? 1 : -1; + } + } + + return keys1.length > keys2.length ? 1 : (keys1.length == keys2.length ? 0 : -1); +} + +function tryParseInt(value) { + return ('' + parseInt(value)) == value ? + parseInt(value) : + value; +} + +module.exports = naturalCompare; diff --git a/node_modules/clean-css/lib/utils/override.js b/node_modules/clean-css/lib/utils/override.js new file mode 100644 index 000000000..e7f84948c --- /dev/null +++ b/node_modules/clean-css/lib/utils/override.js @@ -0,0 +1,34 @@ +function override(source1, source2) { + var target = {}; + var key1; + var key2; + var item; + + for (key1 in source1) { + item = source1[key1]; + + if (Array.isArray(item)) { + target[key1] = item.slice(0); + } else if (typeof item == 'object' && item !== null) { + target[key1] = override(item, {}); + } else { + target[key1] = item; + } + } + + for (key2 in source2) { + item = source2[key2]; + + if (key2 in target && Array.isArray(item)) { + target[key2] = item.slice(0); + } else if (key2 in target && typeof item == 'object' && item !== null) { + target[key2] = override(target[key2], item); + } else { + target[key2] = item; + } + } + + return target; +} + +module.exports = override; diff --git a/node_modules/clean-css/lib/utils/split.js b/node_modules/clean-css/lib/utils/split.js new file mode 100644 index 000000000..c91625506 --- /dev/null +++ b/node_modules/clean-css/lib/utils/split.js @@ -0,0 +1,50 @@ +var Marker = require('../tokenizer/marker'); + +function split(value, separator) { + var openLevel = Marker.OPEN_ROUND_BRACKET; + var closeLevel = Marker.CLOSE_ROUND_BRACKET; + var level = 0; + var cursor = 0; + var lastStart = 0; + var lastValue; + var lastCharacter; + var len = value.length; + var parts = []; + + if (value.indexOf(separator) == -1) { + return [value]; + } + + if (value.indexOf(openLevel) == -1) { + return value.split(separator); + } + + while (cursor < len) { + if (value[cursor] == openLevel) { + level++; + } else if (value[cursor] == closeLevel) { + level--; + } + + if (level === 0 && cursor > 0 && cursor + 1 < len && value[cursor] == separator) { + parts.push(value.substring(lastStart, cursor)); + lastStart = cursor + 1; + } + + cursor++; + } + + if (lastStart < cursor + 1) { + lastValue = value.substring(lastStart); + lastCharacter = lastValue[lastValue.length - 1]; + if (lastCharacter == separator) { + lastValue = lastValue.substring(0, lastValue.length - 1); + } + + parts.push(lastValue); + } + + return parts; +} + +module.exports = split; diff --git a/node_modules/clean-css/lib/writer/helpers.js b/node_modules/clean-css/lib/writer/helpers.js new file mode 100644 index 000000000..0b8999e95 --- /dev/null +++ b/node_modules/clean-css/lib/writer/helpers.js @@ -0,0 +1,219 @@ +var lineBreak = require('os').EOL; +var emptyCharacter = ''; + +var Breaks = require('../options/format').Breaks; +var Spaces = require('../options/format').Spaces; + +var Marker = require('../tokenizer/marker'); +var Token = require('../tokenizer/token'); + +function supportsAfterClosingBrace(token) { + return token[1][1] == 'background' || token[1][1] == 'transform' || token[1][1] == 'src'; +} + +function afterClosingBrace(token, valueIndex) { + return token[valueIndex][1][token[valueIndex][1].length - 1] == Marker.CLOSE_ROUND_BRACKET; +} + +function afterComma(token, valueIndex) { + return token[valueIndex][1] == Marker.COMMA; +} + +function afterSlash(token, valueIndex) { + return token[valueIndex][1] == Marker.FORWARD_SLASH; +} + +function beforeComma(token, valueIndex) { + return token[valueIndex + 1] && token[valueIndex + 1][1] == Marker.COMMA; +} + +function beforeSlash(token, valueIndex) { + return token[valueIndex + 1] && token[valueIndex + 1][1] == Marker.FORWARD_SLASH; +} + +function inFilter(token) { + return token[1][1] == 'filter' || token[1][1] == '-ms-filter'; +} + +function disallowsSpace(context, token, valueIndex) { + return !context.spaceAfterClosingBrace && supportsAfterClosingBrace(token) && afterClosingBrace(token, valueIndex) || + beforeSlash(token, valueIndex) || + afterSlash(token, valueIndex) || + beforeComma(token, valueIndex) || + afterComma(token, valueIndex); +} + +function rules(context, tokens) { + var store = context.store; + + for (var i = 0, l = tokens.length; i < l; i++) { + store(context, tokens[i]); + + if (i < l - 1) { + store(context, comma(context)); + } + } +} + +function body(context, tokens) { + var lastPropertyAt = lastPropertyIndex(tokens); + + for (var i = 0, l = tokens.length; i < l; i++) { + property(context, tokens, i, lastPropertyAt); + } +} + +function lastPropertyIndex(tokens) { + var index = tokens.length - 1; + + for (; index >= 0; index--) { + if (tokens[index][0] != Token.COMMENT) { + break; + } + } + + return index; +} + +function property(context, tokens, position, lastPropertyAt) { + var store = context.store; + var token = tokens[position]; + var isPropertyBlock = token[2][0] == Token.PROPERTY_BLOCK; + var needsSemicolon = position < lastPropertyAt || isPropertyBlock; + var isLast = position === lastPropertyAt; + + switch (token[0]) { + case Token.AT_RULE: + store(context, token); + store(context, position < lastPropertyAt ? semicolon(context, Breaks.AfterProperty, false) : emptyCharacter); + break; + case Token.COMMENT: + store(context, token); + break; + case Token.PROPERTY: + store(context, token[1]); + store(context, colon(context)); + value(context, token); + store(context, needsSemicolon ? semicolon(context, Breaks.AfterProperty, isLast) : emptyCharacter); + } +} + +function value(context, token) { + var store = context.store; + var j, m; + + if (token[2][0] == Token.PROPERTY_BLOCK) { + store(context, openBrace(context, Breaks.AfterBlockBegins, false)); + body(context, token[2][1]); + store(context, closeBrace(context, Breaks.AfterBlockEnds, false, true)); + } else { + for (j = 2, m = token.length; j < m; j++) { + store(context, token[j]); + + if (j < m - 1 && (inFilter(token) || !disallowsSpace(context, token, j))) { + store(context, Marker.SPACE); + } + } + } +} + +function allowsBreak(context, where) { + return context.format && context.format.breaks[where]; +} + +function allowsSpace(context, where) { + return context.format && context.format.spaces[where]; +} + +function openBrace(context, where, needsPrefixSpace) { + if (context.format) { + context.indentBy += context.format.indentBy; + context.indentWith = context.format.indentWith.repeat(context.indentBy); + return (needsPrefixSpace && allowsSpace(context, Spaces.BeforeBlockBegins) ? Marker.SPACE : emptyCharacter) + + Marker.OPEN_CURLY_BRACKET + + (allowsBreak(context, where) ? lineBreak : emptyCharacter) + + context.indentWith; + } else { + return Marker.OPEN_CURLY_BRACKET; + } +} + +function closeBrace(context, where, beforeBlockEnd, isLast) { + if (context.format) { + context.indentBy -= context.format.indentBy; + context.indentWith = context.format.indentWith.repeat(context.indentBy); + return (allowsBreak(context, Breaks.AfterProperty) || beforeBlockEnd && allowsBreak(context, Breaks.BeforeBlockEnds) ? lineBreak : emptyCharacter) + + context.indentWith + + Marker.CLOSE_CURLY_BRACKET + + (isLast ? emptyCharacter : (allowsBreak(context, where) ? lineBreak : emptyCharacter) + context.indentWith); + } else { + return Marker.CLOSE_CURLY_BRACKET; + } +} + +function colon(context) { + return context.format ? + Marker.COLON + (allowsSpace(context, Spaces.BeforeValue) ? Marker.SPACE : emptyCharacter) : + Marker.COLON; +} + +function semicolon(context, where, isLast) { + return context.format ? + Marker.SEMICOLON + (isLast || !allowsBreak(context, where) ? emptyCharacter : lineBreak + context.indentWith) : + Marker.SEMICOLON; +} + +function comma(context) { + return context.format ? + Marker.COMMA + (allowsBreak(context, Breaks.BetweenSelectors) ? lineBreak : emptyCharacter) + context.indentWith : + Marker.COMMA; +} + +function all(context, tokens) { + var store = context.store; + var token; + var isLast; + var i, l; + + for (i = 0, l = tokens.length; i < l; i++) { + token = tokens[i]; + isLast = i == l - 1; + + switch (token[0]) { + case Token.AT_RULE: + store(context, token); + store(context, semicolon(context, Breaks.AfterAtRule, isLast)); + break; + case Token.AT_RULE_BLOCK: + rules(context, token[1]); + store(context, openBrace(context, Breaks.AfterRuleBegins, true)); + body(context, token[2]); + store(context, closeBrace(context, Breaks.AfterRuleEnds, false, isLast)); + break; + case Token.NESTED_BLOCK: + rules(context, token[1]); + store(context, openBrace(context, Breaks.AfterBlockBegins, true)); + all(context, token[2]); + store(context, closeBrace(context, Breaks.AfterBlockEnds, true, isLast)); + break; + case Token.COMMENT: + store(context, token); + store(context, allowsBreak(context, Breaks.AfterComment) ? lineBreak : emptyCharacter); + break; + case Token.RULE: + rules(context, token[1]); + store(context, openBrace(context, Breaks.AfterRuleBegins, true)); + body(context, token[2]); + store(context, closeBrace(context, Breaks.AfterRuleEnds, false, isLast)); + break; + } + } +} + +module.exports = { + all: all, + body: body, + property: property, + rules: rules, + value: value +}; diff --git a/node_modules/clean-css/lib/writer/one-time.js b/node_modules/clean-css/lib/writer/one-time.js new file mode 100644 index 000000000..33fccead6 --- /dev/null +++ b/node_modules/clean-css/lib/writer/one-time.js @@ -0,0 +1,52 @@ +var helpers = require('./helpers'); + +function store(serializeContext, token) { + serializeContext.output.push(typeof token == 'string' ? token : token[1]); +} + +function context() { + var newContext = { + output: [], + store: store + }; + + return newContext; +} + +function all(tokens) { + var oneTimeContext = context(); + helpers.all(oneTimeContext, tokens); + return oneTimeContext.output.join(''); +} + +function body(tokens) { + var oneTimeContext = context(); + helpers.body(oneTimeContext, tokens); + return oneTimeContext.output.join(''); +} + +function property(tokens, position) { + var oneTimeContext = context(); + helpers.property(oneTimeContext, tokens, position, true); + return oneTimeContext.output.join(''); +} + +function rules(tokens) { + var oneTimeContext = context(); + helpers.rules(oneTimeContext, tokens); + return oneTimeContext.output.join(''); +} + +function value(tokens) { + var oneTimeContext = context(); + helpers.value(oneTimeContext, tokens); + return oneTimeContext.output.join(''); +} + +module.exports = { + all: all, + body: body, + property: property, + rules: rules, + value: value +}; diff --git a/node_modules/clean-css/lib/writer/simple.js b/node_modules/clean-css/lib/writer/simple.js new file mode 100644 index 000000000..21e7f88a7 --- /dev/null +++ b/node_modules/clean-css/lib/writer/simple.js @@ -0,0 +1,52 @@ +var all = require('./helpers').all; + +var lineBreak = require('os').EOL; + +function store(serializeContext, token) { + var value = typeof token == 'string' ? + token : + token[1]; + var wrap = serializeContext.wrap; + + wrap(serializeContext, value); + track(serializeContext, value); + serializeContext.output.push(value); +} + +function wrap(serializeContext, value) { + if (serializeContext.column + value.length > serializeContext.format.wrapAt) { + track(serializeContext, lineBreak); + serializeContext.output.push(lineBreak); + } +} + +function track(serializeContext, value) { + var parts = value.split('\n'); + + serializeContext.line += parts.length - 1; + serializeContext.column = parts.length > 1 ? 0 : (serializeContext.column + parts.pop().length); +} + +function serializeStyles(tokens, context) { + var serializeContext = { + column: 0, + format: context.options.format, + indentBy: 0, + indentWith: '', + line: 1, + output: [], + spaceAfterClosingBrace: context.options.compatibility.properties.spaceAfterClosingBrace, + store: store, + wrap: context.options.format.wrapAt ? + wrap : + function () { /* noop */ } + }; + + all(serializeContext, tokens); + + return { + styles: serializeContext.output.join('') + }; +} + +module.exports = serializeStyles; diff --git a/node_modules/clean-css/lib/writer/source-maps.js b/node_modules/clean-css/lib/writer/source-maps.js new file mode 100644 index 000000000..4729eb055 --- /dev/null +++ b/node_modules/clean-css/lib/writer/source-maps.js @@ -0,0 +1,102 @@ +var SourceMapGenerator = require('source-map').SourceMapGenerator; +var all = require('./helpers').all; + +var lineBreak = require('os').EOL; +var isRemoteResource = require('../utils/is-remote-resource'); + +var isWindows = process.platform == 'win32'; + +var NIX_SEPARATOR_PATTERN = /\//g; +var UNKNOWN_SOURCE = '$stdin'; +var WINDOWS_SEPARATOR = '\\'; + +function store(serializeContext, element) { + var fromString = typeof element == 'string'; + var value = fromString ? element : element[1]; + var mappings = fromString ? null : element[2]; + var wrap = serializeContext.wrap; + + wrap(serializeContext, value); + track(serializeContext, value, mappings); + serializeContext.output.push(value); +} + +function wrap(serializeContext, value) { + if (serializeContext.column + value.length > serializeContext.format.wrapAt) { + track(serializeContext, lineBreak, false); + serializeContext.output.push(lineBreak); + } +} + +function track(serializeContext, value, mappings) { + var parts = value.split('\n'); + + if (mappings) { + trackAllMappings(serializeContext, mappings); + } + + serializeContext.line += parts.length - 1; + serializeContext.column = parts.length > 1 ? 0 : (serializeContext.column + parts.pop().length); +} + +function trackAllMappings(serializeContext, mappings) { + for (var i = 0, l = mappings.length; i < l; i++) { + trackMapping(serializeContext, mappings[i]); + } +} + +function trackMapping(serializeContext, mapping) { + var line = mapping[0]; + var column = mapping[1]; + var originalSource = mapping[2]; + var source = originalSource; + var storedSource = source || UNKNOWN_SOURCE; + + if (isWindows && source && !isRemoteResource(source)) { + storedSource = source.replace(NIX_SEPARATOR_PATTERN, WINDOWS_SEPARATOR); + } + + serializeContext.outputMap.addMapping({ + generated: { + line: serializeContext.line, + column: serializeContext.column + }, + source: storedSource, + original: { + line: line, + column: column + } + }); + + if (serializeContext.inlineSources && (originalSource in serializeContext.sourcesContent)) { + serializeContext.outputMap.setSourceContent(storedSource, serializeContext.sourcesContent[originalSource]); + } +} + +function serializeStylesAndSourceMap(tokens, context) { + var serializeContext = { + column: 0, + format: context.options.format, + indentBy: 0, + indentWith: '', + inlineSources: context.options.sourceMapInlineSources, + line: 1, + output: [], + outputMap: new SourceMapGenerator(), + sourcesContent: context.sourcesContent, + spaceAfterClosingBrace: context.options.compatibility.properties.spaceAfterClosingBrace, + store: store, + wrap: context.options.format.wrapAt ? + wrap : + function () { /* noop */ } + }; + + all(serializeContext, tokens); + + return { + sourceMap: serializeContext.outputMap, + styles: serializeContext.output.join('') + }; +} + +module.exports = serializeStylesAndSourceMap; diff --git a/node_modules/clean-css/package.json b/node_modules/clean-css/package.json new file mode 100644 index 000000000..f8a8db1f4 --- /dev/null +++ b/node_modules/clean-css/package.json @@ -0,0 +1,48 @@ +{ + "name": "clean-css", + "version": "4.0.12", + "author": "Jakub Pawlowicz <contact@jakubpawlowicz.com> (http://twitter.com/jakubpawlowicz)", + "description": "A well-tested CSS minifier", + "license": "MIT", + "keywords": [ + "css", + "minifier" + ], + "homepage": "https://github.com/jakubpawlowicz/clean-css", + "repository": { + "type": "git", + "url": "https://github.com/jakubpawlowicz/clean-css.git" + }, + "bugs": { + "url": "https://github.com/jakubpawlowicz/clean-css/issues" + }, + "main": "index.js", + "files": [ + "lib", + "History.md", + "index.js", + "LICENSE" + ], + "scripts": { + "browserify": "browserify --standalone CleanCSS index.js | uglifyjs --compress --mangle -o cleancss-browser.js", + "bench": "node ./test/bench.js", + "check": "jshint .", + "prepublish": "npm run check", + "test": "vows" + }, + "dependencies": { + "source-map": "0.5.x" + }, + "devDependencies": { + "browserify": "13.x", + "http-proxy": "1.x", + "jshint": "2.x", + "nock": "9.x", + "server-destroy": "1.x", + "uglify-js": ">=2.6.1", + "vows": "0.8.x" + }, + "engines": { + "node": ">= 4.0" + } +} |