diff options
author | Florian Dold <florian.dold@gmail.com> | 2017-05-24 15:10:37 +0200 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2017-05-24 15:11:17 +0200 |
commit | 7a3df06eb573d36142bd1a8e03c5ce8752d300b3 (patch) | |
tree | 70bfaea8884c374876f607774850a3a51c0cb381 /node_modules/clean-css | |
parent | aca1143cb9eed16cf37f04e475e4257418dd18ac (diff) |
fix build issues and add typedoc
Diffstat (limited to 'node_modules/clean-css')
35 files changed, 1764 insertions, 484 deletions
diff --git a/node_modules/clean-css/History.md b/node_modules/clean-css/History.md index 3cf6682ef..2a4bab0c3 100644 --- a/node_modules/clean-css/History.md +++ b/node_modules/clean-css/History.md @@ -1,3 +1,39 @@ +[4.1.3 / 2017-05-18](https://github.com/jakubpawlowicz/clean-css/compare/v4.1.2...v4.1.3) +================== + +* Fixed issue [#946](https://github.com/jakubpawlowicz/clean-css/issues/946) - tokenizing `-ms-grid-columns` repeat syntax. + +[4.1.2 / 2017-05-10](https://github.com/jakubpawlowicz/clean-css/compare/v4.1.1...v4.1.2) +================== + +* Fixed issue [#939](https://github.com/jakubpawlowicz/clean-css/issues/939) - semicolon after `@apply` at rule. +* Fixed issue [#940](https://github.com/jakubpawlowicz/clean-css/issues/940) - handling more `font` keywords. +* Fixed issue [#941](https://github.com/jakubpawlowicz/clean-css/issues/941) - breaking up vendor prefixed `animation`. + +[4.1.1 / 2017-05-08](https://github.com/jakubpawlowicz/clean-css/compare/v4.1.0...v4.1.1) +================== + +* Fixed issue [#938](https://github.com/jakubpawlowicz/clean-css/issues/938) - removing unused at-rules with `!important`. + +[4.1.0 / 2017-05-07](https://github.com/jakubpawlowicz/clean-css/compare/4.0...v4.1.0) +================== + +* Improves longhand-into-shorthand merging mechanism in complex cases as with `border-*` shorthands. +* Fixed issue [#254](https://github.com/jakubpawlowicz/clean-css/issues/254) - adds `font` property optimizer. +* Fixed issue [#525](https://github.com/jakubpawlowicz/clean-css/issues/525) - restores `inherit`-based merging. +* Fixed issue [#755](https://github.com/jakubpawlowicz/clean-css/issues/755) - adds custom handling of remote requests. +* Fixed issue [#860](https://github.com/jakubpawlowicz/clean-css/issues/860) - adds `animation` property optimizer. +* Fixed issue [#862](https://github.com/jakubpawlowicz/clean-css/issues/862) - allows removing unused at rules. +* Fixed issue [#886](https://github.com/jakubpawlowicz/clean-css/issues/886) - better multi pseudo class / element merging. +* Fixed issue [#890](https://github.com/jakubpawlowicz/clean-css/issues/890) - adds toggle to disable empty tokens removal. +* Fixed issue [#893](https://github.com/jakubpawlowicz/clean-css/issues/893) - `inline: false` as alias to `inline: 'none'`. +* Fixed issue [#905](https://github.com/jakubpawlowicz/clean-css/issues/905) - allows disabling selector sorting. +* Fixed issue [#906](https://github.com/jakubpawlowicz/clean-css/issues/906) - improves usability of web UI settings. +* Fixed issue [#908](https://github.com/jakubpawlowicz/clean-css/issues/908) - improved `minify` method signature. +* Fixed issue [#916](https://github.com/jakubpawlowicz/clean-css/issues/916) - maximum number of merged selectors. +* Fixed issue [#920](https://github.com/jakubpawlowicz/clean-css/issues/920) - allows skipping certain properties in level 2 optimizations. +* Fixed issue [#934](https://github.com/jakubpawlowicz/clean-css/issues/934) - smarter longhand into shorthand merging. + [4.0.12 / 2017-04-12](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.11...v4.0.12) ================== @@ -13,7 +49,7 @@ * 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. +* Refixed issue [#556](https://github.com/jakubpawlowicz/clean-css/issues/556) - IE backslash hacks. [4.0.9 / 2017-03-15](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.8...v4.0.9) ================== @@ -55,7 +91,7 @@ [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. +* Fixed issue [#875](https://github.com/jakubpawlowicz/clean-css/issues/875) - invalid traversing in semantic merging. [4.0.2 / 2017-01-26](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.1...v4.0.2) ================== @@ -120,6 +156,11 @@ * 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.25 / 2017-02-22](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.24...v3.4.25) +================== + +* Fixed issue [#897](https://github.com/jakubpawlowicz/clean-css/issues/897) - tokenization with escaped markers. + [3.4.24 / 2017-01-20](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.23...v3.4.24) ================== diff --git a/node_modules/clean-css/README.md b/node_modules/clean-css/README.md index a19594801..b240f4381 100644 --- a/node_modules/clean-css/README.md +++ b/node_modules/clean-css/README.md @@ -22,8 +22,10 @@ According to [tests](http://goalsmashers.github.io/css-minification-benchmark/) - [Install](#install) - [Use](#use) * [Important: 4.0 breaking changes](#important-40-breaking-changes) + * [What's new in version 4.1](#whats-new-in-version-41) * [Constructor options](#constructor-options) * [Compatibility modes](#compatibility-modes) + * [Fetch option](#fetch-option) * [Formatting options](#formatting-options) * [Inlining options](#inlining-options) * [Optimization levels](#optimization-levels) @@ -32,6 +34,7 @@ According to [tests](http://goalsmashers.github.io/css-minification-benchmark/) + [Level 2 optimizations](#level-2-optimizations) * [Minify method](#minify-method) * [Promise interface](#promise-interface) + * [CLI utility](#cli-utility) - [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) @@ -90,11 +93,31 @@ clean-css 4.0 introduces some breaking changes: * `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. +## What's new in version 4.1 + +clean-css 4.1 introduces the following changes / features: + +* `inline: false` as an alias to `inline: ['none']`; +* `multiplePseudoMerging` compatibility flag controlling merging of rules with multiple pseudo classes / elements; +* `removeEmpty` flag in level 1 optimizations controlling removal of rules and nested blocks; +* `removeEmpty` flag in level 2 optimizations controlling removal of rules and nested blocks; +* `compatibility: { selectors: { mergeLimit: <number> } }` flag in compatibility settings controlling maximum number of selectors in a single rule; +* `minify` method improved signature accepting a list of hashes for a predictable traversal; +* `selectorsSortingMethod` level 1 optimization allows `false` or `'none'` for disabling selector sorting; +* `fetch` option controlling a function for handling remote requests; +* new `font` shorthand and `font-*` longhand optimizers; +* removal of `optimizeFont` flag in level 1 optimizations due to new `font` shorthand optimizer; +* `skipProperties` flag in level 2 optimizations controlling which properties won't be optimized; +* new `animation` shorthand and `animation-*` longhand optimizers; +* `removeUnusedAtRules` level 2 optimization controlling removal of unused `@counter-style`, `@font-face`, `@keyframes`, and `@namespace` at rules; +* the [web interface](https://jakubpawlowicz.github.io/clean-css) gets an improved settings panel with "reset to defaults", instant option changes, and settings being persisted across sessions. + ## 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; +* `fetch` - controls a function for handling remote requests; see [fetch option](#fetch-option) for examples (since 4.1.0); * `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); @@ -142,7 +165,9 @@ new CleanCSS({ 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 + mergeablePseudoElements: ['::after', ...], // controls a whitelist of mergeable pseudo elements + mergeLimit: 8191, // controls maximum number of selectors in a single rule (since 4.1.0) + multiplePseudoMerging: true // controls merging of rules with multiple pseudo classes / elements (since 4.1.0) }, units: { ch: true, // controls treating `ch` as a supported unit @@ -167,9 +192,52 @@ new CleanCSS({ }) ``` +## Fetch option + +The `fetch` option accepts a function which handles remote resource fetching, e.g. + +```js +var request = require('request'); +var source = '@import url(http://example.com/path/to/stylesheet.css);'; +new CleanCSS({ + fetch: function (uri, inlineRequest, inlineTimeout, callback) { + request(uri, function (error, response, body) { + if (error) { + callback(error, null); + } else if (response && response.statusCode != 200) { + callback(response.statusCode, null); + } else { + callback(null, body); + } + }); + } +}).minify(source); +``` + +This option provides a convenient way of overriding the default fetching logic if it doesn't support a particular feature, say CONNECT proxies. + +Unless given, the default [loadRemoteResource](https://github.com/jakubpawlowicz/clean-css/blob/master/lib/reader/load-remote-resource.js) logic is used. + ## Formatting options -The `format` option accept the following options: +By default output CSS is formatted without any whitespace unless a `format` option is given. +First of all there are two shorthands: + +```js +new CleanCSS({ + format: 'beautify' // formats output in a really nice way +}) +``` + +and + +```js +new CleanCSS({ + format: 'keep-breaks' // formats output the default way but adds line breaks for improved readability +}) +``` + +however `format` option also accept a fine-grained set of options: ```js new CleanCSS({ @@ -203,25 +271,39 @@ new CleanCSS({ ```js new CleanCSS({ - inline: ['local'] // default + inline: ['local'] // default; enables local inlining only +}) +``` + +```js +new CleanCSS({ + inline: ['none'] // disables all inlining +}) +``` + +```js +// introduced in clean-css 4.1.0 + +new CleanCSS({ + inline: false // disables all inlining (alias to `['none']`) }) ``` ```js new CleanCSS({ - inline: ['all'] // same as ['local', 'remote'] + inline: ['all'] // enables all inlining, same as ['local', 'remote'] }) ``` ```js new CleanCSS({ - inline: ['local', 'mydomain.example.com'] + inline: ['local', 'mydomain.example.com'] // enables local inlining plus given remote source }) ``` ```js new CleanCSS({ - inline: ['local', 'remote', '!fonts.googleapis.com'] + inline: ['local', 'remote', '!fonts.googleapis.com'] // enables all inlining but from given remote source }) ``` @@ -255,12 +337,13 @@ new CleanCSS({ 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` + optimizeBackground: true, // controls `background` property optimizations; defaults to `true` + optimizeBorderRadius: true, // controls `border-radius` property optimizations; defaults to `true` + optimizeFilter: true, // controls `filter` property optimizations; defaults to `true` + optimizeFont: true, // controls `font` property optimizations; defaults to `true` + optimizeFontWeight: true, // controls `font-weight` property optimizations; defaults to `true` + optimizeOutline: true, // controls `outline` property optimizations; defaults to `true` + removeEmpty: true, // controls removing empty rules and nested blocks; 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` @@ -268,7 +351,7 @@ new CleanCSS({ 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` + selectorsSortingMethod: 'standard', // denotes selector sorting method; can be `'natural'` or `'standard'`, `'none'`, or false (the last two since 4.1.0); 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` @@ -310,11 +393,14 @@ new CleanCSS({ 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 + removeEmpty: true, // controls removing empty rules and nested blocks; 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 + removeUnusedAtRules: false, // controls unused at rule removing; defaults to false (available since 4.1.0) + restructureRules: false, // controls rule restructuring; defaults to false + skipProperties: [] // controls which properties won't be optimized, defaults to `[]` which means all will be optimized (since 4.1.0) } } }); @@ -379,11 +465,15 @@ new CleanCSS({ returnPromise: true }) .catch(function (error) { // deal with errors }); ``` +## CLI utility + +Clean-css has an associated command line utility that can be installed separately using `npm install clean-css-cli`. For more detailed information, please visit https://github.com/jakubpawlowicz/clean-css-cli. + # 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: +It can be done either by passing an array of paths, or, when sources are already available, a hash or an array of hashes: ```js new CleanCSS().minify(['path/to/file/one', 'path/to/file/two']); @@ -400,6 +490,15 @@ new CleanCSS().minify({ }); ``` +```js +new CleanCSS().minify([ + {'path/to/file/one': {styles: 'contents of file one'}}, + {'path/to/file/two': {styles: 'contents of file two'}} +]); +``` + +Passing an array of hashes allows you to explicitly specify the order in which the input files are concatenated. Whereas when you use a single hash the order is determined by the [traversal order of object properties](http://2ality.com/2015/10/property-traversal-order-es6.html) - available since 4.1.0. + Important note - any `@import` rules already present in the hash will be resolved in memory. ## How to process remote `@import`s correctly? @@ -604,6 +703,7 @@ Sorted alphabetically by GitHub handle: * [@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; +* [@grandrath](https://github.com/grandrath) (Martin Grandrath) for improving `minify` method source traversal in ES6; * [@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; diff --git a/node_modules/clean-css/lib/clean.js b/node_modules/clean-css/lib/clean.js index f8268777b..62308efec 100644 --- a/node_modules/clean-css/lib/clean.js +++ b/node_modules/clean-css/lib/clean.js @@ -11,6 +11,7 @@ var level2Optimize = require('./optimizer/level-2/optimize'); var validator = require('./optimizer/validator'); var compatibilityFrom = require('./options/compatibility'); +var fetchFrom = require('./options/fetch'); var formatFrom = require('./options/format').formatFrom; var inlineFrom = require('./options/inline'); var inlineRequestFrom = require('./options/inline-request'); @@ -31,6 +32,7 @@ var CleanCSS = module.exports = function CleanCSS(options) { this.options = { compatibility: compatibilityFrom(options.compatibility), + fetch: fetchFrom(options.fetch), format: formatFrom(options.format), inline: inlineFrom(options.inline), inlineRequest: inlineRequestFrom(options.inlineRequest), diff --git a/node_modules/clean-css/lib/optimizer/level-1/optimize.js b/node_modules/clean-css/lib/optimizer/level-1/optimize.js index e7a9bbb47..5a6da47c0 100644 --- a/node_modules/clean-css/lib/optimizer/level-1/optimize.js +++ b/node_modules/clean-css/lib/optimizer/level-1/optimize.js @@ -26,10 +26,6 @@ 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)$/; @@ -159,63 +155,6 @@ function optimizeFilter(property) { .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]; @@ -483,7 +422,7 @@ function optimizeBody(properties, context) { break; } - if (valueIsUrl && !context.validator.isValidUrl(value)) { + if (valueIsUrl && !context.validator.isUrl(value)) { property.unused = true; context.warnings.push('Broken URL \'' + value + '\' at ' + formatPosition(property.value[j][2][0]) + '. Ignoring.'); break; @@ -543,8 +482,6 @@ function optimizeBody(properties, context) { 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) { @@ -717,7 +654,7 @@ function level1Optimize(tokens, context) { break; } - if (token[1].length === 0 || (token[2] && token[2].length === 0)) { + if (levelOptions.removeEmpty && (token[1].length === 0 || (token[2] && token[2].length === 0))) { tokens.splice(i, 1); i--; l--; 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 index 2f179e6ef..5b261dfb0 100644 --- a/node_modules/clean-css/lib/optimizer/level-1/sort-selectors.js +++ b/node_modules/clean-css/lib/optimizer/level-1/sort-selectors.js @@ -9,17 +9,15 @@ function standardSorter(scope1, scope2) { } function sortSelectors(selectors, method) { - var sorter; - switch (method) { case 'natural': - sorter = naturalSorter; - break; + return selectors.sort(naturalSorter); case 'standard': - sorter = standardSorter; + return selectors.sort(standardSorter); + case 'none': + case false: + return selectors; } - - return selectors.sort(sorter); } module.exports = sortSelectors; 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 index 9e44d5a1f..b48ccf23d 100644 --- a/node_modules/clean-css/lib/optimizer/level-2/break-up.js +++ b/node_modules/clean-css/lib/optimizer/level-2/break-up.js @@ -3,20 +3,31 @@ var InvalidPropertyError = require('./invalid-property-error'); var wrapSingle = require('../wrap-for-optimizing').single; var Token = require('../../tokenizer/token'); +var Marker = require('../../tokenizer/marker'); var formatPosition = require('../../utils/format-position'); -var MULTIPLEX_SEPARATOR = ','; +function _anyIsInherit(values) { + var i, l; + + for (i = 0, l = values.length; i < l; i++) { + if (values[i][1] == 'inherit') { + return true; + } + } + + return false; +} function _colorFilter(validator) { return function (value) { - return value[1] == 'invert' || validator.isValidColor(value[1]) || validator.isValidVendorPrefixedValue(value[1]); + return value[1] == 'invert' || validator.isColor(value[1]) || validator.isPrefixed(value[1]); }; } function _styleFilter(validator) { return function (value) { - return value[1] != 'inherit' && validator.isValidStyle(value[1]) && !validator.isValidColorValue(value[1]); + return value[1] != 'inherit' && validator.isStyleKeyword(value[1]) && !validator.isColorFunction(value[1]); }; } @@ -46,10 +57,80 @@ function _wrapDefault(name, property, compactable) { function _widthFilter(validator) { return function (value) { - return value[1] != 'inherit' && validator.isValidWidth(value[1]) && !validator.isValidStyle(value[1]) && !validator.isValidColorValue(value[1]); + return value[1] != 'inherit' && + (validator.isWidth(value[1]) || validator.isUnit(value[1]) && !validator.isDynamicUnit(value[1])) && + !validator.isStyleKeyword(value[1]) && + !validator.isColorFunction(value[1]); }; } +function animation(property, compactable, validator) { + var duration = _wrapDefault(property.name + '-duration', property, compactable); + var timing = _wrapDefault(property.name + '-timing-function', property, compactable); + var delay = _wrapDefault(property.name + '-delay', property, compactable); + var iteration = _wrapDefault(property.name + '-iteration-count', property, compactable); + var direction = _wrapDefault(property.name + '-direction', property, compactable); + var fill = _wrapDefault(property.name + '-fill-mode', property, compactable); + var play = _wrapDefault(property.name + '-play-state', property, compactable); + var name = _wrapDefault(property.name + '-name', property, compactable); + var components = [duration, timing, delay, iteration, direction, fill, play, name]; + var values = property.value; + var value; + var durationSet = false; + var timingSet = false; + var delaySet = false; + var iterationSet = false; + var directionSet = false; + var fillSet = false; + var playSet = false; + var nameSet = false; + var i; + var l; + + if (property.value.length == 1 && property.value[0][1] == 'inherit') { + duration.value = timing.value = delay.value = iteration.value = direction.value = fill.value = play.value = name.value = property.value; + return components; + } + + if (values.length > 1 && _anyIsInherit(values)) { + throw new InvalidPropertyError('Invalid animation values at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); + } + + for (i = 0, l = values.length; i < l; i++) { + value = values[i]; + + if (validator.isTime(value[1]) && !durationSet) { + duration.value = [value]; + durationSet = true; + } else if (validator.isTime(value[1]) && !delaySet) { + delay.value = [value]; + delaySet = true; + } else if ((validator.isGlobal(value[1]) || validator.isAnimationTimingFunction(value[1])) && !timingSet) { + timing.value = [value]; + timingSet = true; + } else if ((validator.isAnimationIterationCountKeyword(value[1]) || validator.isPositiveNumber(value[1])) && !iterationSet) { + iteration.value = [value]; + iterationSet = true; + } else if (validator.isAnimationDirectionKeyword(value[1]) && !directionSet) { + direction.value = [value]; + directionSet = true; + } else if (validator.isAnimationFillModeKeyword(value[1]) && !fillSet) { + fill.value = [value]; + fillSet = true; + } else if (validator.isAnimationPlayStateKeyword(value[1]) && !playSet) { + play.value = [value]; + playSet = true; + } else if ((validator.isAnimationNameKeyword(value[1]) || validator.isIdentifier(value[1])) && !nameSet) { + name.value = [value]; + nameSet = true; + } else { + throw new InvalidPropertyError('Invalid animation value at ' + formatPosition(value[2][0]) + '. Ignoring.'); + } + } + + return components; +} + function background(property, compactable, validator) { var image = _wrapDefault('background-image', property, compactable); var position = _wrapDefault('background-position', property, compactable); @@ -82,10 +163,10 @@ function background(property, compactable, validator) { for (var i = values.length - 1; i >= 0; i--) { var value = values[i]; - if (validator.isValidBackgroundAttachment(value[1])) { + if (validator.isBackgroundAttachmentKeyword(value[1])) { attachment.value = [value]; anyValueSet = true; - } else if (validator.isValidBackgroundClip(value[1]) || validator.isValidBackgroundOrigin(value[1])) { + } else if (validator.isBackgroundClipKeyword(value[1]) || validator.isBackgroundOriginKeyword(value[1])) { if (clipSet) { origin.value = [value]; originSet = true; @@ -94,7 +175,7 @@ function background(property, compactable, validator) { clipSet = true; } anyValueSet = true; - } else if (validator.isValidBackgroundRepeat(value[1])) { + } else if (validator.isBackgroundRepeatKeyword(value[1])) { if (repeatSet) { repeat.value.unshift(value); } else { @@ -102,13 +183,13 @@ function background(property, compactable, validator) { repeatSet = true; } anyValueSet = true; - } else if (validator.isValidBackgroundPositionPart(value[1]) || validator.isValidBackgroundSizePart(value[1])) { + } else if (validator.isBackgroundPositionKeyword(value[1]) || validator.isBackgroundSizeKeyword(value[1]) || validator.isUnit(value[1]) || validator.isDynamicUnit(value[1])) { if (i > 0) { var previousValue = values[i - 1]; - if (previousValue[1] == '/') { + if (previousValue[1] == Marker.FORWARD_SLASH) { size.value = [value]; - } else if (i > 1 && values[i - 2][1] == '/') { + } else if (i > 1 && values[i - 2][1] == Marker.FORWARD_SLASH) { size.value = [previousValue, value]; i -= 2; } else { @@ -126,10 +207,10 @@ function background(property, compactable, validator) { 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]))) { + } else if ((color.value[0][1] == compactable[color.name].defaultValue || color.value[0][1] == 'none') && (validator.isColor(value[1]) || validator.isPrefixed(value[1]))) { color.value = [value]; anyValueSet = true; - } else if (validator.isValidUrl(value[1]) || validator.isValidFunction(value[1])) { + } else if (validator.isUrl(value[1]) || validator.isFunction(value[1])) { image.value = [value]; anyValueSet = true; } @@ -150,7 +231,7 @@ function borderRadius(property, compactable) { var splitAt = -1; for (var i = 0, l = values.length; i < l; i++) { - if (values[i][1] == '/') { + if (values[i][1] == Marker.FORWARD_SLASH) { splitAt = i; break; } @@ -180,6 +261,122 @@ function borderRadius(property, compactable) { return target.components; } +function font(property, compactable, validator) { + var style = _wrapDefault('font-style', property, compactable); + var variant = _wrapDefault('font-variant', property, compactable); + var weight = _wrapDefault('font-weight', property, compactable); + var stretch = _wrapDefault('font-stretch', property, compactable); + var size = _wrapDefault('font-size', property, compactable); + var height = _wrapDefault('line-height', property, compactable); + var family = _wrapDefault('font-family', property, compactable); + var components = [style, variant, weight, stretch, size, height, family]; + var values = property.value; + var fuzzyMatched = 4; // style, variant, weight, and stretch + var index = 0; + var isStretchSet = false; + var isStretchValid; + var isStyleSet = false; + var isStyleValid; + var isVariantSet = false; + var isVariantValid; + var isWeightSet = false; + var isWeightValid; + var isSizeSet = false; + var appendableFamilyName = false; + + if (!values[index]) { + throw new InvalidPropertyError('Missing font values at ' + formatPosition(property.all[property.position][1][2][0]) + '. Ignoring.'); + } + + if (values.length == 1 && values[0][1] == 'inherit') { + style.value = variant.value = weight.value = stretch.value = size.value = height.value = family.value = values; + return components; + } + + if (values.length == 1 && (validator.isFontKeyword(values[0][1]) || validator.isGlobal(values[0][1]) || validator.isPrefixed(values[0][1]))) { + values[0][1] = Marker.INTERNAL + values[0][1]; + style.value = variant.value = weight.value = stretch.value = size.value = height.value = family.value = values; + return components; + } + + if (values.length > 1 && _anyIsInherit(values)) { + throw new InvalidPropertyError('Invalid font values at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); + } + + // fuzzy match style, variant, weight, and stretch on first elements + while (index < fuzzyMatched) { + isStretchValid = validator.isFontStretchKeyword(values[index][1]) || validator.isGlobal(values[index][1]); + isStyleValid = validator.isFontStyleKeyword(values[index][1]) || validator.isGlobal(values[index][1]); + isVariantValid = validator.isFontVariantKeyword(values[index][1]) || validator.isGlobal(values[index][1]); + isWeightValid = validator.isFontWeightKeyword(values[index][1]) || validator.isGlobal(values[index][1]); + + if (isStyleValid && !isStyleSet) { + style.value = [values[index]]; + isStyleSet = true; + } else if (isVariantValid && !isVariantSet) { + variant.value = [values[index]]; + isVariantSet = true; + } else if (isWeightValid && !isWeightSet) { + weight.value = [values[index]]; + isWeightSet = true; + } else if (isStretchValid && !isStretchSet) { + stretch.value = [values[index]]; + isStretchSet = true; + } else if (isStyleValid && isStyleSet || isVariantValid && isVariantSet || isWeightValid && isWeightSet || isStretchValid && isStretchSet) { + throw new InvalidPropertyError('Invalid font style / variant / weight / stretch value at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); + } else { + break; + } + + index++; + } + + // now comes font-size ... + if (validator.isFontSizeKeyword(values[index][1]) || validator.isUnit(values[index][1]) && !validator.isDynamicUnit(values[index][1])) { + size.value = [values[index]]; + isSizeSet = true; + index++; + } else { + throw new InvalidPropertyError('Missing font size at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); + } + + if (!values[index]) { + throw new InvalidPropertyError('Missing font family at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); + } + + // ... and perhaps line-height + if (isSizeSet && values[index] && values[index][1] == Marker.FORWARD_SLASH && values[index + 1] && (validator.isLineHeightKeyword(values[index + 1][1]) || validator.isUnit(values[index + 1][1]) || validator.isNumber(values[index + 1][1]))) { + height.value = [values[index + 1]]; + index++; + index++; + } + + // ... and whatever comes next is font-family + family.value = []; + + while (values[index]) { + if (values[index][1] == Marker.COMMA) { + appendableFamilyName = false; + } else { + if (appendableFamilyName) { + family.value[family.value.length - 1][1] += Marker.SPACE + values[index][1]; + } else { + family.value.push(values[index]); + } + + appendableFamilyName = true; + } + + index++; + } + + if (family.value.length === 0) { + throw new InvalidPropertyError('Missing font family at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); + } + + return components; +} + function fourValues(property, compactable) { var componentNames = compactable[property.name].components; var components = []; @@ -242,7 +439,7 @@ function multiplex(splitWith) { components[i].multiplex = true; for (j = 1, m = splitComponents.length; j < m; j++) { - components[i].value.push([Token.PROPERTY_VALUE, MULTIPLEX_SEPARATOR]); + components[i].value.push([Token.PROPERTY_VALUE, Marker.COMMA]); Array.prototype.push.apply(components[i].value, splitComponents[j][i].value); } } @@ -268,25 +465,26 @@ function listStyle(property, compactable, validator) { // `image` first... for (index = 0, total = values.length; index < total; index++) { - if (validator.isValidUrl(values[index][1]) || values[index][1] == '0') { + if (validator.isUrl(values[index][1]) || values[index][1] == '0') { image.value = [values[index]]; values.splice(index, 1); break; } } - // ... then `type`... + // ... then `position` for (index = 0, total = values.length; index < total; index++) { - if (validator.isValidListStyleType(values[index][1])) { - type.value = [values[index]]; + if (validator.isListStylePositionKeyword(values[index][1])) { + position.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]]; + // ... and what's left is a `type` + if (values.length > 0 && (validator.isListStyleTypeKeyword(values[0][1]) || validator.isIdentifier(values[0][1]))) { + type.value = [values[0]]; + } return components; } @@ -352,9 +550,11 @@ function widthStyleColor(property, compactable, validator) { } module.exports = { + animation: animation, background: background, border: widthStyleColor, borderRadius: borderRadius, + font: font, fourValues: fourValues, listStyle: listStyle, multiplex: multiplex, 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 index 2617234ca..10d3bba17 100644 --- a/node_modules/clean-css/lib/optimizer/level-2/can-override.js +++ b/node_modules/clean-css/lib/optimizer/level-2/can-override.js @@ -1,11 +1,52 @@ var understandable = require('./properties/understandable'); +function animationIterationCount(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !(validator.isAnimationIterationCountKeyword(value2) || validator.isPositiveNumber(value2))) { + return false; + } else if (validator.isVariable(value1) && validator.isVariable(value2)) { + return true; + } + + return validator.isAnimationIterationCountKeyword(value2) || validator.isPositiveNumber(value2); +} + +function animationName(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !(validator.isAnimationNameKeyword(value2) || validator.isIdentifier(value2))) { + return false; + } else if (validator.isVariable(value1) && validator.isVariable(value2)) { + return true; + } + + return validator.isAnimationNameKeyword(value2) || validator.isIdentifier(value2); +} + +function animationTimingFunction(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !(validator.isAnimationTimingFunction(value2) || validator.isGlobal(value2))) { + return false; + } else if (validator.isVariable(value1) && validator.isVariable(value2)) { + return true; + } + + return validator.isAnimationTimingFunction(value2) || validator.isGlobal(value2); +} + +function areSameFunction(validator, value1, value2) { + if (!validator.isFunction(value1) || !validator.isFunction(value2)) { + return false; + } + + var function1Name = value1.substring(0, value1.indexOf('(')); + var function2Name = value2.substring(0, value2.indexOf('(')); + + return function1Name === function2Name; +} + function backgroundPosition(validator, value1, value2) { - if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue('background-position', value2, true)) { + if (!understandable(validator, value1, value2, 0, true) && !(validator.isBackgroundPositionKeyword(value2) || validator.isGlobal(value2))) { return false; - } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + } else if (validator.isVariable(value1) && validator.isVariable(value2)) { return true; - } else if (validator.isValidKeywordValue('background-position', value2, true)) { + } else if (validator.isBackgroundPositionKeyword(value2) || validator.isGlobal(value2)) { return true; } @@ -13,11 +54,11 @@ function backgroundPosition(validator, value1, value2) { } function backgroundSize(validator, value1, value2) { - if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue('background-size', value2, true)) { + if (!understandable(validator, value1, value2, 0, true) && !(validator.isBackgroundSizeKeyword(value2) || validator.isGlobal(value2))) { return false; - } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + } else if (validator.isVariable(value1) && validator.isVariable(value2)) { return true; - } else if (validator.isValidKeywordValue('background-size', value2, true)) { + } else if (validator.isBackgroundSizeKeyword(value2) || validator.isGlobal(value2)) { return true; } @@ -25,15 +66,15 @@ function backgroundSize(validator, value1, value2) { } function color(validator, value1, value2) { - if (!understandable(validator, value1, value2, 0, true) && !validator.isValidColor(value2)) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isColor(value2)) { return false; - } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + } else if (validator.isVariable(value1) && validator.isVariable(value2)) { return true; - } else if (!validator.colorOpacity && (validator.isValidRgbaColor(value1) || validator.isValidHslaColor(value1))) { + } else if (!validator.colorOpacity && (validator.isRgbColor(value1) || validator.isHslColor(value1))) { return false; - } else if (!validator.colorOpacity && (validator.isValidRgbaColor(value2) || validator.isValidHslaColor(value2))) { + } else if (!validator.colorOpacity && (validator.isRgbColor(value2) || validator.isHslColor(value2))) { return false; - } else if (validator.isValidColor(value1) && validator.isValidColor(value2)) { + } else if (validator.isColor(value1) && validator.isColor(value2)) { return true; } @@ -46,14 +87,18 @@ function components(overrideCheckers) { }; } +function fontFamily(validator, value1, value2) { + return understandable(validator, value1, value2, 0, true); +} + function image(validator, value1, value2) { - if (!understandable(validator, value1, value2, 0, true) && !validator.isValidImage(value2)) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isImage(value2)) { return false; - } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + } else if (validator.isVariable(value1) && validator.isVariable(value2)) { return true; - } else if (validator.isValidImage(value2)) { + } else if (validator.isImage(value2)) { return true; - } else if (validator.isValidImage(value1)) { + } else if (validator.isImage(value1)) { return false; } @@ -62,56 +107,76 @@ function image(validator, value1, value2) { function keyword(propertyName) { return function(validator, value1, value2) { - if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue(propertyName, value2)) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isKeyword(propertyName)(value2)) { return false; - } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + } else if (validator.isVariable(value1) && validator.isVariable(value2)) { return true; } - return validator.isValidKeywordValue(propertyName, value2, false); + return validator.isKeyword(propertyName)(value2); }; } function keywordWithGlobal(propertyName) { return function(validator, value1, value2) { - if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue(propertyName, value2, true)) { + if (!understandable(validator, value1, value2, 0, true) && !(validator.isKeyword(propertyName)(value2) || validator.isGlobal(value2))) { return false; - } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + } else if (validator.isVariable(value1) && validator.isVariable(value2)) { return true; } - return validator.isValidKeywordValue(propertyName, value2, true); + return validator.isKeyword(propertyName)(value2) || validator.isGlobal(value2); }; } function sameFunctionOrValue(validator, value1, value2) { - return validator.areSameFunction(value1, value2) ? + return areSameFunction(validator, value1, value2) ? true : value1 === value2; } + + function textShadow(validator, value1, value2) { - if (!understandable(validator, value1, value2, 0, true) && !validator.isValidTextShadow(value2)) { + if (!understandable(validator, value1, value2, 0, true) && !(validator.isUnit(value2) || validator.isColor(value2) || validator.isGlobal(value2))) { + return false; + } else if (validator.isVariable(value1) && validator.isVariable(value2)) { + return true; + } + + return validator.isUnit(value2) || validator.isColor(value2) || validator.isGlobal(value2); +} + +function time(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isTime(value2)) { + return false; + } else if (validator.isVariable(value1) && validator.isVariable(value2)) { + return true; + } else if (validator.isTime(value1) && !validator.isTime(value2)) { return false; - } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + } else if (validator.isTime(value2)) { + return true; + } else if (validator.isTime(value1)) { + return false; + } else if (validator.isFunction(value1) && !validator.isPrefixed(value1) && validator.isFunction(value2) && !validator.isPrefixed(value2)) { return true; } - return validator.isValidTextShadow(value2); + return sameFunctionOrValue(validator, value1, value2); } function unit(validator, value1, value2) { - if (!understandable(validator, value1, value2, 0, true) && !validator.isValidUnitWithoutFunction(value2)) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isUnit(value2)) { return false; - } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + } else if (validator.isVariable(value1) && validator.isVariable(value2)) { return true; - } else if (validator.isValidUnitWithoutFunction(value1) && !validator.isValidUnitWithoutFunction(value2)) { + } else if (validator.isUnit(value1) && !validator.isUnit(value2)) { return false; - } else if (validator.isValidUnitWithoutFunction(value2)) { + } else if (validator.isUnit(value2)) { return true; - } else if (validator.isValidUnitWithoutFunction(value1)) { + } else if (validator.isUnit(value1)) { return false; - } else if (validator.isValidFunctionWithoutVendorPrefix(value1) && validator.isValidFunctionWithoutVendorPrefix(value2)) { + } else if (validator.isFunction(value1) && !validator.isPrefixed(value1) && validator.isFunction(value2) && !validator.isPrefixed(value2)) { return true; } @@ -127,13 +192,13 @@ function unitOrKeywordWithGlobal(propertyName) { } function zIndex(validator, value1, value2) { - if (!understandable(validator, value1, value2, 0, true) && !validator.isValidZIndex(value2)) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isZIndex(value2)) { return false; - } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + } else if (validator.isVariable(value1) && validator.isVariable(value2)) { return true; } - return validator.isValidZIndex(value2); + return validator.isZIndex(value2); } module.exports = { @@ -141,9 +206,16 @@ module.exports = { color: color, components: components, image: image, + time: time, unit: unit }, property: { + animationDirection: keywordWithGlobal('animation-direction'), + animationFillMode: keyword('animation-fill-mode'), + animationIterationCount: animationIterationCount, + animationName: animationName, + animationPlayState: keywordWithGlobal('animation-play-state'), + animationTimingFunction: animationTimingFunction, backgroundAttachment: keyword('background-attachment'), backgroundClip: keywordWithGlobal('background-clip'), backgroundOrigin: keyword('background-origin'), @@ -157,8 +229,11 @@ module.exports = { cursor: keywordWithGlobal('cursor'), display: keywordWithGlobal('display'), float: keywordWithGlobal('float'), - fontStyle: keywordWithGlobal('font-style'), left: unitOrKeywordWithGlobal('left'), + fontFamily: fontFamily, + fontStretch: keywordWithGlobal('font-stretch'), + fontStyle: keywordWithGlobal('font-style'), + fontVariant: keywordWithGlobal('font-variant'), fontWeight: keywordWithGlobal('font-weight'), listStyleType: keywordWithGlobal('list-style-type'), listStylePosition: keywordWithGlobal('list-style-position'), diff --git a/node_modules/clean-css/lib/optimizer/level-2/compactable.js b/node_modules/clean-css/lib/optimizer/level-2/compactable.js index 9cf334d44..97e7e2aca 100644 --- a/node_modules/clean-css/lib/optimizer/level-2/compactable.js +++ b/node_modules/clean-css/lib/optimizer/level-2/compactable.js @@ -35,6 +35,141 @@ var override = require('../../utils/override'); // Puts the shorthand together from its components. // var compactable = { + 'animation': { + canOverride: canOverride.generic.components([ + canOverride.generic.time, + canOverride.property.animationTimingFunction, + canOverride.generic.time, + canOverride.property.animationIterationCount, + canOverride.property.animationDirection, + canOverride.property.animationFillMode, + canOverride.property.animationPlayState, + canOverride.property.animationName + ]), + components: [ + 'animation-duration', + 'animation-timing-function', + 'animation-delay', + 'animation-iteration-count', + 'animation-direction', + 'animation-fill-mode', + 'animation-play-state', + 'animation-name' + ], + breakUp: breakUp.multiplex(breakUp.animation), + defaultValue: 'none', + restore: restore.multiplex(restore.withoutDefaults), + shorthand: true, + vendorPrefixes: [ + '-moz-', + '-o-', + '-webkit-' + ] + }, + 'animation-delay': { + canOverride: canOverride.generic.time, + componentOf: [ + 'animation' + ], + defaultValue: '0s', + intoMultiplexMode: 'real', + vendorPrefixes: [ + '-moz-', + '-o-', + '-webkit-' + ] + }, + 'animation-direction': { + canOverride: canOverride.property.animationDirection, + componentOf: [ + 'animation' + ], + defaultValue: 'normal', + intoMultiplexMode: 'real', + vendorPrefixes: [ + '-moz-', + '-o-', + '-webkit-' + ] + }, + 'animation-duration': { + canOverride: canOverride.generic.time, + componentOf: [ + 'animation' + ], + defaultValue: '0s', + intoMultiplexMode: 'real', + vendorPrefixes: [ + '-moz-', + '-o-', + '-webkit-' + ] + }, + 'animation-fill-mode': { + canOverride: canOverride.property.animationFillMode, + componentOf: [ + 'animation' + ], + defaultValue: 'none', + intoMultiplexMode: 'real', + vendorPrefixes: [ + '-moz-', + '-o-', + '-webkit-' + ] + }, + 'animation-iteration-count': { + canOverride: canOverride.property.animationIterationCount, + componentOf: [ + 'animation' + ], + defaultValue: '1', + intoMultiplexMode: 'real', + vendorPrefixes: [ + '-moz-', + '-o-', + '-webkit-' + ] + }, + 'animation-name': { + canOverride: canOverride.property.animationName, + componentOf: [ + 'animation' + ], + defaultValue: 'none', + intoMultiplexMode: 'real', + vendorPrefixes: [ + '-moz-', + '-o-', + '-webkit-' + ] + }, + 'animation-play-state': { + canOverride: canOverride.property.animationPlayState, + componentOf: [ + 'animation' + ], + defaultValue: 'running', + intoMultiplexMode: 'real', + vendorPrefixes: [ + '-moz-', + '-o-', + '-webkit-' + ] + }, + 'animation-timing-function': { + canOverride: canOverride.property.animationTimingFunction, + componentOf: [ + 'animation' + ], + defaultValue: 'ease', + intoMultiplexMode: 'real', + vendorPrefixes: [ + '-moz-', + '-o-', + '-webkit-' + ] + }, 'background': { canOverride: canOverride.generic.components([ canOverride.generic.image, @@ -67,7 +202,8 @@ var compactable = { componentOf: [ 'background' ], - defaultValue: 'scroll' + defaultValue: 'scroll', + intoMultiplexMode: 'real' }, 'background-clip': { canOverride: canOverride.property.backgroundClip, @@ -75,6 +211,7 @@ var compactable = { 'background' ], defaultValue: 'border-box', + intoMultiplexMode: 'real', shortestValue: 'border-box' }, 'background-color': { @@ -83,6 +220,7 @@ var compactable = { 'background' ], defaultValue: 'transparent', + intoMultiplexMode: 'real', // otherwise real color will turn into default since color appears in last multiplex only multiplexLastOnly: true, nonMergeableValue: 'none', shortestValue: 'red' @@ -92,7 +230,8 @@ var compactable = { componentOf: [ 'background' ], - defaultValue: 'none' + defaultValue: 'none', + intoMultiplexMode: 'default' }, 'background-origin': { canOverride: canOverride.property.backgroundOrigin, @@ -100,6 +239,7 @@ var compactable = { 'background' ], defaultValue: 'padding-box', + intoMultiplexMode: 'real', shortestValue: 'border-box' }, 'background-position': { @@ -109,6 +249,7 @@ var compactable = { ], defaultValue: ['0', '0'], doubleValues: true, + intoMultiplexMode: 'real', shortestValue: '0' }, 'background-repeat': { @@ -117,7 +258,8 @@ var compactable = { 'background' ], defaultValue: ['repeat'], - doubleValues: true + doubleValues: true, + intoMultiplexMode: 'real' }, 'background-size': { canOverride: canOverride.property.backgroundSize, @@ -126,6 +268,7 @@ var compactable = { ], defaultValue: ['auto'], doubleValues: true, + intoMultiplexMode: 'real', shortestValue: '0 0' }, 'bottom': { @@ -216,6 +359,7 @@ var compactable = { 'border-width' ], defaultValue: 'medium', + oppositeTo: 'border-top-width', shortestValue: '0' }, 'border-collapse': { @@ -230,7 +374,9 @@ var compactable = { canOverride.generic.color, canOverride.generic.color ]), - componentOf: ['border'], + componentOf: [ + 'border' + ], components: [ 'border-top-color', 'border-right-color', @@ -281,6 +427,7 @@ var compactable = { 'border-width' ], defaultValue: 'medium', + oppositeTo: 'border-right-width', shortestValue: '0' }, 'border-radius': { @@ -344,6 +491,7 @@ var compactable = { 'border-width' ], defaultValue: 'medium', + oppositeTo: 'border-left-width', shortestValue: '0' }, 'border-style': { @@ -428,6 +576,7 @@ var compactable = { 'border-width' ], defaultValue: 'medium', + oppositeTo: 'border-bottom-width', shortestValue: '0' }, 'border-width': { @@ -438,6 +587,9 @@ var compactable = { canOverride.generic.unit, canOverride.generic.unit ]), + componentOf: [ + 'border' + ], components: [ 'border-top-width', 'border-right-width', @@ -469,18 +621,53 @@ var compactable = { canOverride: canOverride.property.float, defaultValue: 'none' }, + 'font': { + breakUp: breakUp.font, + canOverride: canOverride.generic.components([ + canOverride.property.fontStyle, + canOverride.property.fontVariant, + canOverride.property.fontWeight, + canOverride.property.fontStretch, + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.property.fontFamily + ]), + components: [ + 'font-style', + 'font-variant', + 'font-weight', + 'font-stretch', + 'font-size', + 'line-height', + 'font-family' + ], + restore: restore.font, + shorthand: true + }, + 'font-family': { + canOverride: canOverride.property.fontFamily, + defaultValue: 'user|agent|specific' + }, 'font-size': { canOverride: canOverride.generic.unit, defaultValue: 'medium', shortestValue: '0' }, + 'font-stretch': { + canOverride: canOverride.property.fontStretch, + defaultValue: 'normal' + }, 'font-style': { canOverride: canOverride.property.fontStyle, defaultValue: 'normal' }, + 'font-variant': { + canOverride: canOverride.property.fontVariant, + defaultValue: 'normal' + }, 'font-weight': { canOverride: canOverride.property.fontWeight, - defaultValue: '400', + defaultValue: 'normal', shortestValue: '400' }, 'height': { @@ -563,28 +750,32 @@ var compactable = { componentOf: [ 'margin' ], - defaultValue: '0' + defaultValue: '0', + oppositeTo: 'margin-top' }, 'margin-left': { canOverride: canOverride.generic.unit, componentOf: [ 'margin' ], - defaultValue: '0' + defaultValue: '0', + oppositeTo: 'margin-right' }, 'margin-right': { canOverride: canOverride.generic.unit, componentOf: [ 'margin' ], - defaultValue: '0' + defaultValue: '0', + oppositeTo: 'margin-left' }, 'margin-top': { canOverride: canOverride.generic.unit, componentOf: [ 'margin' ], - defaultValue: '0' + defaultValue: '0', + oppositeTo: 'margin-bottom' }, 'outline': { canOverride: canOverride.generic.components([ @@ -660,28 +851,32 @@ var compactable = { componentOf: [ 'padding' ], - defaultValue: '0' + defaultValue: '0', + oppositeTo: 'padding-top' }, 'padding-left': { canOverride: canOverride.generic.unit, componentOf: [ 'padding' ], - defaultValue: '0' + defaultValue: '0', + oppositeTo: 'padding-right' }, 'padding-right': { canOverride: canOverride.generic.unit, componentOf: [ 'padding' ], - defaultValue: '0' + defaultValue: '0', + oppositeTo: 'padding-left' }, 'padding-top': { canOverride: canOverride.generic.unit, componentOf: [ 'padding' ], - defaultValue: '0' + defaultValue: '0', + oppositeTo: 'padding-bottom' }, 'position': { canOverride: canOverride.property.position, 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 index 4a91a8331..29049302a 100644 --- a/node_modules/clean-css/lib/optimizer/level-2/is-mergeable.js +++ b/node_modules/clean-css/lib/optimizer/level-2/is-mergeable.js @@ -14,6 +14,19 @@ var PSEUDO_CLASSES_WITH_ARGUMENTS = [ ':nth-of-type' ]; var RELATION_PATTERN = /[>\+~]/; +var UNMIXABLE_PSEUDO_CLASSES = [ + ':after', + ':before', + ':first-letter', + ':first-line', + ':lang' +]; +var UNMIXABLE_PSEUDO_ELEMENTS = [ + '::after', + '::before', + '::first-letter', + '::first-line' +]; var Level = { DOUBLE_QUOTE: 'double-quote', @@ -21,7 +34,7 @@ var Level = { ROOT: 'root' }; -function isMergeable(selector, mergeablePseudoClasses, mergeablePseudoElements) { +function isMergeable(selector, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) { var singleSelectors = split(selector, Marker.COMMA); var singleSelector; var i, l; @@ -31,7 +44,7 @@ function isMergeable(selector, mergeablePseudoClasses, mergeablePseudoElements) if (singleSelector.length === 0 || isDeepSelector(singleSelector) || - (singleSelector.indexOf(Marker.COLON) > -1 && !areMergeable(singleSelector, extractPseudoFrom(singleSelector), mergeablePseudoClasses, mergeablePseudoElements))) { + (singleSelector.indexOf(Marker.COLON) > -1 && !areMergeable(singleSelector, extractPseudoFrom(singleSelector), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging))) { return false; } } @@ -122,11 +135,11 @@ function extractPseudoFrom(selector) { return list; } -function areMergeable(selector, matches, mergeablePseudoClasses, mergeablePseudoElements) { +function areMergeable(selector, matches, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) { return areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) && needArguments(matches) && (matches.length < 2 || !someIncorrectlyChained(selector, matches)) && - (matches.length < 2 || !someMixed(matches)); + (matches.length < 2 || multiplePseudoMerging && allMixable(matches)); } function areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) { @@ -217,20 +230,30 @@ function someIncorrectlyChained(selector, matches) { return false; } -function someMixed(matches) { - var firstIsPseudoElement = DOUBLE_COLON_PATTERN.test(matches[0]); +function allMixable(matches) { + var unmixableMatches = 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; + if (isPseudoElement(match)) { + unmixableMatches += UNMIXABLE_PSEUDO_ELEMENTS.indexOf(match) > -1 ? 1 : 0; + } else { + unmixableMatches += UNMIXABLE_PSEUDO_CLASSES.indexOf(match) > -1 ? 1 : 0; + } + + if (unmixableMatches > 1) { + return false; } } - return false; + return true; +} + +function isPseudoElement(pseudo) { + return DOUBLE_COLON_PATTERN.test(pseudo); } 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 index 7c0537501..b148bacd7 100644 --- a/node_modules/clean-css/lib/optimizer/level-2/merge-adjacent.js +++ b/node_modules/clean-css/lib/optimizer/level-2/merge-adjacent.js @@ -19,7 +19,8 @@ function mergeAdjacent(tokens, context) { var selectorsSortingMethod = options.level[OptimizationLevel.One].selectorsSortingMethod; var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; - var mergeLimit = 8191; + var mergeLimit = options.compatibility.selectors.mergeLimit; + var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging; for (var i = 0, l = tokens.length; i < l; i++) { var token = tokens[i]; @@ -34,8 +35,8 @@ function mergeAdjacent(tokens, context) { 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) && + isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) && + isMergeable(serializeRules(lastToken[1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) && 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]; 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 index bdcf53ec0..82db950fe 100644 --- 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 @@ -42,6 +42,7 @@ function mergeNonAdjacentByBody(tokens, context) { var selectorsSortingMethod = options.level[OptimizationLevel.One].selectorsSortingMethod; var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; + var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging; var candidates = {}; for (var i = tokens.length - 1; i >= 0; i--) { @@ -58,8 +59,8 @@ function mergeNonAdjacentByBody(tokens, context) { var candidateBody = serializeBody(token[2]); var oldToken = candidates[candidateBody]; if (oldToken && - isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements) && - isMergeable(serializeRules(oldToken[1]), mergeablePseudoClasses, mergeablePseudoElements)) { + isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) && + isMergeable(serializeRules(oldToken[1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging)) { if (token[2].length > 0) { token[1] = tidyRules(oldToken[1].concat(token[1]), false, adjacentSpace, false, context.warnings); diff --git a/node_modules/clean-css/lib/optimizer/level-2/optimize.js b/node_modules/clean-css/lib/optimizer/level-2/optimize.js index 78cd44666..9be961d09 100644 --- a/node_modules/clean-css/lib/optimizer/level-2/optimize.js +++ b/node_modules/clean-css/lib/optimizer/level-2/optimize.js @@ -6,6 +6,7 @@ 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 removeUnusedAtRules = require('./remove-unused-at-rules'); var restructure = require('./restructure'); var optimizeProperties = require('./properties/optimize'); @@ -27,6 +28,9 @@ function removeEmpty(tokens) { removeEmpty(token[2]); isEmpty = token[2].length === 0; break; + case Token.AT_RULE: + isEmpty = token[1].length === 0; + break; case Token.AT_RULE_BLOCK: isEmpty = token[2].length === 0; } @@ -109,6 +113,10 @@ function level2Optimize(tokens, context, withRestructuring) { removeDuplicateMediaQueries(tokens, context); } + if (levelOptions.removeUnusedAtRules) { + removeUnusedAtRules(tokens, context); + } + if (levelOptions.mergeMedia) { reduced = mergeMediaQueries(tokens, context); for (i = reduced.length - 1; i >= 0; i--) { @@ -116,7 +124,9 @@ function level2Optimize(tokens, context, withRestructuring) { } } - removeEmpty(tokens); + if (levelOptions.removeEmpty) { + removeEmpty(tokens); + } return tokens; } diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/is-mergeable-shorthand.js b/node_modules/clean-css/lib/optimizer/level-2/properties/is-mergeable-shorthand.js new file mode 100644 index 000000000..ee7191ef6 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/is-mergeable-shorthand.js @@ -0,0 +1,11 @@ +var Marker = require('../../../tokenizer/marker'); + +function isMergeableShorthand(shorthand) { + if (shorthand.name != 'font') { + return true; + } + + return shorthand.value[0][1].indexOf(Marker.INTERNAL) == -1; +} + +module.exports = isMergeableShorthand; 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 index a75c4ed63..bcfeeb609 100644 --- 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 @@ -4,52 +4,410 @@ var populateComponents = require('./populate-components'); var compactable = require('../compactable'); var deepClone = require('../clone').deep; +var restoreWithComponents = require('../restore-with-components'); +var restoreFromOptimizing = require('../../restore-from-optimizing'); var wrapSingle = require('../../wrap-for-optimizing').single; +var serializeBody = require('../../../writer/one-time').body; var Token = require('../../../tokenizer/token'); +function mergeIntoShorthands(properties, validator) { + var candidates = {}; + var descriptor; + var componentOf; + var property; + var i, l; + var j, m; + + // there is no shorthand property made up of less than 3 longhands + if (properties.length < 3) { + return; + } + + for (i = 0, l = properties.length; i < l; i++) { + property = properties[i]; + descriptor = compactable[property.name]; + + if (property.unused) { + continue; + } + + if (property.hack) { + continue; + } + + if (property.block) { + continue; + } + + invalidateOrCompact(properties, i, candidates, validator); + + if (descriptor && descriptor.componentOf) { + 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); +} + +function invalidateOrCompact(properties, position, candidates, validator) { + var invalidatedBy = properties[position]; + var shorthandName; + var shorthandDescriptor; + var candidateComponents; + + for (shorthandName in candidates) { + if (undefined !== invalidatedBy && shorthandName == invalidatedBy.name) { + continue; + } + + shorthandDescriptor = compactable[shorthandName]; + candidateComponents = candidates[shorthandName]; + if (invalidatedBy && invalidates(candidates, shorthandName, invalidatedBy)) { + delete candidates[shorthandName]; + continue; + } + + if (shorthandDescriptor.components.length > Object.keys(candidateComponents).length) { + continue; + } + + if (mixedImportance(candidateComponents)) { + continue; + } + + if (!overridable(candidateComponents, shorthandName, validator)) { + continue; + } + + if (!mergeable(candidateComponents)) { + continue; + } + + if (mixedInherit(candidateComponents)) { + replaceWithInheritBestFit(properties, candidateComponents, shorthandName, validator); + } else { + replaceWithShorthand(properties, candidateComponents, shorthandName, validator); + } + } +} + +function invalidates(candidates, shorthandName, invalidatedBy) { + var shorthandDescriptor = compactable[shorthandName]; + var invalidatedByDescriptor = compactable[invalidatedBy.name]; + var componentName; + + if ('overridesShorthands' in shorthandDescriptor && shorthandDescriptor.overridesShorthands.indexOf(invalidatedBy.name) > -1) { + return true; + } + + if (invalidatedByDescriptor && 'componentOf' in invalidatedByDescriptor) { + for (componentName in candidates[shorthandName]) { + if (invalidatedByDescriptor.componentOf.indexOf(componentName) > -1) { + return true; + } + } + } + + return false; +} + function mixedImportance(components) { var important; + var componentName; - for (var name in components) { - if (undefined !== important && components[name].important != important) + for (componentName in components) { + if (undefined !== important && components[componentName].important != important) { return true; + } - important = components[name].important; + important = components[componentName].important; } return false; } +function overridable(components, shorthandName, validator) { + var descriptor = compactable[shorthandName]; + var newValuePlaceholder = [ + Token.PROPERTY, + [Token.PROPERTY_NAME, shorthandName], + [Token.PROPERTY_VALUE, descriptor.defaultValue] + ]; + var newProperty = wrapSingle(newValuePlaceholder); + var component; + var mayOverride; + var i, l; + + populateComponents([newProperty], validator, []); + + for (i = 0, l = descriptor.components.length; i < l; i++) { + component = components[descriptor.components[i]]; + mayOverride = compactable[component.name].canOverride; + + if (!everyValuesPair(mayOverride.bind(null, validator), newProperty.components[i], component)) { + return false; + } + } + + return true; +} + +function mergeable(components) { + var lastCount = null; + var currentCount; + var componentName; + var component; + var descriptor; + var values; + + for (componentName in components) { + component = components[componentName]; + descriptor = compactable[componentName]; + + if (!('restore' in descriptor)) { + continue; + } + + restoreFromOptimizing([component.all[component.position]], restoreWithComponents); + values = descriptor.restore(component, compactable); + + currentCount = values.length; + + if (lastCount !== null && currentCount !== lastCount) { + return false; + } + + lastCount = currentCount; + } + + return true; +} + +function mixedInherit(components) { + var componentName; + var lastValue = null; + var currentValue; + + for (componentName in components) { + currentValue = hasInherit(components[componentName]); + + if (lastValue !== null && lastValue !== currentValue) { + return true; + } + + lastValue = currentValue; + } + + return false; +} + +function replaceWithInheritBestFit(properties, candidateComponents, shorthandName, validator) { + var viaLonghands = buildSequenceWithInheritLonghands(candidateComponents, shorthandName, validator); + var viaShorthand = buildSequenceWithInheritShorthand(candidateComponents, shorthandName, validator); + var longhandTokensSequence = viaLonghands[0]; + var shorthandTokensSequence = viaShorthand[0]; + var isLonghandsShorter = serializeBody(longhandTokensSequence).length < serializeBody(shorthandTokensSequence).length; + var newTokensSequence = isLonghandsShorter ? longhandTokensSequence : shorthandTokensSequence; + var newProperty = isLonghandsShorter ? viaLonghands[1] : viaShorthand[1]; + var newComponents = isLonghandsShorter ? viaLonghands[2] : viaShorthand[2]; + var all = candidateComponents[Object.keys(candidateComponents)[0]].all; + var componentName; + var oldComponent; + var newComponent; + var newToken; + + newProperty.position = all.length; + newProperty.shorthand = true; + newProperty.dirty = true; + newProperty.all = all; + newProperty.all.push(newTokensSequence[0]); + + properties.push(newProperty); + + for (componentName in candidateComponents) { + oldComponent = candidateComponents[componentName]; + oldComponent.unused = true; + + if (oldComponent.name in newComponents) { + newComponent = newComponents[oldComponent.name]; + newToken = findTokenIn(newTokensSequence, componentName); + + newComponent.position = all.length; + newComponent.all = all; + newComponent.all.push(newToken); + + properties.push(newComponent); + } + } +} + +function buildSequenceWithInheritLonghands(components, shorthandName, validator) { + var tokensSequence = []; + var inheritComponents = {}; + var nonInheritComponents = {}; + var descriptor = compactable[shorthandName]; + var shorthandToken = [ + Token.PROPERTY, + [Token.PROPERTY_NAME, shorthandName], + [Token.PROPERTY_VALUE, descriptor.defaultValue] + ]; + var newProperty = wrapSingle(shorthandToken); + var component; + var longhandToken; + var newComponent; + var nameMetadata; + var i, l; + + populateComponents([newProperty], validator, []); + + for (i = 0, l = descriptor.components.length; i < l; i++) { + component = components[descriptor.components[i]]; + + if (hasInherit(component)) { + longhandToken = component.all[component.position].slice(0, 2); + Array.prototype.push.apply(longhandToken, component.value); + tokensSequence.push(longhandToken); + + newComponent = deepClone(component); + newComponent.value = inferComponentValue(components, newComponent.name); + + newProperty.components[i] = newComponent; + inheritComponents[component.name] = deepClone(component); + } else { + newComponent = deepClone(component); + newComponent.all = component.all; + newProperty.components[i] = newComponent; + + nonInheritComponents[component.name] = component; + } + } + + nameMetadata = joinMetadata(nonInheritComponents, 1); + shorthandToken[1].push(nameMetadata); + + restoreFromOptimizing([newProperty], restoreWithComponents); + + shorthandToken = shorthandToken.slice(0, 2); + Array.prototype.push.apply(shorthandToken, newProperty.value); + + tokensSequence.unshift(shorthandToken); + + return [tokensSequence, newProperty, inheritComponents]; +} + +function inferComponentValue(components, propertyName) { + var descriptor = compactable[propertyName]; + + if ('oppositeTo' in descriptor) { + return components[descriptor.oppositeTo].value; + } else { + return [[Token.PROPERTY_VALUE, descriptor.defaultValue]]; + } +} + function joinMetadata(components, at) { var metadata = []; var component; var originalValue; var componentMetadata; - var name; + var componentName; - for (name in components) { - component = components[name]; + for (componentName in components) { + component = components[componentName]; originalValue = component.all[component.position]; componentMetadata = originalValue[at][originalValue[at].length - 1]; Array.prototype.push.apply(metadata, componentMetadata); } - return metadata; + return metadata.sort(metadataSorter); +} + +function metadataSorter(metadata1, metadata2) { + var line1 = metadata1[0]; + var line2 = metadata2[0]; + var column1 = metadata1[1]; + var column2 = metadata2[1]; + + if (line1 < line2) { + return -1; + } else if (line1 === line2) { + return column1 < column2 ? -1 : 1; + } else { + return 1; + } } -function replaceWithShorthand(properties, candidateComponents, name, validator) { - var descriptor = compactable[name]; +function buildSequenceWithInheritShorthand(components, shorthandName, validator) { + var tokensSequence = []; + var inheritComponents = {}; + var nonInheritComponents = {}; + var descriptor = compactable[shorthandName]; + var shorthandToken = [ + Token.PROPERTY, + [Token.PROPERTY_NAME, shorthandName], + [Token.PROPERTY_VALUE, 'inherit'] + ]; + var newProperty = wrapSingle(shorthandToken); + var component; + var longhandToken; + var nameMetadata; + var valueMetadata; + var i, l; + + populateComponents([newProperty], validator, []); + + for (i = 0, l = descriptor.components.length; i < l; i++) { + component = components[descriptor.components[i]]; + + if (hasInherit(component)) { + inheritComponents[component.name] = component; + } else { + longhandToken = component.all[component.position].slice(0, 2); + Array.prototype.push.apply(longhandToken, component.value); + tokensSequence.push(longhandToken); + + nonInheritComponents[component.name] = deepClone(component); + } + } + + nameMetadata = joinMetadata(inheritComponents, 1); + shorthandToken[1].push(nameMetadata); + + valueMetadata = joinMetadata(inheritComponents, 2); + shorthandToken[2].push(valueMetadata); + + tokensSequence.unshift(shorthandToken); + + return [tokensSequence, newProperty, nonInheritComponents]; +} + +function findTokenIn(tokens, componentName) { + var i, l; + + for (i = 0, l = tokens.length; i < l; i++) { + if (tokens[i][1][1] == componentName) { + return tokens[i]; + } + } +} + +function replaceWithShorthand(properties, candidateComponents, shorthandName, validator) { + var descriptor = compactable[shorthandName]; var nameMetadata; var valueMetadata; var newValuePlaceholder = [ Token.PROPERTY, - [Token.PROPERTY_NAME, name], + [Token.PROPERTY_NAME, shorthandName], [Token.PROPERTY_VALUE, descriptor.defaultValue] ]; - var mayOverride; var all; var newProperty = wrapSingle(newValuePlaceholder); @@ -61,13 +419,6 @@ function replaceWithShorthand(properties, candidateComponents, name, 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; @@ -91,67 +442,4 @@ function replaceWithShorthand(properties, candidateComponents, name, validator) 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 index 250b05fc4..5dc4bfb98 100644 --- a/node_modules/clean-css/lib/optimizer/level-2/properties/optimize.js +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/optimize.js @@ -11,7 +11,8 @@ 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 levelOptions = context.options.level[OptimizationLevel.Two]; + var _properties = wrapForOptimizing(properties, false, levelOptions.skipProperties); var _property; var i, l; @@ -24,12 +25,12 @@ function optimizeProperties(properties, withOverriding, withMerging, context) { } } - if (withOverriding && context.options.level[OptimizationLevel.Two].overrideProperties) { - overrideProperties(_properties, withMerging, context.options.compatibility, context.validator); + if (withMerging && levelOptions.mergeIntoShorthands) { + mergeIntoShorthands(_properties, context.validator); } - if (withMerging && context.options.level[OptimizationLevel.Two].mergeIntoShorthands) { - mergeIntoShorthands(_properties, context.validator); + if (withOverriding && levelOptions.overrideProperties) { + overrideProperties(_properties, withMerging, context.options.compatibility, context.validator); } restoreFromOptimizing(_properties, restoreWithComponents); 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 index 3c9d8d2af..3749720c9 100644 --- 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 @@ -2,6 +2,7 @@ var hasInherit = require('./has-inherit'); var everyValuesPair = require('./every-values-pair'); var findComponentIn = require('./find-component-in'); var isComponentOf = require('./is-component-of'); +var isMergeableShorthand = require('./is-mergeable-shorthand'); var overridesNonComponentShorthand = require('./overrides-non-component-shorthand'); var sameVendorPrefixesIn = require('./vendor-prefixes').same; @@ -73,16 +74,44 @@ function overrideShorthand(property, by) { 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; + if (compactable[property.name].shorthand) { + turnShorthandValueIntoMultiplex(property, size); + } else { + turnLonghandValueIntoMultiplex(property, size); + } +} - var value = component.value.slice(0); +function turnShorthandValueIntoMultiplex(property, size) { + var component; + var i, l; - for (var j = 1; j < size; j++) { - component.value.push([Token.PROPERTY_VALUE, Marker.COMMA]); - Array.prototype.push.apply(component.value, value); + for (i = 0, l = property.components.length; i < l; i++) { + component = property.components[i]; + + if (!component.multiplex) { + turnLonghandValueIntoMultiplex(component, size); + } + } +} + +function turnLonghandValueIntoMultiplex(property, size) { + var withRealValue = compactable[property.name].intoMultiplexMode == 'real'; + var withValue = withRealValue ? + property.value.slice(0) : + compactable[property.name].defaultValue; + var i = multiplexSize(property); + var j; + var m = withValue.length; + + for (; i < size; i++) { + property.value.push([Token.PROPERTY_VALUE, Marker.COMMA]); + + if (Array.isArray(withValue)) { + for (j = 0; j < m; j++) { + property.value.push(withRealValue ? withValue[j] : [Token.PROPERTY_VALUE, withValue[j]]); + } + } else { + property.value.push(withRealValue ? withValue : [Token.PROPERTY_VALUE, withValue]); } } } @@ -124,8 +153,9 @@ function moreSameShorthands(properties, startAt, name) { function overridingFunction(shorthand, validator) { for (var i = 0, l = shorthand.components.length; i < l; i++) { - if (anyValue(validator.isValidFunction, shorthand.components[i])) + if (!anyValue(validator.isUrl, shorthand.components[i]) && anyValue(validator.isFunction, shorthand.components[i])) { return true; + } } return false; @@ -271,8 +301,13 @@ function overrideProperties(properties, withMerging, compatibility, validator) { if (!sameVendorPrefixesIn([left], right.components)) continue; - if (!anyValue(validator.isValidFunction, left) && overridingFunction(right, validator)) + if (!anyValue(validator.isFunction, left) && overridingFunction(right, validator)) + continue; + + if (!isMergeableShorthand(right)) { + left.unused = true; continue; + } component = findComponentIn(right, left); mayOverride = compactable[left.name].canOverride; @@ -289,7 +324,7 @@ function overrideProperties(properties, withMerging, compatibility, validator) { continue; } - if (!anyValue(validator.isValidFunction, left) && overridingFunction(right, validator)) { + if (!anyValue(validator.isFunction, left) && overridingFunction(right, validator)) { continue; } @@ -325,6 +360,9 @@ function overrideProperties(properties, withMerging, compatibility, validator) { if (overridingFunction(left, validator)) continue; + if (!isMergeableShorthand(left)) + continue; + component = findComponentIn(left, right); if (everyValuesPair(mayOverride.bind(null, validator), component, right)) { var disabledBackgroundMerging = @@ -367,6 +405,11 @@ function overrideProperties(properties, withMerging, compatibility, validator) { continue; } + if (!isMergeableShorthand(right)) { + left.unused = true; + continue; + } + for (k = left.components.length - 1; k >= 0; k--) { var leftComponent = left.components[k]; var rightComponent = right.components[k]; 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 index 6c77db50d..032169a24 100644 --- a/node_modules/clean-css/lib/optimizer/level-2/properties/understandable.js +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/understandable.js @@ -5,7 +5,7 @@ function understandable(validator, value1, value2, _position, isPaired) { return false; } - if (isPaired && validator.isValidVariable(value1) !== validator.isValidVariable(value2)) { + if (isPaired && validator.isVariable(value1) !== validator.isVariable(value2)) { return false; } 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 index 3461bb268..6ce0902b9 100644 --- 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 @@ -13,6 +13,7 @@ function reduceNonAdjacent(tokens, context) { var options = context.options; var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; + var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging; var candidates = {}; var repeated = []; @@ -27,7 +28,7 @@ function reduceNonAdjacent(tokens, context) { var selectorAsString = serializeRules(token[1]); var isComplexAndNotSpecial = token[1].length > 1 && - isMergeable(selectorAsString, mergeablePseudoClasses, mergeablePseudoElements); + isMergeable(selectorAsString, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging); var wrappedSelectors = wrappedSelectorsFrom(token[1]); var selectors = isComplexAndNotSpecial ? [selectorAsString].concat(wrappedSelectors) : @@ -88,6 +89,7 @@ function reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, con function reduceComplexNonAdjacentCases(tokens, candidates, options, context) { var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; + var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging; var localContext = {}; function filterOut(idx) { @@ -109,7 +111,7 @@ function reduceComplexNonAdjacentCases(tokens, candidates, options, context) { var intoToken = tokens[intoPosition]; var reducedBodies = []; - var selectors = isMergeable(complexSelector, mergeablePseudoClasses, mergeablePseudoElements) ? + var selectors = isMergeable(complexSelector, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) ? into[0].list : [complexSelector]; diff --git a/node_modules/clean-css/lib/optimizer/level-2/remove-unused-at-rules.js b/node_modules/clean-css/lib/optimizer/level-2/remove-unused-at-rules.js new file mode 100644 index 000000000..e60d5e7c2 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/remove-unused-at-rules.js @@ -0,0 +1,232 @@ +var populateComponents = require('./properties/populate-components'); + +var wrapForOptimizing = require('../wrap-for-optimizing').single; +var restoreFromOptimizing = require('../restore-from-optimizing'); + +var Token = require('../../tokenizer/token'); + +var animationNameRegex = /^(\-moz\-|\-o\-|\-webkit\-)?animation-name$/; +var animationRegex = /^(\-moz\-|\-o\-|\-webkit\-)?animation$/; +var keyframeRegex = /^@(\-moz\-|\-o\-|\-webkit\-)?keyframes /; + +function removeUnusedAtRules(tokens, context) { + removeUnusedAtRule(tokens, matchCounterStyle, markCounterStylesAsUsed, context); + removeUnusedAtRule(tokens, matchFontFace, markFontFacesAsUsed, context); + removeUnusedAtRule(tokens, matchKeyframe, markKeyframesAsUsed, context); + removeUnusedAtRule(tokens, matchNamespace, markNamespacesAsUsed, context); +} + +function removeUnusedAtRule(tokens, matchCallback, markCallback, context) { + var atRules = {}; + var atRule; + var token; + var zeroAt; + var i, l; + + for (i = 0, l = tokens.length; i < l; i++) { + matchCallback(tokens[i], atRules); + } + + if (Object.keys(atRules).length === 0) { + return; + } + + markUsedAtRules(tokens, markCallback, atRules, context); + + for (atRule in atRules) { + token = atRules[atRule]; + zeroAt = token[0] == Token.AT_RULE ? 1 : 2; + token[zeroAt] = []; + } +} + +function markUsedAtRules(tokens, markCallback, atRules, context) { + var boundMarkCallback = markCallback(atRules); + var i, l; + + for (i = 0, l = tokens.length; i < l; i++) { + switch (tokens[i][0]) { + case Token.RULE: + boundMarkCallback(tokens[i], context); + break; + case Token.NESTED_BLOCK: + markUsedAtRules(tokens[i][2], markCallback, atRules, context); + } + } +} + +function matchCounterStyle(token, atRules) { + var match; + + if (token[0] == Token.AT_RULE_BLOCK && token[1][0][1].indexOf('@counter-style') === 0) { + match = token[1][0][1].split(' ')[1]; + atRules[match] = token; + } +} + +function markCounterStylesAsUsed(atRules) { + return function (token, context) { + var property; + var wrappedProperty; + var i, l; + + for (i = 0, l = token[2].length; i < l; i++) { + property = token[2][i]; + + if (property[1][1] == 'list-style') { + wrappedProperty = wrapForOptimizing(property); + populateComponents([wrappedProperty], context.validator, context.warnings); + + if (wrappedProperty.components[0].value[0][1] in atRules) { + delete atRules[property[2][1]]; + } + + restoreFromOptimizing([wrappedProperty]); + } + + if (property[1][1] == 'list-style-type' && property[2][1] in atRules) { + delete atRules[property[2][1]]; + } + } + }; +} + +function matchFontFace(token, atRules) { + var property; + var match; + var i, l; + + if (token[0] == Token.AT_RULE_BLOCK && token[1][0][1] == '@font-face') { + for (i = 0, l = token[2].length; i < l; i++) { + property = token[2][i]; + + if (property[1][1] == 'font-family') { + match = property[2][1].toLowerCase(); + atRules[match] = token; + break; + } + } + } +} + +function markFontFacesAsUsed(atRules) { + return function (token, context) { + var property; + var wrappedProperty; + var component; + var normalizedMatch; + var i, l; + var j, m; + + for (i = 0, l = token[2].length; i < l; i++) { + property = token[2][i]; + + if (property[1][1] == 'font') { + wrappedProperty = wrapForOptimizing(property); + populateComponents([wrappedProperty], context.validator, context.warnings); + component = wrappedProperty.components[6]; + + for (j = 0, m = component.value.length; j < m; j++) { + normalizedMatch = component.value[j][1].toLowerCase(); + + if (normalizedMatch in atRules) { + delete atRules[normalizedMatch]; + } + } + + restoreFromOptimizing([wrappedProperty]); + } + + if (property[1][1] == 'font-family') { + for (j = 2, m = property.length; j < m; j++) { + normalizedMatch = property[j][1].toLowerCase(); + + if (normalizedMatch in atRules) { + delete atRules[normalizedMatch]; + } + } + } + } + }; +} + +function matchKeyframe(token, atRules) { + var match; + + if (token[0] == Token.NESTED_BLOCK && keyframeRegex.test(token[1][0][1])) { + match = token[1][0][1].split(' ')[1]; + atRules[match] = token; + } +} + +function markKeyframesAsUsed(atRules) { + return function (token, context) { + var property; + var wrappedProperty; + var component; + var i, l; + var j, m; + + for (i = 0, l = token[2].length; i < l; i++) { + property = token[2][i]; + + if (animationRegex.test(property[1][1])) { + wrappedProperty = wrapForOptimizing(property); + populateComponents([wrappedProperty], context.validator, context.warnings); + component = wrappedProperty.components[7]; + + for (j = 0, m = component.value.length; j < m; j++) { + if (component.value[j][1] in atRules) { + delete atRules[component.value[j][1]]; + } + } + + restoreFromOptimizing([wrappedProperty]); + } + + if (animationNameRegex.test(property[1][1])) { + for (j = 2, m = property.length; j < m; j++) { + if (property[j][1] in atRules) { + delete atRules[property[j][1]]; + } + } + } + } + }; +} + +function matchNamespace(token, atRules) { + var match; + + if (token[0] == Token.AT_RULE && token[1].indexOf('@namespace') === 0) { + match = token[1].split(' ')[1]; + atRules[match] = token; + } +} + +function markNamespacesAsUsed(atRules) { + var namespaceRegex = new RegExp(Object.keys(atRules).join('\\\||') + '\\\|', 'g'); + + return function (token) { + var match; + var scope; + var normalizedMatch; + var i, l; + var j, m; + + for (i = 0, l = token[1].length; i < l; i++) { + scope = token[1][i]; + match = scope[1].match(namespaceRegex); + + for (j = 0, m = match.length; j < m; j++) { + normalizedMatch = match[j].substring(0, match[j].length - 1); + + if (normalizedMatch in atRules) { + delete atRules[normalizedMatch]; + } + } + } + }; +} + +module.exports = removeUnusedAtRules; diff --git a/node_modules/clean-css/lib/optimizer/level-2/restore.js b/node_modules/clean-css/lib/optimizer/level-2/restore.js index 149688c5b..13f12e496 100644 --- a/node_modules/clean-css/lib/optimizer/level-2/restore.js +++ b/node_modules/clean-css/lib/optimizer/level-2/restore.js @@ -135,6 +135,59 @@ function borderRadius(property, compactable) { } } +function font(property, compactable) { + var components = property.components; + var restored = []; + var component; + var componentIndex = 0; + var fontFamilyIndex = 0; + + if (property.value[0][1].indexOf(Marker.INTERNAL) === 0) { + property.value[0][1] = property.value[0][1].substring(Marker.INTERNAL.length); + return property.value; + } + + // first four components are optional + while (componentIndex < 4) { + component = components[componentIndex]; + + if (component.value[0][1] != compactable[component.name].defaultValue) { + Array.prototype.push.apply(restored, component.value); + } + + componentIndex++; + } + + // then comes font-size + Array.prototype.push.apply(restored, components[componentIndex].value); + componentIndex++; + + // then may come line-height + if (components[componentIndex].value[0][1] != compactable[components[componentIndex].name].defaultValue) { + Array.prototype.push.apply(restored, [[Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]]); + Array.prototype.push.apply(restored, components[componentIndex].value); + } + + componentIndex++; + + // then comes font-family + while (components[componentIndex].value[fontFamilyIndex]) { + restored.push(components[componentIndex].value[fontFamilyIndex]); + + if (components[componentIndex].value[fontFamilyIndex + 1]) { + restored.push([Token.PROPERTY_VALUE, Marker.COMMA]); + } + + fontFamilyIndex++; + } + + if (isInheritOnly(restored)) { + return [restored[0]]; + } + + return restored; +} + function fourValues(property) { var components = property.components; var value1 = components[0].value[0]; @@ -227,6 +280,7 @@ function withoutDefaults(property, compactable) { module.exports = { background: background, borderRadius: borderRadius, + font: font, 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 index 2635415ae..90b8bfa65 100644 --- a/node_modules/clean-css/lib/optimizer/level-2/restructure.js +++ b/node_modules/clean-css/lib/optimizer/level-2/restructure.js @@ -25,7 +25,8 @@ function restructure(tokens, context) { var options = context.options; var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; - var mergeLimit = 8191; + var mergeLimit = options.compatibility.selectors.mergeLimit; + var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging; var specificityCache = context.cache.specificity; var movableTokens = {}; var movedProperties = []; @@ -86,7 +87,7 @@ function restructure(tokens, context) { var mergeableTokens = []; for (var i = sourceTokens.length - 1; i >= 0; i--) { - if (!isMergeable(serializeRules(sourceTokens[i][1]), mergeablePseudoClasses, mergeablePseudoElements)) { + if (!isMergeable(serializeRules(sourceTokens[i][1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging)) { continue; } diff --git a/node_modules/clean-css/lib/optimizer/validator.js b/node_modules/clean-css/lib/optimizer/validator.js index 35f041ea9..cfccd0f9d 100644 --- a/node_modules/clean-css/lib/optimizer/validator.js +++ b/node_modules/clean-css/lib/optimizer/validator.js @@ -1,44 +1,28 @@ -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 functionNoVendorRegexStr = '[A-Z]+(\\-|[A-Z]|[0-9])+\\(.*?\\)'; +var functionVendorRegexStr = '\\-(\\-|[A-Z]|[0-9])+\\(.*?\\)'; +var variableRegexStr = 'var\\(\\-\\-[^\\)]+\\)'; +var functionAnyRegexStr = '(' + variableRegexStr + '|' + functionNoVendorRegexStr + '|' + functionVendorRegexStr + ')'; + +var animationTimingFunctionRegex = /^(cubic\-bezier|steps)\([^\)]+\)$/; +var calcRegex = new RegExp('^(\\-moz\\-|\\-webkit\\-)?calc\\([^\\)]+\\)$', 'i'); +var functionAnyRegex = new RegExp('^' + functionAnyRegexStr + '$', 'i'); +var hslColorRegex = /^hsl\(\s*[\-\.\d]+\s*,\s*[\.\d]+%\s*,\s*[\.\d]+%\s*\)|hsla\(\s*[\-\.\d]+\s*,\s*[\.\d]+%\s*,\s*[\.\d]+%\s*,\s*[\.\d]+\s*\)$/; +var identifierRegex = /^(\-[a-z0-9_][a-z0-9\-_]*|[a-z][a-z0-9\-_]*)$/i; +var longHexColorRegex = /^#[0-9a-f]{6}$/i; +var namedEntityRegex = /^[a-z]+$/i; +var prefixRegex = /^-([a-z0-9]|-)*$/i; +var rgbColorRegex = /^rgb\(\s*[\d]{1,3}\s*,\s*[\d]{1,3}\s*,\s*[\d]{1,3}\s*\)|rgba\(\s*[\d]{1,3}\s*,\s*[\d]{1,3}\s*,\s*[\d]{1,3}\s*,\s*[\.\d]+\s*\)$/; +var shortHexColorRegex = /^#[0-9a-f]{3}$/i; +var timeRegex = new RegExp('^(\\-?\\+?\\.?\\d+\\.?\\d*(s|ms))$'); var urlRegex = /^url\([\s\S]+\)$/i; - -var globalKeywords = [ - 'inherit', - 'initial', - 'unset' -]; +var variableRegex = new RegExp('^' + variableRegexStr + '$', 'i'); var Keywords = { + '^': [ + 'inherit', + 'initial', + 'unset' + ], '*-style': [ 'auto', 'dashed', @@ -52,6 +36,37 @@ var Keywords = { 'ridge', 'solid' ], + 'animation-direction': [ + 'alternate', + 'alternate-reverse', + 'normal', + 'reverse' + ], + 'animation-fill-mode': [ + 'backwards', + 'both', + 'forwards', + 'none' + ], + 'animation-iteration-count': [ + 'infinite' + ], + 'animation-name': [ + 'none' + ], + 'animation-play-state': [ + 'paused', + 'running' + ], + 'animation-timing-function': [ + 'ease', + 'ease-in', + 'ease-in-out', + 'ease-out', + 'linear', + 'step-end', + 'step-start' + ], 'background-attachment': [ 'fixed', 'inherit', @@ -106,6 +121,9 @@ var Keywords = { 'none', 'right' ], + 'color': [ + 'transparent' + ], 'cursor': [ 'all-scroll', 'auto', @@ -156,11 +174,46 @@ var Keywords = { 'left': [ 'auto' ], + 'font': [ + 'caption', + 'icon', + 'menu', + 'message-box', + 'small-caption', + 'status-bar', + 'unset' + ], + 'font-size': [ + 'large', + 'larger', + 'medium', + 'small', + 'smaller', + 'x-large', + 'x-small', + 'xx-large', + 'xx-small' + ], + 'font-stretch': [ + 'condensed', + 'expanded', + 'extra-condensed', + 'extra-expanded', + 'normal', + 'semi-condensed', + 'semi-expanded', + 'ultra-condensed', + 'ultra-expanded' + ], 'font-style': [ 'italic', 'normal', 'oblique' ], + 'font-variant': [ + 'normal', + 'small-caps' + ], 'font-weight': [ '100', '200', @@ -176,6 +229,9 @@ var Keywords = { 'lighter', 'normal' ], + 'line-height': [ + 'normal' + ], 'list-style-position': [ 'inside', 'outside' @@ -262,163 +318,118 @@ var Keywords = { ] }; -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; -} +var Units = [ + '%', + 'ch', + 'cm', + 'em', + 'ex', + 'in', + 'mm', + 'pc', + 'pt', + 'px', + 'rem', + 'vh', + 'vm', + 'vmax', + 'vmin', + 'vw' +]; -function isValidBackgroundPositionPart(value) { - return Keywords['background-position'].indexOf(value) > -1 || cssUnitOrCalcRegex.test(value); -} +function isAnimationTimingFunction() { + var isTimingFunctionKeyword = isKeyword('animation-timing-function'); -function isValidBackgroundSizePart(value) { - return Keywords['background-size'].indexOf(value) > -1 || cssUnitRegex.test(value); + return function (value) { + return isTimingFunctionKeyword(value) || animationTimingFunctionRegex.test(value); + }; } -function isValidColor(value) { - return isValidNamedColor(value) || - isValidColorValue(value); +function isColor(value) { + return value != 'auto' && + ( + isKeyword('color')(value) || + isHexColor(value) || + isColorFunction(value) || + isNamedEntity(value) + ); } -function isValidColorValue(value) { - return isValidHexColor(value) || - isValidRgbaColor(value) || - isValidHslaColor(value); +function isColorFunction(value) { + return isRgbColor(value) || isHslColor(value); } -function isValidFunction(value) { - return !urlRegex.test(value) && cssFunctionAnyRegex.test(value); +function isDynamicUnit(value) { + return calcRegex.test(value); } -function isValidFunctionWithoutVendorPrefix(value) { - return !urlRegex.test(value) && cssFunctionNoVendorRegex.test(value); +function isFunction(value) { + return functionAnyRegex.test(value); } -function isValidGlobalValue(value) { - return globalKeywords.indexOf(value) > -1; +function isHexColor(value) { + return shortHexColorRegex.test(value) || longHexColorRegex.test(value); } -function isValidHexColor(value) { - return (value.length === 4 || value.length === 7) && value[0] === '#'; +function isHslColor(value) { + return hslColorRegex.test(value); } -function isValidHslaColor(value) { - return value.length > 0 && value.indexOf('hsla(') === 0 && value.indexOf(')') === value.length - 1; +function isIdentifier(value) { + return identifierRegex.test(value); } -function isValidImage(value) { - return value == 'none' || value == 'inherit' || isValidUrl(value); +function isImage(value) { + return value == 'none' || value == 'inherit' || isUrl(value); } -function isValidKeywordValue(propertyName, value, includeGlobal) { - return Keywords[propertyName].indexOf(value) > -1 || includeGlobal && isValidGlobalValue(value); +function isKeyword(propertyName) { + return function(value) { + return Keywords[propertyName].indexOf(value) > -1; + }; } -function isValidListStyleType(value) { - return Keywords['list-style-type'].indexOf(value) > -1; +function isNamedEntity(value) { + return namedEntityRegex.test(value); } -function isValidListStylePosition(value) { - return Keywords['list-style-position'].indexOf(value) > -1; +function isNumber(value) { + return value.length > 0 && ('' + parseFloat(value)) === value; } -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 isRgbColor(value) { + return rgbColorRegex.test(value); } -function isValidRgbaColor(value) { - return value.length > 0 && value.indexOf('rgba(') === 0 && value.indexOf(')') === value.length - 1; +function isPrefixed(value) { + return prefixRegex.test(value); } -function isValidStyle(value) { - return Keywords['*-style'].indexOf(value) > -1; +function isPositiveNumber(value) { + return isNumber(value) && + parseFloat(value) >= 0; } -function isValidTextShadow(compatibleCssUnitRegex, value) { - return isValidUnitWithoutFunction(compatibleCssUnitRegex, value) || - isValidColor(value) || - isValidGlobalValue(value); +function isVariable(value) { + return variableRegex.test(value); } -function isValidUnit(compatibleCssUnitAnyRegex, value) { - return compatibleCssUnitAnyRegex.test(value); +function isTime(value) { + return timeRegex.test(value); } -function isValidUnitWithoutFunction(compatibleCssUnitRegex, value) { +function isUnit(compatibleCssUnitRegex, value) { return compatibleCssUnitRegex.test(value); } -function isValidUrl(value) { +function isUrl(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) { +function isZIndex(value) { return value == 'auto' || - isValidGlobalValue(value) || - value.length > 0 && value == ('' + parseInt(value)); + isNumber(value) || + isKeyword('^')(value); } function validator(compatibility) { @@ -426,44 +437,50 @@ function validator(compatibility) { 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; + var compatibleCssUnitRegex = new RegExp('^(\\-?\\.?\\d+\\.?\\d*(' + validUnits.join('|') + '|)|auto|inherit)$', 'i'); 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 + colorOpacity: compatibility.colors.opacity, + isAnimationDirectionKeyword: isKeyword('animation-direction'), + isAnimationFillModeKeyword: isKeyword('animation-fill-mode'), + isAnimationIterationCountKeyword: isKeyword('animation-iteration-count'), + isAnimationNameKeyword: isKeyword('animation-name'), + isAnimationPlayStateKeyword: isKeyword('animation-play-state'), + isAnimationTimingFunction: isAnimationTimingFunction(), + isBackgroundAttachmentKeyword: isKeyword('background-attachment'), + isBackgroundClipKeyword: isKeyword('background-clip'), + isBackgroundOriginKeyword: isKeyword('background-origin'), + isBackgroundPositionKeyword: isKeyword('background-position'), + isBackgroundRepeatKeyword: isKeyword('background-repeat'), + isBackgroundSizeKeyword: isKeyword('background-size'), + isColor: isColor, + isColorFunction: isColorFunction, + isDynamicUnit: isDynamicUnit, + isFontKeyword: isKeyword('font'), + isFontSizeKeyword: isKeyword('font-size'), + isFontStretchKeyword: isKeyword('font-stretch'), + isFontStyleKeyword: isKeyword('font-style'), + isFontVariantKeyword: isKeyword('font-variant'), + isFontWeightKeyword: isKeyword('font-weight'), + isFunction: isFunction, + isGlobal: isKeyword('^'), + isHslColor: isHslColor, + isIdentifier: isIdentifier, + isImage: isImage, + isKeyword: isKeyword, + isLineHeightKeyword: isKeyword('line-height'), + isListStylePositionKeyword: isKeyword('list-style-position'), + isListStyleTypeKeyword: isKeyword('list-style-type'), + isPrefixed: isPrefixed, + isPositiveNumber: isPositiveNumber, + isRgbColor: isRgbColor, + isStyleKeyword: isKeyword('*-style'), + isTime: isTime, + isUnit: isUnit.bind(null, compatibleCssUnitRegex), + isUrl: isUrl, + isVariable: isVariable, + isWidth: isKeyword('width'), + isZIndex: isZIndex }; } diff --git a/node_modules/clean-css/lib/optimizer/wrap-for-optimizing.js b/node_modules/clean-css/lib/optimizer/wrap-for-optimizing.js index 0fbfdb056..c516fbc4a 100644 --- a/node_modules/clean-css/lib/optimizer/wrap-for-optimizing.js +++ b/node_modules/clean-css/lib/optimizer/wrap-for-optimizing.js @@ -17,7 +17,7 @@ var Match = { VARIABLE_REFERENCE_PATTERN: /var\(--.+\)$/ }; -function wrapAll(properties, includeVariable) { +function wrapAll(properties, includeVariable, skipProperties) { var wrapped = []; var single; var property; @@ -34,6 +34,10 @@ function wrapAll(properties, includeVariable) { continue; } + if (skipProperties && skipProperties.indexOf(property[1][1]) > -1) { + continue; + } + single = wrapSingle(property); single.all = properties; single.position = i; diff --git a/node_modules/clean-css/lib/options/compatibility.js b/node_modules/clean-css/lib/options/compatibility.js index ca881c627..8e6a119a8 100644 --- a/node_modules/clean-css/lib/options/compatibility.js +++ b/node_modules/clean-css/lib/options/compatibility.js @@ -56,7 +56,9 @@ var DEFAULTS = { '::before', '::first-letter', '::first-line' - ] // selectors with these pseudo-elements can be merged as these are universally supported + ], // selectors with these pseudo-elements can be merged as these are universally supported + mergeLimit: 8191, // number of rules that can be safely merged together + multiplePseudoMerging: true }, units: { ch: true, diff --git a/node_modules/clean-css/lib/options/fetch.js b/node_modules/clean-css/lib/options/fetch.js new file mode 100644 index 000000000..0aaad7866 --- /dev/null +++ b/node_modules/clean-css/lib/options/fetch.js @@ -0,0 +1,7 @@ +var loadRemoteResource = require('../reader/load-remote-resource'); + +function fetchFrom(callback) { + return callback || loadRemoteResource; +} + +module.exports = fetchFrom; diff --git a/node_modules/clean-css/lib/options/inline.js b/node_modules/clean-css/lib/options/inline.js index 4a6997075..54761f40a 100644 --- a/node_modules/clean-css/lib/options/inline.js +++ b/node_modules/clean-css/lib/options/inline.js @@ -3,6 +3,10 @@ function inlineOptionsFrom(rules) { return rules; } + if (rules === false) { + return ['none']; + } + return undefined === rules ? ['local'] : rules.split(','); diff --git a/node_modules/clean-css/lib/options/optimization-level.js b/node_modules/clean-css/lib/options/optimization-level.js index d9828287b..0d3ad7303 100644 --- a/node_modules/clean-css/lib/options/optimization-level.js +++ b/node_modules/clean-css/lib/options/optimization-level.js @@ -17,9 +17,9 @@ DEFAULTS[OptimizationLevel.One] = { optimizeBackground: true, optimizeBorderRadius: true, optimizeFilter: true, - optimizeFont: true, optimizeFontWeight: true, optimizeOutline: true, + removeEmpty: true, removeNegativePaddings: true, removeQuotes: true, removeWhitespace: true, @@ -41,11 +41,14 @@ DEFAULTS[OptimizationLevel.Two] = { mergeNonAdjacentRules: true, mergeSemantically: false, overrideProperties: true, + removeEmpty: true, reduceNonAdjacentRules: true, removeDuplicateFontRules: true, removeDuplicateMediaBlocks: true, removeDuplicateRules: true, - restructureRules: false + removeUnusedAtRules: false, + restructureRules: false, + skipProperties: [] }; var ALL_KEYWORD_1 = '*'; @@ -55,6 +58,7 @@ var FALSE_KEYWORD_2 = 'off'; var TRUE_KEYWORD_1 = 'true'; var TRUE_KEYWORD_2 = 'on'; +var LIST_VALUE_SEPARATOR = ','; var OPTION_SEPARATOR = ';'; var OPTION_VALUE_SEPARATOR = ':'; @@ -99,6 +103,10 @@ function optimizationLevelFrom(source) { source[One].roundingPrecision = roundingPrecisionFrom(source[One].roundingPrecision); } + if (Two in source && 'skipProperties' in source[Two] && typeof(source[Two].skipProperties) == 'string') { + source[Two].skipProperties = source[Two].skipProperties.split(LIST_VALUE_SEPARATOR); + } + if (Zero in source || One in source || Two in source) { level[Zero] = override(level[Zero], source[Zero]); } diff --git a/node_modules/clean-css/lib/reader/apply-source-maps.js b/node_modules/clean-css/lib/reader/apply-source-maps.js index d2f87250e..7c5a9282b 100644 --- a/node_modules/clean-css/lib/reader/apply-source-maps.js +++ b/node_modules/clean-css/lib/reader/apply-source-maps.js @@ -2,7 +2,6 @@ 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'); @@ -17,6 +16,7 @@ var MAP_MARKER_PATTERN = /^\/\*# sourceMappingURL=(\S+) \*\/$/; function applySourceMaps(tokens, context, callback) { var applyContext = { callback: callback, + fetch: context.options.fetch, index: 0, inline: context.options.inline, inlineRequest: context.options.inlineRequest, @@ -151,7 +151,7 @@ function loadInputSourceMapFromRemoteUri(uri, applyContext, whenLoaded) { return whenLoaded(null); } - loadRemoteResource(uri, applyContext.inlineRequest, applyContext.inlineTimeout, function (error, body) { + applyContext.fetch(uri, applyContext.inlineRequest, applyContext.inlineTimeout, function (error, body) { if (error) { applyContext.warnings.push('Missing source map at "' + uri + '" - ' + error); return whenLoaded(null); diff --git a/node_modules/clean-css/lib/reader/load-original-sources.js b/node_modules/clean-css/lib/reader/load-original-sources.js index dbe2cad09..465035d6b 100644 --- a/node_modules/clean-css/lib/reader/load-original-sources.js +++ b/node_modules/clean-css/lib/reader/load-original-sources.js @@ -2,7 +2,6 @@ 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'); @@ -10,6 +9,7 @@ var isRemoteResource = require('../utils/is-remote-resource'); function loadOriginalSources(context, callback) { var loadContext = { callback: callback, + fetch: context.options.fetch, index: 0, inline: context.options.inline, inlineRequest: context.options.inlineRequest, @@ -99,7 +99,7 @@ function loadOriginalSourceFromRemoteUri(uri, loadContext, whenLoaded) { return whenLoaded(null); } - loadRemoteResource(uri, loadContext.inlineRequest, loadContext.inlineTimeout, function (error, content) { + loadContext.fetch(uri, loadContext.inlineRequest, loadContext.inlineTimeout, function (error, content) { if (error) { loadContext.warnings.push('Missing original source at "' + uri + '" - ' + error); } diff --git a/node_modules/clean-css/lib/reader/read-sources.js b/node_modules/clean-css/lib/reader/read-sources.js index e12e23514..c9173ed62 100644 --- a/node_modules/clean-css/lib/reader/read-sources.js +++ b/node_modules/clean-css/lib/reader/read-sources.js @@ -5,7 +5,6 @@ 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'); @@ -50,27 +49,38 @@ function fromString(input, context, callback) { } function fromArray(input, context, callback) { - var inputAsImports = input.reduce(function (accumulator, uri) { - var normalizedUri = normalizeUri(uri); + var inputAsImports = input.reduce(function (accumulator, uriOrHash) { + if (typeof uriOrHash === 'string') { + return addStringSource(uriOrHash, accumulator); + } else { + return addHashSource(uriOrHash, context, accumulator); + } - accumulator.push(restoreAsImport(normalizedUri)); - return accumulator; }, []); return fromStyles(inputAsImports.join(''), context, { inline: ['all'] }, callback); } function fromHash(input, context, callback) { + var inputAsImports = addHashSource(input, context, []); + return fromStyles(inputAsImports.join(''), context, { inline: ['all'] }, callback); +} + +function addStringSource(input, imports) { + imports.push(restoreAsImport(normalizeUri(input))); + return imports; +} + +function addHashSource(input, context, imports) { var uri; var normalizedUri; var source; - var inputAsImports = []; for (uri in input) { source = input[uri]; normalizedUri = normalizeUri(uri); - inputAsImports.push(restoreAsImport(normalizedUri)); + imports.push(restoreAsImport(normalizedUri)); context.sourcesContent[normalizedUri] = source.styles; @@ -79,7 +89,7 @@ function fromHash(input, context, callback) { } } - return fromStyles(inputAsImports.join(''), context, { inline: ['all'] }, callback); + return imports; } function normalizeUri(uri) { @@ -152,6 +162,7 @@ function inline(tokens, externalContext, parentInlinerContext, callback) { callback: callback, errors: externalContext.errors, externalContext: externalContext, + fetch: externalContext.options.fetch, inlinedStylesheets: parentInlinerContext.inlinedStylesheets || externalContext.inlinedStylesheets, inline: parentInlinerContext.inline, inlineRequest: externalContext.options.inlineRequest, @@ -267,7 +278,7 @@ function inlineRemoteStylesheet(uri, mediaQuery, metadata, inlinerContext) { return isLoaded ? whenLoaded(null, inlinerContext.externalContext.sourcesContent[uri]) : - loadRemoteResource(uri, inlinerContext.inlineRequest, inlinerContext.inlineTimeout, whenLoaded); + inlinerContext.fetch(uri, inlinerContext.inlineRequest, inlinerContext.inlineTimeout, whenLoaded); } function inlineLocalStylesheet(uri, mediaQuery, metadata, inlinerContext) { diff --git a/node_modules/clean-css/lib/reader/rebase.js b/node_modules/clean-css/lib/reader/rebase.js index 4e0ff9751..181b319ad 100644 --- a/node_modules/clean-css/lib/reader/rebase.js +++ b/node_modules/clean-css/lib/reader/rebase.js @@ -91,7 +91,7 @@ function rebaseProperties(properties, validator, rebaseConfig) { for (j = 2 /* 0 is Token.PROPERTY, 1 is name */, m = property.length; j < m; j++) { value = property[j][1]; - if (validator.isValidUrl(value)) { + if (validator.isUrl(value)) { property[j][1] = rewriteUrl(value, rebaseConfig); } } diff --git a/node_modules/clean-css/lib/tokenizer/marker.js b/node_modules/clean-css/lib/tokenizer/marker.js index c3f6805a7..767a5f0e8 100644 --- a/node_modules/clean-css/lib/tokenizer/marker.js +++ b/node_modules/clean-css/lib/tokenizer/marker.js @@ -10,6 +10,7 @@ var Marker = { DOUBLE_QUOTE: '"', EXCLAMATION: '!', FORWARD_SLASH: '/', + INTERNAL: '-clean-css-', NEW_LINE_NIX: '\n', NEW_LINE_WIN: '\r', OPEN_CURLY_BRACKET: '{', diff --git a/node_modules/clean-css/lib/tokenizer/tokenize.js b/node_modules/clean-css/lib/tokenizer/tokenize.js index 15fb584a0..7c071dd93 100644 --- a/node_modules/clean-css/lib/tokenizer/tokenize.js +++ b/node_modules/clean-css/lib/tokenizer/tokenize.js @@ -28,6 +28,7 @@ var BLOCK_RULES = [ '@supports' ]; +var REPEAT_PATTERN = /^\[\s*\d+\s*\]$/; var RULE_WORD_SEPARATOR_PATTERN = /[\s\(]/; var TAIL_BROKEN_VALUE_PATTERN = /[\s|\}]*$/; @@ -377,6 +378,12 @@ function intoTokens(source, externalContext, internalContext, isNested) { propertyToken.push([Token.PROPERTY_VALUE, character, [[position.line, position.column, position.source]]]); buffer = []; + } else if (character == Marker.CLOSE_SQUARE_BRACKET && propertyToken && propertyToken.length > 1 && buffer.length > 0 && isRepeatToken(buffer)) { + buffer.push(character); + serializedBuffer = buffer.join('').trim(); + propertyToken[propertyToken.length - 1][1] += serializedBuffer; + + 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(); @@ -460,4 +467,8 @@ function tokenScopeFrom(tokenType) { } } +function isRepeatToken(buffer) { + return REPEAT_PATTERN.test(buffer.join('') + Marker.CLOSE_SQUARE_BRACKET); +} + module.exports = tokenize; diff --git a/node_modules/clean-css/lib/writer/helpers.js b/node_modules/clean-css/lib/writer/helpers.js index 0b8999e95..ab08633e8 100644 --- a/node_modules/clean-css/lib/writer/helpers.js +++ b/node_modules/clean-css/lib/writer/helpers.js @@ -85,7 +85,7 @@ function property(context, tokens, position, lastPropertyAt) { switch (token[0]) { case Token.AT_RULE: store(context, token); - store(context, position < lastPropertyAt ? semicolon(context, Breaks.AfterProperty, false) : emptyCharacter); + store(context, semicolon(context, Breaks.AfterProperty, false)); break; case Token.COMMENT: store(context, token); diff --git a/node_modules/clean-css/package.json b/node_modules/clean-css/package.json index f8a8db1f4..d612da4e3 100644 --- a/node_modules/clean-css/package.json +++ b/node_modules/clean-css/package.json @@ -1,6 +1,6 @@ { "name": "clean-css", - "version": "4.0.12", + "version": "4.1.3", "author": "Jakub Pawlowicz <contact@jakubpawlowicz.com> (http://twitter.com/jakubpawlowicz)", "description": "A well-tested CSS minifier", "license": "MIT", @@ -34,7 +34,7 @@ "source-map": "0.5.x" }, "devDependencies": { - "browserify": "13.x", + "browserify": "^14.0.0", "http-proxy": "1.x", "jshint": "2.x", "nock": "9.x", |