This commit is contained in:
Fred Boniface 2024-04-26 18:37:00 +01:00
commit 12b22bd1b2
202 changed files with 63202 additions and 0 deletions

0
deploy.sh Normal file
View File

6
dockerfile Normal file
View File

@ -0,0 +1,6 @@
FROM node:latest
WORKDIR /app
COPY package* ./
RUN npm i
COPY process.js ./process.js
CMD [ "echo", "This container is meant to be run as part of a build pipeline. Copy input files to /deploy/in, run 'node process.js', output files will be available in /deploy/out" ]

1
node_modules/.bin/minify-html generated vendored Symbolic link
View File

@ -0,0 +1 @@
../@minify-html/node/cli.js

1
node_modules/.bin/uglifyjs generated vendored Symbolic link
View File

@ -0,0 +1 @@
../uglify-js/bin/uglifyjs

85
node_modules/.package-lock.json generated vendored Normal file
View File

@ -0,0 +1,85 @@
{
"name": "web-deploy",
"version": "0.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/@minify-html/node": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/@minify-html/node/-/node-0.15.0.tgz",
"integrity": "sha512-ANzt6ZBiqtwrepVXRfa0Qn/woCkINFBjQEKiXyBmg7+51mIFQHVAUbAm6UHRrT0L3xoPG0BX0/XI3NqtjK8Vyg==",
"bin": {
"minify-html": "cli.js"
},
"engines": {
"node": ">= 8.6.0"
},
"optionalDependencies": {
"@minify-html/node-darwin-arm64": "0.15.0",
"@minify-html/node-darwin-x64": "0.15.0",
"@minify-html/node-linux-arm64": "0.15.0",
"@minify-html/node-linux-x64": "0.15.0",
"@minify-html/node-win32-x64": "0.15.0"
}
},
"node_modules/@minify-html/node-linux-x64": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/@minify-html/node-linux-x64/-/node-linux-x64-0.15.0.tgz",
"integrity": "sha512-cO893EV6O9ZHUFX+2Yge546OCo/eCiatjzJDmUmrPP56fQ7pzTRquHs4ko3t8Rg6tMKG7RT49mBuF09JWPnrgg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
]
},
"node_modules/@putout/minify": {
"version": "3.8.5",
"resolved": "https://registry.npmjs.org/@putout/minify/-/minify-3.8.5.tgz",
"integrity": "sha512-m7Umg719m9kqcq7kV6RXtoRZZyUKn1YQOExfpgbHCYCJvwHPR2J/lADtgtkCFZslUKIyqSsjYUmQeKqDGJpXDg==",
"engines": {
"node": ">=18"
}
},
"node_modules/clean-css": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
"integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
"dependencies": {
"source-map": "~0.6.0"
},
"engines": {
"node": ">= 10.0"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/uglify-js": {
"version": "3.17.4",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
"integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==",
"bin": {
"uglifyjs": "bin/uglifyjs"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/zlib": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/zlib/-/zlib-1.0.5.tgz",
"integrity": "sha512-40fpE2II+Cd3k8HWTWONfeKE2jL+P42iWJ1zzps5W51qcTsOUKM5Q5m2PFb0CLxlmFAaUuUdJGc3OfZy947v0w==",
"hasInstallScript": true,
"engines": {
"node": ">=0.2.0"
}
}
}
}

1
node_modules/@minify-html/node-linux-x64/README.md generated vendored Normal file
View File

@ -0,0 +1 @@
This is a specific platform-arch build for [@minify-html/nodejs](https://github.com/wilsonzlin/minify-html), and should not be installed directly.

BIN
node_modules/@minify-html/node-linux-x64/index.node generated vendored Executable file

Binary file not shown.

17
node_modules/@minify-html/node-linux-x64/package.json generated vendored Normal file
View File

@ -0,0 +1,17 @@
{
"name": "@minify-html/node-linux-x64",
"version": "0.15.0",
"description": "@minify-html/node native module build for linux-x64",
"main": "index.node",
"files": [
"README.md",
"index.node"
],
"repository": "wilsonzlin/minify-html",
"os": [
"linux"
],
"cpu": [
"x64"
]
}

497
node_modules/@minify-html/node/README.md generated vendored Normal file
View File

@ -0,0 +1,497 @@
<h1>
minify-html
<img width="24" src="https://wilsonl.in/minify-html/icon/cli.png">
<img width="24" src="https://wilsonl.in/minify-html/icon/deno.png">
<img width="24" src="https://wilsonl.in/minify-html/icon/java.png">
<img width="24" src="https://wilsonl.in/minify-html/icon/nodejs.png">
<img width="24" src="https://wilsonl.in/minify-html/icon/python.png">
<img width="24" src="https://wilsonl.in/minify-html/icon/ruby.png">
<img width="24" src="https://wilsonl.in/minify-html/icon/rust.png">
<img width="24" src="https://wilsonl.in/minify-html/icon/wasm.png">
</h1>
A Rust HTML minifier meticulously optimised for speed and effectiveness, with bindings for other languages.
- Advanced minification strategy beats other minifiers while being much faster.
- Uses SIMD searching, direct tries, and lookup tables.
- Handles [invalid HTML](./notes/Parsing.md), with extensive testing and [fuzzing](./fuzz).
- Uses [minify-js](https://github.com/wilsonzlin/minify-js) and [lightningcss](https://github.com/parcel-bundler/lightningcss) for super fast JS and CSS minification.
View the [changelog](./CHANGELOG.md) to see the latest updates.
## Performance
Comparison with [html-minifier](https://github.com/kangax/html-minifier) and [minimize](https://github.com/Swaagie/minimize), run on the top web pages. [See the breakdown here.](./bench)
<img width="400" alt="Chart showing speed of HTML minifiers" src="https://wilsonl.in/minify-html/bench/0.15.0/core/average-speeds.png"><img width="400" alt="Chart showing compression of HTML minifiers" src="https://wilsonl.in/minify-html/bench/0.15.0/core/average-sizes.png">
The [onepass](https://github.com/wilsonzlin/minify-html/tree/master/rust/onepass) variant is even more optimised for speed. See its [README](https://github.com/wilsonzlin/minify-html/tree/master/rust/onepass) for more details.
## Compatibility and usage
<details>
<summary><img width="24" src="https://wilsonl.in/minify-html/icon/cli.png"> <strong>CLI</strong></summary>
The CLI is called [minhtml](./minhtml).
Precompiled binaries are available for Linux (ARM64 and x64), macOS (ARM64 and x64), and Windows (x64).
You can download them in the GitHub release.
If you have Cargo installed, you can also build and install from source: `cargo install minhtml`.
### Use
Use the `--help` argument for more details.
```bash
minhtml --output /path/to/output.min.html --keep-closing-tags --minify-css /path/to/src.html
```
To quickly parallel process a batch of files in place:
```bash
minhtml --keep-closing-tags --minify-css /path/to/**/*.html
```
</details>
<details>
<summary><img width="24" src="https://wilsonl.in/minify-html/icon/rust.png"> <strong>Rust</strong></summary>
### Get
```toml
[dependencies]
minify-html = "0.15.0"
```
### Use
Check out the [docs](https://docs.rs/minify-html) for API and usage examples.
</details>
<details>
<summary><img width="24" src="https://wilsonl.in/minify-html/icon/deno.png"> <strong>Deno</strong></summary>
- Package: https://wilsonl.in/minify-html/deno/0.15.0/index.js
- Binding: [WASM](https://webassembly.org/)
- Platforms: All
### Use
```ts
import init, {minify} from "https://wilsonl.in/minify-html/deno/0.15.0/index.js";
const encoder = new TextEncoder();
const decoder = new TextDecoder();
await init();
const minified = decoder.decode(minify(encoder.encode("<p> Hello, world! </p>"), { keep_spaces_between_attributes: true, keep_comments: true }));
```
All [`Cfg` fields](https://docs.rs/minify-html/latest/minify_html/struct.Cfg.html) are available as snake_case properties on the object provided as the second argument; if any are not set, they default to `false`.
</details>
<details>
<summary><img width="24" src="https://wilsonl.in/minify-html/icon/nodejs.png"> <strong>Node.js</strong></summary>
- Package: [@minify-html/node](https://www.npmjs.com/package/@minify-html/node)
- Binding: [Neon](https://github.com/neon-bindings/neon)
- Platforms: Linux (ARM64 and x64), macOS (ARM64 and x64), Windows (x64); Node.js 8.6.0 and higher
### Get
Using npm:
```bash
npm i @minify-html/node
```
Using Yarn:
```bash
yarn add @minify-html/node
```
### Use
TypeScript definitions are available.
```ts
import { Buffer } from "node:buffer";
import minifyHtml from "@minify-html/node";
// Or `const minifyHtml = require("@minify-html/node")` if not using TS/ESM.
const minified = minifyHtml.minify(Buffer.from("<p> Hello, world! </p>"), { keep_spaces_between_attributes: true, keep_comments: true });
```
All [`Cfg` fields](https://docs.rs/minify-html/latest/minify_html/struct.Cfg.html) are available as snake_case properties on the object provided as the second argument; if any are not set, they default to `false`.
</details>
<details>
<summary><img width="24" src="https://wilsonl.in/minify-html/icon/java.png"> <strong>Java</strong></summary>
- Package: [in.wilsonl.minifyhtml](https://search.maven.org/artifact/in.wilsonl.minifyhtml/minify-html)
- Binding: [JNI](https://github.com/jni-rs/jni-rs)
- Platforms: Linux (ARM64 and x64), macOS (ARM64 and x64), Windows (x64); Java 7 and higher
### Get
Add as a Maven dependency:
```xml
<dependency>
<groupId>in.wilsonl.minifyhtml</groupId>
<artifactId>minify-html</artifactId>
<version>0.15.0</version>
</dependency>
```
### Use
```java
import in.wilsonl.minifyhtml.Configuration;
import in.wilsonl.minifyhtml.MinifyHtml;
Configuration cfg = new Configuration.Builder()
.setKeepHtmlAndHeadOpeningTags(true)
.setMinifyCss(true)
.build();
String minified = MinifyHtml.minify("<p> Hello, world! </p>", cfg);
```
All [`Cfg` fields](https://docs.rs/minify-html/latest/minify_html/struct.Cfg.html) are available as camelCase setter methods on the `Builder`; if any are not set, they default to `false`.
</details>
<details>
<summary><img width="24" src="https://wilsonl.in/minify-html/icon/python.png"> <strong>Python</strong></summary>
- Package: [minify-html](https://pypi.org/project/minify-html)
- Binding: [PyO3](https://github.com/PyO3/pyo3)
- Platforms: Linux (ARM64 and x64), macOS (ARM64 and x64), Windows (x64); Python 3.8 to 3.12
### Get
Add the PyPI project as a dependency and install it using `pip` or `pipenv`.
### Use
```python
import minify_html
minified = minify_html.minify("<p> Hello, world! </p>", minify_js=True, remove_processing_instructions=True)
```
All [`Cfg` fields](https://docs.rs/minify-html/latest/minify_html/struct.Cfg.html) are available as Python keyword arguments; if any are omitted, they default to `False`.
</details>
<details>
<summary><img width="24" src="https://wilsonl.in/minify-html/icon/ruby.png"> <strong>Ruby</strong></summary>
- Package: [minify_html](https://rubygems.org/gems/minify_html)
- Binding: [rb-sys](https://github.com/oxidize-rb/rb-sys) and [magnus](https://github.com/matsadler/magnus)
- Platforms: Linux (ARM64 and x64), macOS (ARM64 and x64), Windows (x64); Ruby 2.7 to 3.2
### Get
Add the library as a dependency to `Gemfile` or `*.gemspec`.
### Use
```ruby
require 'minify_html'
print minify_html("<p> Hello, world! </p>", { :keep_spaces_between_attributes => true, :minify_js => true })
```
All [`Cfg` fields](https://docs.rs/minify-html/latest/minify_html/struct.Cfg.html) are available; if any are omitted, they default to `false`.
</details>
<details>
<summary><img width="24" src="https://wilsonl.in/minify-html/icon/wasm.png"> <strong>WASM</strong></summary>
- Package: [@minify-html/wasm](https://npmjs.org/package/@minify-html/wasm)
- Binding: [WASM](https://webassembly.org/)
- Platforms: All
A bundler may be required to use the WebAssembly module, see [this](https://rustwasm.github.io/wasm-bindgen/reference/deployment.html#bundlers) for more details.
### Use
```ts
import init, {minify} from "@minify-html/wasm";
const encoder = new TextEncoder();
const decoder = new TextDecoder();
await init();
const minified = decoder.decode(minify(encoder.encode("<p> Hello, world! </p>"), { keep_spaces_between_attributes: true, keep_comments: true }));
```
All [`Cfg` fields](https://docs.rs/minify-html/latest/minify_html/struct.Cfg.html) are available as snake_case properties on the object provided as the second argument; if any are not set, they default to `false`.
</details>
## Templating syntax
minify-html can parse and preserve `{{`/`{%`/`{#` and `<%` syntax in the source code, which allows minification of many HTML templates written for most engines like Pebble, Mustache, Django, Go, Jinja, Twix, Nunjucks, Handlebars, Sailfish, JSP, EJS, and ERB. Look for the `preserve_*_template_syntax` Cfg options.
PHP blocks (`<?php` or `<?=`) also happen to be processing instructions, which are preserved by default.
Note that in all of these syntax, the parsing is "dumb": it will simply look for the next subsequence of characters that match the closing delimiter. This may cause issues if nesting or string literals appear inside these blocks, but this should be rare.
## Minification
Note that some of the minification done can result in HTML that will not pass validation, but remain interpreted and rendered correctly by the browser; essentially, the laxness of the browser is taken advantage of for better minification. To prevent this, refer to these configuration options:
- `do_not_minify_doctype`
- `ensure_spec_compliant_unquoted_attribute_values`
- `keep_spaces_between_attributes`
### Whitespace
minify-html has advanced context-aware whitespace minification that does things such as:
- Leave whitespace untouched in `pre` and `code`, which are whitespace sensitive.
- Trim and collapse whitespace in content tags, as whitespace is collapsed anyway when rendered.
- Remove whitespace in layout tags, which allows the use of inline layouts while keeping formatted code.
#### Methods
There are three whitespace minification methods. When processing text content, minify-html chooses which ones to use depending on the containing element.
<details>
<summary><strong>Collapse whitespace</strong></summary>
> **Applies to:** any element except [whitespace sensitive](./minify-html-common/src/spec/tag/whitespace.rs) elements.
Reduce a sequence of whitespace characters in text nodes to a single space (U+0020).
<table><thead><tr><th>Before<th>After<tbody><tr><td>
```html
<p>
··The·quick·brown·fox↵
··jumps·over·the·lazy↵
··dog.↵
</p>
```
<td>
```html
<p>·The·quick·brown·fox·jumps·over·the·lazy·dog.·</p>
```
</table>
</details>
<details>
<summary><strong>Destroy whole whitespace</strong></summary>
> **Applies to:** any element except [whitespace sensitive](./minify-html-common/src/spec/tag/whitespace.rs), [content](src/spec/tag/whitespace.rs), [content-first](./minify-html-common/src/spec/tag/whitespace.rs), and [formatting](./minify-html-common/src/spec/tag/whitespace.rs) elements.
Remove any text nodes between tags that only consist of whitespace characters.
<table><thead><tr><th>Before<th>After<tbody><tr><td>
```html
<ul>
··<li>A</li>
··<li>B</li>
··<li>C</li>
</ul>
```
<td>
```html
<ul>
··<li>A</li><li>B</li><li>C</li>
</ul>
```
</table>
</details>
<details>
<summary><strong>Trim whitespace</strong></summary>
> **Applies to:** any element except [whitespace sensitive](./minify-html-common/src/spec/tag/whitespace.rs) and [formatting](./minify-html-common/src/spec/tag/whitespace.rs) elements.
Remove any leading/trailing whitespace from any leading/trailing text nodes of a tag.
<table><thead><tr><th>Before<th>After<tbody><tr><td>
```html
<p>
··Hey,·I·<em>just</em>·found↵
··out·about·this·<strong>cool</strong>·website!↵
··<sup>[1]</sup>
</p>
```
<td>
```html
<p>Hey,·I·<em>just</em>·found↵
··out·about·this·<strong>cool</strong>·website!↵
··<sup>[1]</sup></p>
```
</table>
</details>
#### Element types
minify-html assumes HTML and SVG elements are used in specific ways, based on standards and best practices. By making these assumptions, it can apply optimal whitespace minification strategies. If these assumptions do not hold, consider adjusting the HTML source or turning off whitespace minification.
|Group|Elements|Expected children|
|---|---|---|
|Formatting|`a`, `strong`, [and others](./minify-html-common/src/spec/tag/whitespace.rs)|Formatting elements, text.|
|Content|`h1`, `p`, [and others](./minify-html-common/src/spec/tag/whitespace.rs)|Formatting elements, text.|
|Layout|`div`, `ul`, [and others](./minify-html-common/src/spec/tag/whitespace.rs)|Layout elements, content elements.|
|Content-first|`label`, `li`, [and others](./minify-html-common/src/spec/tag/whitespace.rs)|Like content but could be layout with only one child.|
<details>
<summary><strong>Formatting elements</strong></summary>
> Whitespace is collapsed.
Formatting elements are usually inline elements that wrap around part of some text in a content element, so its whitespace isn't trimmed as they're probably part of the content.
</details>
<details>
<summary><strong>Content elements</strong></summary>
> Whitespace is trimmed and collapsed.
Content elements usually represent a contiguous and complete unit of content such as a paragraph. As such, whitespace is significant but sequences of them are most likely due to formatting.
###### Before
```html
<p>
··Hey,·I·<em>just</em>·found↵
··out·about·this·<strong>cool</strong>·website!↵
··<sup>[1]</sup>
</p>
```
###### After
```html
<p>Hey,·I·<em>just</em>·found·out·about·this·<strong>cool</strong>·website!·<sup>[1]</sup></p>
```
</details>
<details>
<summary><strong>Layout elements</strong></summary>
> Whitespace is trimmed and collapsed. Whole whitespace is removed.
These elements should only contain other elements and no text. This makes it possible to remove whole whitespace, which is useful when using `display: inline-block` so that whitespace between elements (e.g. indentation) does not alter layout and styling.
###### Before
```html
<ul>
··<li>A</li>
··<li>B</li>
··<li>C</li>
</ul>
```
###### After
```html
<ul><li>A</li><li>B</li><li>C</li></ul>
```
</details>
<details>
<summary><strong>Content-first elements</strong></summary>
> Whitespace is trimmed and collapsed.
These elements are usually like content elements but are occasionally used like a layout element with one child. Whole whitespace is not removed as it might contain content, but this is OK for using as layout as there is only one child and whitespace is trimmed.
###### Before
```html
<li>
··<article>
····<section></section>
····<section></section>
··</article>
</li>
```
###### After
```html
<li><article><section></section><section></section></article></li>
```
</details>
### Tags
[Optional opening and closing tags](https://html.spec.whatwg.org/multipage/syntax.html#syntax-tag-omission) are removed.
### Attributes
Any entities in attribute values are decoded, and then the shortest representation of the value is calculated and used:
- Double quoted, with any `"` encoded.
- Single quoted, with any `'` encoded.
- Unquoted, with `"`/`'` first character (if applicable), any `>`, and any whitespace encoded.
Attributes have their whitespace (after any decoding) trimmed and collapsed when possible.
[Boolean attribute](https://github.com/wilsonzlin/html-data) values are removed.
[Some other attributes](https://github.com/wilsonzlin/html-data) are completely removed if their value is empty or the default value after any processing.
`type` attributes on `script` tags with a value equaling a [JavaScript MIME type](https://mimesniff.spec.whatwg.org/#javascript-mime-type) are removed.
If an attribute value is empty after any processing, everything but the name is completely removed (i.e. no `=`), as an empty attribute is implicitly [the same](https://html.spec.whatwg.org/multipage/syntax.html#attributes-2) as an attribute with an empty string value.
Spaces are removed between attributes when possible.
### Entities
Entities are decoded if they're valid and shorter or equal in length when decoded. UTF-8 sequences that have a shorter entity representation are encoded.
Numeric entities that do not refer to a valid [Unicode Scalar Value](https://www.unicode.org/glossary/#unicode_scalar_value) are replaced with the [replacement character](https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character).
Encoding is avoided when possible; for example, `<` are only encoded in content if they are followed by a valid tag name character.
If necessary, the shortest entity representation is chosen.
### Comments
Comments are removed.
### Ignored
Bangs, [processing instructions](https://en.wikipedia.org/wiki/Processing_Instruction), and empty elements are not removed as it is assumed there is a special reason for their declaration.
## Parsing
minify-html can process any HTML, handling all possible syntax (including invalid ones) gracefully like browsers. See [Parsing.md](./notes/Parsing.md) for more details.
## Issues and contributions
Pull requests and any contributions welcome!
If minify-html did something unexpected, misunderstood some syntax, or incorrectly kept/removed some code, [raise an issue](https://github.com/wilsonzlin/minify-html/issues) with some relevant code that can be used to reproduce and investigate the issue.

25
node_modules/@minify-html/node/cli.js generated vendored Executable file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env node
const lib = require(".");
const fs = require("fs");
const cfg = {};
let input;
let output;
const args = process.argv.slice(2);
let arg;
while ((arg = args.shift()) !== undefined) {
if (arg.slice(0, 2) === "--") {
const name = arg.slice(2);
if (name === "output") {
output = args.shift();
} else {
cfg[name.replace(/-/g, "_")] = true;
}
} else {
input = arg;
}
}
const min = lib.minify(fs.readFileSync(input), cfg);
fs.writeFileSync(output, min);

42
node_modules/@minify-html/node/index.d.ts generated vendored Normal file
View File

@ -0,0 +1,42 @@
/**
* Minifies a Buffer containing UTF-8 HTML code.
*
* @param src - Source HTML code
* @param cfg - Configurable minifier settings to use
* @returns Minified HTML code
*/
export function minify(
src: Buffer,
cfg: {
/** Do not minify DOCTYPEs. Minified DOCTYPEs may not be spec compliant. */
do_not_minify_doctype?: boolean;
/** Ensure all unquoted attribute values in the output do not contain any characters prohibited by the WHATWG specification. */
ensure_spec_compliant_unquoted_attribute_values?: boolean;
/** Do not omit closing tags when possible. */
keep_closing_tags?: boolean;
/** Do not omit `<html>` and `<head>` opening tags when they don't have attributes. */
keep_html_and_head_opening_tags?: boolean;
/** Keep spaces between attributes when possible to conform to HTML standards. */
keep_spaces_between_attributes?: boolean;
/** Keep all comments. */
keep_comments?: boolean;
/** Keep SSI comments. */
keep_ssi_comments?: boolean;
/** When `{{`, `{#`, or `{%` are seen in content, all source code until the subsequent matching closing `}}`, `#}`, or `%}` respectively gets piped through untouched. */
preserve_brace_template_syntax?: boolean;
/** When `<%` is seen in content, all source code until the subsequent matching closing `%>` gets piped through untouched. */
preserve_chevron_percent_template_syntax?: boolean;
/**
* If enabled, content in `<script>` tags with a JS or no [MIME type](https://mimesniff.spec.whatwg.org/#javascript-mime-type) will be minified using [minify-js](https://github.com/wilsonzlin/minify-js).
*/
minify_js?: boolean;
/**
* If enabled, CSS in `<style>` tags and `style` attributes will be minified.
*/
minify_css?: boolean;
/** Remove all bangs. */
remove_bangs?: boolean;
/** Remove all processing_instructions. */
remove_processing_instructions?: boolean;
}
): Buffer;

8
node_modules/@minify-html/node/index.js generated vendored Normal file
View File

@ -0,0 +1,8 @@
// This wrapper file also exists to allow importing from ESM contexts, as Node.js does not allow importing ".node" modules directly from ESM.
try {
// Prioritise any local built binary.
module.exports = require("./index.node");
} catch {
module.exports = require(`@minify-html/node-${process.platform}-${process.arch}`);
}

59
node_modules/@minify-html/node/package.json generated vendored Normal file
View File

@ -0,0 +1,59 @@
{
"name": "@minify-html/node",
"description": "Extremely fast and smart HTML + JS + CSS minifier",
"bin": {
"minify-html": "./cli.js"
},
"main": "index.js",
"files": [
"cli.js",
"index.d.ts",
"index.js",
"README.md"
],
"version": "0.15.0",
"types": "index.d.ts",
"scripts": {
"build": "cargo-cp-artifact --artifact cdylib minify-html-nodejs index.node -- cargo build --message-format=json-render-diagnostics",
"build-debug": "npm run build --",
"build-release": "npm run build -- --release",
"clean": "shx rm -rf target index.node"
},
"repository": {
"type": "git",
"url": "git+https://github.com/wilsonzlin/minify-html.git"
},
"author": {
"email": "npm@wilsonl.in",
"name": "Wilson Lin",
"url": "https://wilsonl.in/"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/wilsonzlin/minify-html/issues"
},
"engines": {
"node": ">= 8.6.0"
},
"homepage": "https://github.com/wilsonzlin/minify-html#readme",
"optionalDependencies": {
"@minify-html/node-darwin-arm64": "0.15.0",
"@minify-html/node-darwin-x64": "0.15.0",
"@minify-html/node-linux-arm64": "0.15.0",
"@minify-html/node-linux-x64": "0.15.0",
"@minify-html/node-win32-x64": "0.15.0"
},
"devDependencies": {
"@types/node": "^14.6.0",
"cargo-cp-artifact": "^0.1",
"shx": "^0.3.4"
},
"keywords": [
"compress",
"compressor",
"fast",
"html",
"minifier",
"minify"
]
}

21
node_modules/@putout/minify/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) coderaiser
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

114
node_modules/@putout/minify/README.md generated vendored Normal file
View File

@ -0,0 +1,114 @@
# @putout/minify [![NPM version][NPMIMGURL]][NPMURL] [![Coverage Status][CoverageIMGURL]][CoverageURL]
[NPMIMGURL]: https://img.shields.io/npm/v/@putout/minify.svg?style=flat&longCache=true
[NPMURL]: https://npmjs.org/package/@putout/minify "npm"
[CoverageURL]: https://coveralls.io/github/putoutjs/minify?branch=master
[CoverageIMGURL]: https://coveralls.io/repos/putoutjs/minify/badge.svg?branch=master&service=github
🐊[**Putout**](https://github.com/coderaiser/putout)-based JavaScript minifier.
## Install
```sh
npm i @putout/minify
```
## Supported minifier transforms
Here is list of supported 🐊**Putout** transformations:
- ✅ [`apply-template-literals`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-apply-template-literals#readme);
- ✅ [`apply-optional-chaining`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-apply-optional-chaining#readme);
- ✅ [`conditions`](https://github.com/coderaiser/putout/tree/v29.10.0/packages/plugin-conditions#readme);
- ✅ [`convert-to-arrow-function`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-convert-to-arrow-functions#readme);
- ✅ [`convert-arguments-to-rest`](https://github.com/coderaiser/putout/tree/v29.10.1/packages/plugin-convert-arguments-to-rest#readme);
- ✅ [`declare`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-declare#readme);
- ✅ [`extract-sequence-expressions`](https://github.com/coderaiser/putout/tree/v34.0.0/packages/plugin-extract-sequence-expressions#readme);
- ✅ [`for-of`](https://github.com/coderaiser/putout/tree/v34.0.0/packages/plugin-for-of#readme);
- ✅ [`logical-expressions`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-logical-expressions#readme);
- ✅ [`merge-duplicate-functions`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-merge-duplicate-functions#readme);
- ✅ [`merge-duplicate-imports`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-merge-duplicate-imports#readme);
- ✅ [`merge-destructuring-properties`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-merge-destructuring-properties#readme);
- ✅ [`minify`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-minify#readme);
- ✅ [`new`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-new#readme);
- ✅ [`regexp`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-regexp#readme);
- ✅ [`reuse-duplicate-init`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-reuse-duplicate-init#readme);
- ✅ [`remove-empty`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-remove-empty#readme);
- ✅ [`remove-nested-blocks`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-remove-nested-blocks#readme);
- ✅ [`remove-unused-variables`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-remove-unused-variables#readme);
- ✅ [`remove-unused-expressions`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-remove-unused-expressions#readme);
- ✅ [`remove-unreferenced-variables`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-remove-unreferenced-variables#readme);
- ✅ [`remove-useless-arguments`](https://github.com/coderaiser/putout/tree/master/packages/plugin-remove-useless-arguments#readme);
- ✅ [`remove-useless-else`](https://github.com/coderaiser/putout/tree/master/packages/plugin-remove-useless-else#readme);
- ✅ [`remove-useless-return`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-remove-useless-return#readme);
- ✅ [`remove-useless-spread`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-remove-useless-spread/#readme);
- ✅ [`remove-useless-variables`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-remove-useless-variables#readme);
- ✅ [`remove-console`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-remove-console#readme);
- ✅ [`remove-debugger`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-remove-debugger#readme);
- ✅ [`remove-unreachable-code`](https://github.com/coderaiser/putout/tree/v29.0.0/packages/plugin-remove-unreachable-code#readme);
- ✅ [`simplify-ternary`](https://github.com/coderaiser/putout/tree/v34.0.0/packages/plugin-simplify-ternary#readme);
- ✅ [`types`](https://github.com/coderaiser/putout/tree/v29.7.1/packages/plugin-types#readme);
## API
```js
import {minify} from '@putout/minify';
minify(`
const a = 5;
const b = 6;
fn(a, b);
`);
```
```
// returns
var a=5,b=6;fn(a,b);
```
### Options
You can also pass options to `minify`. Here is list of default options:
```js
({
mangleClassNames: true,
mangle: true,
removeUnusedVariables: true,
removeConsole: false,
removeUselessSpread: true,
applyTemplateLiterals: true,
});
```
And usage example:
```js
import {minify} from '@putout/minify';
const source = `
const a = 5;
const b = 6;
`;
minify(source, {
removeUnusedVariables: true,
});
```
### How it's compared to [Terser](https://github.com/terser/terser)?
For [such code](https://github.com/coderaiser/minify/issues/96#issuecomment-1546605157):
- 🔥 `@putout/minify`: `473B`
- ❌ `terser`: `482B`
`react.js`:
- 🔥 `@putout/minify`: `16309B`
- ❌ `terser`: `16346B`
## License
MIT

1
node_modules/@putout/minify/bundle/minify.min.js generated vendored Normal file

File diff suppressed because one or more lines are too long

112
node_modules/@putout/minify/package.json generated vendored Normal file
View File

@ -0,0 +1,112 @@
{
"name": "@putout/minify",
"version": "3.8.5",
"type": "module",
"author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
"description": "🐊Putout-based minifier",
"homepage": "https://github.com/putoutjs/minify#readme",
"files": [
"bundle/minify.min.js",
"README.md"
],
"exports": {
".": "./bundle/minify.min.js",
"./dev": "./lib/minify.js"
},
"repository": {
"type": "git",
"url": "git://github.com/putoutjs/minify.git"
},
"scripts": {
"wisdom": "madrun wisdom",
"test": "madrun test",
"watch:test": "madrun watch:test",
"lint": "madrun lint",
"fresh:lint": "madrun fresh:lint",
"lint:fresh": "madrun lint:fresh",
"fix:lint": "madrun fix:lint",
"coverage": "madrun coverage",
"coverage:html": "madrun coverage:html",
"report": "madrun report",
"build": "madrun build",
"minify": "madrun minify"
},
"dependencies": {},
"keywords": [
"putout",
"minify",
"AST",
"babel",
"printer",
"api",
"traverse",
"generate"
],
"devDependencies": {
"@putout/plugin-apply-optional-chaining": "^5.0.1",
"@putout/plugin-apply-shorthand-properties": "^5.1.0",
"@putout/plugin-apply-template-literals": "^3.0.0",
"@putout/plugin-conditions": "^4.0.1",
"@putout/plugin-convert-arguments-to-rest": "^2.0.0",
"@putout/plugin-convert-to-arrow-function": "^4.0.0",
"@putout/plugin-declare": "^3.0.0",
"@putout/plugin-extract-sequence-expressions": "^3.5.0",
"@putout/plugin-for-of": "^4.0.0",
"@putout/plugin-logical-expressions": "^5.0.0",
"@putout/plugin-merge-destructuring-properties": "^8.0.0",
"@putout/plugin-merge-duplicate-functions": "^2.0.0",
"@putout/plugin-merge-duplicate-imports": "^11.0.0",
"@putout/plugin-minify": "^8.2.0",
"@putout/plugin-new": "^3.0.1",
"@putout/plugin-promises": "^14.0.0",
"@putout/plugin-regexp": "^8.0.0",
"@putout/plugin-remove-console": "^6.0.0",
"@putout/plugin-remove-debugger": "^6.0.0",
"@putout/plugin-remove-empty": "^11.0.0",
"@putout/plugin-remove-nested-blocks": "^6.3.0",
"@putout/plugin-remove-unreachable-code": "^1.2.0",
"@putout/plugin-remove-unreferenced-variables": "^3.0.0",
"@putout/plugin-remove-unused-expressions": "^8.0.0",
"@putout/plugin-remove-unused-variables": "^8.1.0",
"@putout/plugin-remove-useless-arguments": "^8.0.0",
"@putout/plugin-remove-useless-else": "^2.0.0",
"@putout/plugin-remove-useless-return": "^6.0.0",
"@putout/plugin-remove-useless-spread": "^10.0.1",
"@putout/plugin-remove-useless-variables": "^10.0.0",
"@putout/plugin-reuse-duplicate-init": "^5.0.0",
"@putout/plugin-simplify-ternary": "^7.0.0",
"@putout/plugin-types": "^3.0.0",
"@putout/printer": "^8.8.0",
"@rollup/plugin-alias": "^5.0.0",
"@rollup/plugin-commonjs": "^25.0.1",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-replace": "^5.0.2",
"c8": "^9.0.0",
"eslint": "^8.0.1",
"eslint-plugin-n": "^16.0.0",
"eslint-plugin-putout": "^22.2.1",
"fullstore": "^3.0.0",
"just-kebab-case": "^4.2.0",
"just-snake-case": "^3.2.0",
"madrun": "^10.0.0",
"minify": "^11.1.0",
"mock-require": "^3.0.3",
"montag": "^1.0.0",
"nodemon": "^3.0.1",
"putout": "^35.0.0",
"rendy": "^4.1.3",
"rollup": "^4.5.0",
"rollup-plugin-node-externals": "^7.0.1",
"rollup-plugin-polyfill-node": "^0.13.0",
"supertape": "^10.0.0",
"try-catch": "^3.0.0"
},
"license": "MIT",
"engines": {
"node": ">=18"
},
"publishConfig": {
"access": "public"
}
}

1504
node_modules/clean-css/History.md generated vendored Normal file

File diff suppressed because it is too large Load Diff

19
node_modules/clean-css/LICENSE generated vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (C) 2017 JakubPawlowicz.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

987
node_modules/clean-css/README.md generated vendored Normal file
View File

@ -0,0 +1,987 @@
<h1 align="center">
<br/>
<img src="./logo.v2.svg" alt="clean-css logo" width="525px"/>
<br/>
<br/>
</h1>
[![npm version](https://img.shields.io/npm/v/clean-css.svg?style=flat)](https://www.npmjs.com/package/clean-css)
[![Build Status](https://img.shields.io/github/workflow/status/clean-css/clean-css/Tests/master)](https://github.com/clean-css/clean-css/actions?query=workflow%3ATests+branch%3Amaster)
[![PPC Linux Build Status](https://img.shields.io/travis/clean-css/clean-css/master.svg?style=flat&label=PPC%20Linux%20build)](https://travis-ci.org/clean-css/clean-css)
[![Dependency Status](https://img.shields.io/david/clean-css/clean-css.svg?style=flat)](https://david-dm.org/clean-css/clean-css)
[![npm Downloads](https://img.shields.io/npm/dm/clean-css.svg)](https://npmcharts.com/compare/clean-css?minimal=true)
clean-css is a fast and efficient CSS optimizer for [Node.js](http://nodejs.org/) platform and [any modern browser](https://clean-css.github.io/).
According to [tests](http://goalsmashers.github.io/css-minification-benchmark/) it is one of the best available.
**Table of Contents**
- [Node.js version support](#nodejs-version-support)
- [Install](#install)
- [Use](#use)
* [What's new in version 5.3](#whats-new-in-version-53)
* [What's new in version 5.0](#whats-new-in-version-50)
* [What's new in version 4.2](#whats-new-in-version-42)
* [What's new in version 4.1](#whats-new-in-version-41)
* [Important: 4.0 breaking changes](#important-40-breaking-changes)
* [Constructor options](#constructor-options)
* [Compatibility modes](#compatibility-modes)
* [Fetch option](#fetch-option)
* [Formatting options](#formatting-options)
* [Inlining options](#inlining-options)
* [Optimization levels](#optimization-levels)
+ [Level 0 optimizations](#level-0-optimizations)
+ [Level 1 optimizations](#level-1-optimizations)
+ [Level 2 optimizations](#level-2-optimizations)
* [Plugins](#plugins)
* [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 multiple files without concatenating them into one output file?](#how-to-process-multiple-files-without-concatenating-them-into-one-output-file)
* [How to process remote `@import`s correctly?](#how-to-process-remote-imports-correctly)
* [How to apply arbitrary transformations to CSS properties?](#how-to-apply-arbitrary-transformations-to-css-properties)
* [How to specify a custom rounding precision?](#how-to-specify-a-custom-rounding-precision)
* [How to keep a CSS fragment intact?](#how-to-keep-a-css-fragment-intact)
* [How to preserve a comment block?](#how-to-preserve-a-comment-block)
* [How to rebase relative image URLs?](#how-to-rebase-relative-image-urls)
* [How to work with source maps?](#how-to-work-with-source-maps)
* [How to apply level 1 & 2 optimizations at the same time?](#how-to-apply-level-1--2-optimizations-at-the-same-time)
* [What level 2 optimizations do?](#what-level-2-optimizations-do)
* [What errors and warnings are?](#what-errors-and-warnings-are)
* [How to use clean-css with build tools?](#how-to-use-clean-css-with-build-tools)
* [How to use clean-css from web browser?](#how-to-use-clean-css-from-web-browser)
- [Contributing](#contributing)
* [How to get started?](#how-to-get-started)
- [Acknowledgments](#acknowledgments)
- [License](#license)
# Node.js version support
clean-css requires Node.js 10.0+ (tested on Linux, OS X, and Windows)
# Install
```
npm install --save-dev clean-css
```
# Use
```js
var CleanCSS = require('clean-css');
var input = 'a{font-weight:bold;}';
var options = { /* options */ };
var output = new CleanCSS(options).minify(input);
```
## What's new in version 5.3
clean-css 5.3 introduces one new feature:
* variables can be optimized using level 1's `variableValueOptimizers` option, which accepts a list of [value optimizers](https://github.com/clean-css/clean-css/blob/master/lib/optimizer/level-1/value-optimizers.js) or a list of their names, e.g. `variableValueOptimizers: ['color', 'fraction']`.
## What's new in version 5.0
clean-css 5.0 introduced some breaking changes:
* Node.js 6.x and 8.x are officially no longer supported;
* `transform` callback in level-1 optimizations is removed in favor of new [plugins](#plugins) interface;
* changes default Internet Explorer compatibility from 10+ to >11, to revert the old default use `{ compatibility: 'ie10' }` flag;
* changes default `rebase` option from `true` to `false` so URLs are not rebased by default. Please note that if you set `rebaseTo` option it still counts as setting `rebase: true` to preserve some of the backward compatibility.
And on the new features side of things:
* format options now accepts numerical values for all breaks, which will allow you to have more control over output formatting, e.g. `format: {breaks: {afterComment: 2}}` means clean-css will add two line breaks after each comment
* a new `batch` option (defaults to `false`) is added, when set to `true` it will process all inputs, given either as an array or a hash, without concatenating them.
## What's new in version 4.2
clean-css 4.2 introduces the following changes / features:
* Adds `process` method for compatibility with optimize-css-assets-webpack-plugin;
* new `transition` property optimizer;
* preserves any CSS content between `/* clean-css ignore:start */` and `/* clean-css ignore:end */` comments;
* allows filtering based on selector in `transform` callback, see [example](#how-to-apply-arbitrary-transformations-to-css-properties);
* adds configurable line breaks via `format: { breakWith: 'lf' }` option.
## 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://clean-css.github.io/) gets an improved settings panel with "reset to defaults", instant option changes, and settings being persisted across sessions.
## Important: 4.0 breaking changes
clean-css 4.0 introduces some breaking changes:
* API and CLI interfaces are split, so API stays in this repository while CLI moves to [clean-css-cli](https://github.com/clean-css/clean-css-cli);
* `root`, `relativeTo`, and `target` options are replaced by a single `rebaseTo` option - this means that rebasing URLs and import inlining is much simpler but may not be (YMMV) as powerful as in 3.x;
* `debug` option is gone as stats are always provided in output object under `stats` property;
* `roundingPrecision` is disabled by default;
* `roundingPrecision` applies to **all** units now, not only `px` as in 3.x;
* `processImport` and `processImportFrom` are merged into `inline` option which defaults to `local`. Remote `@import` rules are **NOT** inlined by default anymore;
* splits `inliner: { request: ..., timeout: ... }` option into `inlineRequest` and `inlineTimeout` options;
* remote resources without a protocol, e.g. `//fonts.googleapis.com/css?family=Domine:700`, are not inlined anymore;
* changes default Internet Explorer compatibility from 9+ to 10+, to revert the old default use `{ compatibility: 'ie9' }` flag;
* renames `keepSpecialComments` to `specialComments`;
* moves `roundingPrecision` and `specialComments` to level 1 optimizations options, see examples;
* moves `mediaMerging`, `restructuring`, `semanticMerging`, and `shorthandCompacting` to level 2 optimizations options, see examples below;
* renames `shorthandCompacting` option to `mergeIntoShorthands`;
* level 1 optimizations are the new default, up to 3.x it was level 2;
* `keepBreaks` option is replaced with `{ format: 'keep-breaks' }` to ease transition;
* `sourceMap` option has to be a boolean from now on - to specify an input source map pass it a 2nd argument to `minify` method or via a hash instead;
* `aggressiveMerging` option is removed as aggressive merging is replaced by smarter override merging.
## Constructor options
clean-css constructor accepts a hash as a parameter with the following options available:
* `compatibility` - controls compatibility mode used; defaults to `ie10+`; see [compatibility modes](#compatibility-modes) for examples;
* `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);
* `inlineTimeout` - controls number of milliseconds after which inlining a remote `@import` fails; defaults to 5000;
* `level` - controls optimization level used; defaults to `1`; see [optimization levels](#optimization-levels) for examples;
* `rebase` - controls URL rebasing; defaults to `false`;
* `rebaseTo` - controls a directory to which all URLs are rebased, most likely the directory under which the output file will live; defaults to the current directory;
* `returnPromise` - controls whether `minify` method returns a Promise object or not; defaults to `false`; see [promise interface](#promise-interface) for examples;
* `sourceMap` - controls whether an output source map is built; defaults to `false`;
* `sourceMapInlineSources` - controls embedding sources inside a source map's `sourcesContent` field; defaults to false.
## Compatibility modes
There is a certain number of compatibility mode shortcuts, namely:
* `new CleanCSS({ compatibility: '*' })` (default) - Internet Explorer 10+ compatibility mode
* `new CleanCSS({ compatibility: 'ie9' })` - Internet Explorer 9+ compatibility mode
* `new CleanCSS({ compatibility: 'ie8' })` - Internet Explorer 8+ compatibility mode
* `new CleanCSS({ compatibility: 'ie7' })` - Internet Explorer 7+ compatibility mode
Each of these modes is an alias to a [fine grained configuration](https://github.com/clean-css/clean-css/blob/master/lib/options/compatibility.js), with the following options available:
```js
new CleanCSS({
compatibility: {
colors: {
hexAlpha: false, // controls 4- and 8-character hex color support
opacity: true // controls `rgba()` / `hsla()` color support
},
properties: {
backgroundClipMerging: true, // controls background-clip merging into shorthand
backgroundOriginMerging: true, // controls background-origin merging into shorthand
backgroundSizeMerging: true, // controls background-size merging into shorthand
colors: true, // controls color optimizations
ieBangHack: false, // controls keeping IE bang hack
ieFilters: false, // controls keeping IE `filter` / `-ms-filter`
iePrefixHack: false, // controls keeping IE prefix hack
ieSuffixHack: false, // controls keeping IE suffix hack
merging: true, // controls property merging based on understandability
shorterLengthUnits: false, // controls shortening pixel units into `pc`, `pt`, or `in` units
spaceAfterClosingBrace: true, // controls keeping space after closing brace - `url() no-repeat` into `url()no-repeat`
urlQuotes: true, // controls keeping quoting inside `url()`
zeroUnits: true // controls removal of units `0` value
},
selectors: {
adjacentSpace: false, // controls extra space before `nav` element
ie7Hack: true, // controls removal of IE7 selector hacks, e.g. `*+html...`
mergeablePseudoClasses: [':active', ...], // controls a whitelist of mergeable pseudo classes
mergeablePseudoElements: ['::after', ...], // controls a whitelist of mergeable pseudo elements
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
in: true, // controls treating `in` as a supported unit
pc: true, // controls treating `pc` as a supported unit
pt: true, // controls treating `pt` as a supported unit
rem: true, // controls treating `rem` as a supported unit
vh: true, // controls treating `vh` as a supported unit
vm: true, // controls treating `vm` as a supported unit
vmax: true, // controls treating `vmax` as a supported unit
vmin: true // controls treating `vmin` as a supported unit
}
}
})
```
You can also use a string when setting a compatibility mode, e.g.
```js
new CleanCSS({
compatibility: 'ie9,-properties.merging' // sets compatibility to IE9 mode with disabled property merging
})
```
## 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/clean-css/clean-css/blob/master/lib/reader/load-remote-resource.js) logic is used.
## Formatting 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({
format: {
breaks: { // controls where to insert breaks
afterAtRule: false, // controls if a line break comes after an at-rule; e.g. `@charset`; defaults to `false`
afterBlockBegins: false, // controls if a line break comes after a block begins; e.g. `@media`; defaults to `false`
afterBlockEnds: false, // controls if a line break comes after a block ends, defaults to `false`
afterComment: false, // controls if a line break comes after a comment; defaults to `false`
afterProperty: false, // controls if a line break comes after a property; defaults to `false`
afterRuleBegins: false, // controls if a line break comes after a rule begins; defaults to `false`
afterRuleEnds: false, // controls if a line break comes after a rule ends; defaults to `false`
beforeBlockEnds: false, // controls if a line break comes before a block ends; defaults to `false`
betweenSelectors: false // controls if a line break comes between selectors; defaults to `false`
},
breakWith: '\n', // controls the new line character, can be `'\r\n'` or `'\n'` (aliased as `'windows'` and `'unix'` or `'crlf'` and `'lf'`); defaults to system one, so former on Windows and latter on Unix
indentBy: 0, // controls number of characters to indent with; defaults to `0`
indentWith: 'space', // controls a character to indent with, can be `'space'` or `'tab'`; defaults to `'space'`
spaces: { // controls where to insert spaces
aroundSelectorRelation: false, // controls if spaces come around selector relations; e.g. `div > a`; defaults to `false`
beforeBlockBegins: false, // controls if a space comes before a block begins; e.g. `.block {`; defaults to `false`
beforeValue: false // controls if a space comes before a value; e.g. `width: 1rem`; defaults to `false`
},
wrapAt: false, // controls maximum line length; defaults to `false`
semicolonAfterLastProperty: false // controls removing trailing semicolons in rule; defaults to `false` - means remove
}
})
```
Also since clean-css 5.0 you can use numerical values for all line breaks, which will repeat a line break that many times, e.g:
```js
new CleanCSS({
format: {
breaks: {
afterAtRule: 2,
afterBlockBegins: 1, // 1 is synonymous with `true`
afterBlockEnds: 2,
afterComment: 1,
afterProperty: 1,
afterRuleBegins: 1,
afterRuleEnds: 1,
beforeBlockEnds: 1,
betweenSelectors: 0 // 0 is synonymous with `false`
}
}
})
```
which will add nicer spacing between at rules and blocks.
## Inlining options
`inline` option whitelists which `@import` rules will be processed, e.g.
```js
new CleanCSS({
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'] // enables all inlining, same as ['local', 'remote']
})
```
```js
new CleanCSS({
inline: ['local', 'mydomain.example.com'] // enables local inlining plus given remote source
})
```
```js
new CleanCSS({
inline: ['local', 'remote', '!fonts.googleapis.com'] // enables all inlining but from given remote source
})
```
## Optimization levels
The `level` option can be either `0`, `1` (default), or `2`, e.g.
```js
new CleanCSS({
level: 2
})
```
or a fine-grained configuration given via a hash.
Please note that level 1 optimization options are generally safe while level 2 optimizations should be safe for most users.
### Level 0 optimizations
Level 0 optimizations simply means "no optimizations". Use it when you'd like to inline imports and / or rebase URLs but skip everything else.
### Level 1 optimizations
Level 1 optimizations (default) operate on single properties only, e.g. can remove units when not required, turn rgb colors to a shorter hex representation, remove comments, etc
Here is a full list of available options:
```js
new CleanCSS({
level: {
1: {
cleanupCharsets: true, // controls `@charset` moving to the front of a stylesheet; defaults to `true`
normalizeUrls: true, // controls URL normalization; defaults to `true`
optimizeBackground: true, // controls `background` property 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`
replaceMultipleZeros: true, // contols removing redundant zeros; defaults to `true`
replaceTimeUnits: true, // controls replacing time units with shorter values; defaults to `true`
replaceZeroUnits: true, // controls replacing zero values with units; defaults to `true`
roundingPrecision: false, // rounds pixel values to `N` decimal places; `false` disables rounding; defaults to `false`
selectorsSortingMethod: 'standard', // denotes selector sorting method; can be `'natural'` or `'standard'`, `'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`
tidySelectors: true, // controls selectors optimizing; defaults to `true`,
variableValueOptimizers: [] // controls value optimizers which are applied to variables
}
}
});
```
There is an `all` shortcut for toggling all options at the same time, e.g.
```js
new CleanCSS({
level: {
1: {
all: false, // set all values to `false`
tidySelectors: true // turns on optimizing selectors
}
}
});
```
### Level 2 optimizations
Level 2 optimizations operate at rules or multiple properties level, e.g. can remove duplicate rules, remove properties redefined further down a stylesheet, or restructure rules by moving them around.
Please note that if level 2 optimizations are turned on then, unless explicitely disabled, level 1 optimizations are applied as well.
Here is a full list of available options:
```js
new CleanCSS({
level: {
2: {
mergeAdjacentRules: true, // controls adjacent rules merging; defaults to true
mergeIntoShorthands: true, // controls merging properties into shorthands; defaults to true
mergeMedia: true, // controls `@media` merging; defaults to true
mergeNonAdjacentRules: true, // controls non-adjacent rule merging; defaults to true
mergeSemantically: false, // controls semantic merging; defaults to false
overrideProperties: true, // controls property overriding based on understandability; defaults to true
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
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)
}
}
});
```
There is an `all` shortcut for toggling all options at the same time, e.g.
```js
new CleanCSS({
level: {
2: {
all: false, // sets all values to `false`
removeDuplicateRules: true // turns on removing duplicate rules
}
}
});
```
## Plugins
In clean-css version 5 and above you can define plugins which run alongside level 1 and level 2 optimizations, e.g.
```js
var myPlugin = {
level1: {
property: function removeRepeatedBackgroundRepeat(_rule, property, _options) {
// So `background-repeat:no-repeat no-repeat` becomes `background-repeat:no-repeat`
if (property.name == 'background-repeat' && property.value.length == 2 && property.value[0][1] == property.value[1][1]) {
property.value.pop();
property.dirty = true;
}
}
}
}
new CleanCSS({plugins: [myPlugin]})
```
Search `test\module-test.js` for `plugins` or check out `lib/optimizer/level-1/property-optimizers` and `lib/optimizer/level-1/value-optimizers` for more examples.
__Important__: To rewrite your old `transform` as a plugin, check out [this commit](https://github.com/clean-css/clean-css/commit/b6ddc523267fc42cf0f6bd1626a79cad97319e17#diff-a71ef45f934725cdb25860dc0b606bcd59e3acee9788cd6df4f9d05339e8a153).
## Minify method
Once configured clean-css provides a `minify` method to optimize a given CSS, e.g.
```js
var output = new CleanCSS(options).minify(source);
```
The output of the `minify` method is a hash with following fields:
```js
console.log(output.styles); // optimized output CSS as a string
console.log(output.sourceMap); // output source map if requested with `sourceMap` option
console.log(output.errors); // a list of errors raised
console.log(output.warnings); // a list of warnings raised
console.log(output.stats.originalSize); // original content size after import inlining
console.log(output.stats.minifiedSize); // optimized content size
console.log(output.stats.timeSpent); // time spent on optimizations in milliseconds
console.log(output.stats.efficiency); // `(originalSize - minifiedSize) / originalSize`, e.g. 0.25 if size is reduced from 100 bytes to 75 bytes
```
Example: Minifying a CSS string:
```js
const CleanCSS = require("clean-css");
const output = new CleanCSS().minify(`
a {
color: blue;
}
div {
margin: 5px
}
`);
console.log(output);
// Log:
{
styles: 'a{color:#00f}div{margin:5px}',
stats: {
efficiency: 0.6704545454545454,
minifiedSize: 29,
originalSize: 88,
timeSpent: 6
},
errors: [],
inlinedStylesheets: [],
warnings: []
}
```
The `minify` method also accepts an input source map, e.g.
```js
var output = new CleanCSS(options).minify(source, inputSourceMap);
```
or a callback invoked when optimizations are finished, e.g.
```js
new CleanCSS(options).minify(source, function (error, output) {
// `output` is the same as in the synchronous call above
});
```
To optimize a single file, without reading it first, pass a path to it to `minify` method as follows:
```js
var output = new CleanCSS(options).minify(['path/to/file.css'])
```
(if you won't enclose the path in an array, it will be treated as a CSS source instead).
There are several ways to optimize multiple files at the same time, see [How to optimize multiple files?](#how-to-optimize-multiple-files).
## Promise interface
If you prefer clean-css to return a Promise object then you need to explicitely ask for it, e.g.
```js
new CleanCSS({ returnPromise: true })
.minify(source)
.then(function (output) { console.log(output.styles); })
.catch(function (error) { // deal with errors });
```
## 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/clean-css/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 or an array of hashes:
```js
new CleanCSS().minify(['path/to/file/one', 'path/to/file/two']);
```
```js
new CleanCSS().minify({
'path/to/file/one': {
styles: 'contents of file one'
},
'path/to/file/two': {
styles: 'contents of file two'
}
});
```
```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 multiple files without concatenating them into one output file?
Since clean-css 5.0 you can, when passing an array of paths, hash, or array of hashes (see above), ask clean-css not to join styles into one output, but instead return stylesheets optimized one by one, e.g.
```js
var output = new CleanCSS({ batch: true }).minify(['path/to/file/one', 'path/to/file/two']);
var outputOfFile1 = output['path/to/file/one'].styles // all other fields, like errors, warnings, or stats are there too
var outputOfFile2 = output['path/to/file/two'].styles
```
## How to process remote `@import`s correctly?
In order to inline remote `@import` statements you need to provide a callback to minify method as fetching remote assets is an asynchronous operation, e.g.:
```js
var source = '@import url(http://example.com/path/to/remote/styles);';
new CleanCSS({ inline: ['remote'] }).minify(source, function (error, output) {
// output.styles
});
```
If you don't provide a callback, then remote `@import`s will be left as is.
## How to apply arbitrary transformations to CSS properties?
Please see [plugins](#plugins).
## How to specify a custom rounding precision?
The level 1 `roundingPrecision` optimization option accept a string with per-unit rounding precision settings, e.g.
```js
new CleanCSS({
level: {
1: {
roundingPrecision: 'all=3,px=5'
}
}
}).minify(source)
```
which sets all units rounding precision to 3 digits except `px` unit precision of 5 digits.
## How to optimize a stylesheet with custom `rpx` units?
Since `rpx` is a non standard unit (see [#1074](https://github.com/clean-css/clean-css/issues/1074)), it will be dropped by default as an invalid value.
However you can treat `rpx` units as regular ones:
```js
new CleanCSS({
compatibility: {
customUnits: {
rpx: true
}
}
}).minify(source)
```
## How to keep a CSS fragment intact?
Note: available since 4.2.0.
Wrap the CSS fragment in special comments which instruct clean-css to preserve it, e.g.
```css
.block-1 {
color: red
}
/* clean-css ignore:start */
.block-special {
color: transparent
}
/* clean-css ignore:end */
.block-2 {
margin: 0
}
```
Optimizing this CSS will result in the following output:
```css
.block-1{color:red}
.block-special {
color: transparent
}
.block-2{margin:0}
```
## How to preserve a comment block?
Use the `/*!` notation instead of the standard one `/*`:
```css
/*!
Important comments included in optimized output.
*/
```
## How to rebase relative image URLs?
clean-css will handle it automatically for you in the following cases:
* when full paths to input files are passed in as options;
* when correct paths are passed in via a hash;
* when `rebaseTo` is used with any of above two.
## How to work with source maps?
To generate a source map, use `sourceMap: true` option, e.g.:
```js
new CleanCSS({ sourceMap: true, rebaseTo: pathToOutputDirectory })
.minify(source, function (error, output) {
// access output.sourceMap for SourceMapGenerator object
// see https://github.com/mozilla/source-map/#sourcemapgenerator for more details
});
```
You can also pass an input source map directly as a 2nd argument to `minify` method:
```js
new CleanCSS({ sourceMap: true, rebaseTo: pathToOutputDirectory })
.minify(source, inputSourceMap, function (error, output) {
// access output.sourceMap to access SourceMapGenerator object
// see https://github.com/mozilla/source-map/#sourcemapgenerator for more details
});
```
or even multiple input source maps at once:
```js
new CleanCSS({ sourceMap: true, rebaseTo: pathToOutputDirectory }).minify({
'path/to/source/1': {
styles: '...styles...',
sourceMap: '...source-map...'
},
'path/to/source/2': {
styles: '...styles...',
sourceMap: '...source-map...'
}
}, function (error, output) {
// access output.sourceMap as above
});
```
## How to apply level 1 & 2 optimizations at the same time?
Using the hash configuration specifying both optimization levels, e.g.
```js
new CleanCSS({
level: {
1: {
all: true,
normalizeUrls: false
},
2: {
restructureRules: true
}
}
})
```
will apply level 1 optimizations, except url normalization, and default level 2 optimizations with rule restructuring.
## What level 2 optimizations do?
All level 2 optimizations are dispatched [here](https://github.com/clean-css/clean-css/blob/master/lib/optimizer/level-2/optimize.js#L67), and this is what they do:
* `recursivelyOptimizeBlocks` - does all the following operations on a nested block, like `@media` or `@keyframe`;
* `recursivelyOptimizeProperties` - optimizes properties in rulesets and flat at-rules, like @font-face, by splitting them into components (e.g. `margin` into `margin-(bottom|left|right|top)`), optimizing, and restoring them back. You may want to use `mergeIntoShorthands` option to control whether you want to turn multiple components into shorthands;
* `removeDuplicates` - gets rid of duplicate rulesets with exactly the same set of properties, e.g. when including a Sass / Less partial twice for no good reason;
* `mergeAdjacent` - merges adjacent rulesets with the same selector or rules;
* `reduceNonAdjacent` - identifies which properties are overridden in same-selector non-adjacent rulesets, and removes them;
* `mergeNonAdjacentBySelector` - identifies same-selector non-adjacent rulesets which can be moved (!) to be merged, requires all intermediate rulesets to not redefine the moved properties, or if redefined to have the same value;
* `mergeNonAdjacentByBody` - same as the one above but for same-selector non-adjacent rulesets;
* `restructure` - tries to reorganize different-selector different-rules rulesets so they take less space, e.g. `.one{padding:0}.two{margin:0}.one{margin-bottom:3px}` into `.two{margin:0}.one{padding:0;margin-bottom:3px}`;
* `removeDuplicateFontAtRules` - removes duplicated `@font-face` rules;
* `removeDuplicateMediaQueries` - removes duplicated `@media` nested blocks;
* `mergeMediaQueries` - merges non-adjacent `@media` at-rules by the same rules as `mergeNonAdjacentBy*` above;
## What errors and warnings are?
If clean-css encounters invalid CSS, it will try to remove the invalid part and continue optimizing the rest of the code. It will make you aware of the problem by generating an error or warning. Although clean-css can work with invalid CSS, it is always recommended that you fix warnings and errors in your CSS.
Example: Minify invalid CSS, resulting in two warnings:
```js
const CleanCSS = require("clean-css");
const output = new CleanCSS().minify(`
a {
-notarealproperty-: 5px;
color:
}
div {
margin: 5px
}
`);
console.log(output);
// Log:
{
styles: 'div{margin:5px}',
stats: {
efficiency: 0.8695652173913043,
minifiedSize: 15,
originalSize: 115,
timeSpent: 1
},
errors: [],
inlinedStylesheets: [],
warnings: [
"Invalid property name '-notarealproperty-' at 4:8. Ignoring.",
"Empty property 'color' at 5:8. Ignoring."
]
}
```
Example: Minify invalid CSS, resulting in one error:
```js
const CleanCSS = require("clean-css");
const output = new CleanCSS().minify(`
@import "idontexist.css";
a {
color: blue;
}
div {
margin: 5px
}
`);
console.log(output);
// Log:
{
styles: 'a{color:#00f}div{margin:5px}',
stats: {
efficiency: 0.7627118644067796,
minifiedSize: 28,
originalSize: 118,
timeSpent: 2
},
errors: [
'Ignoring local @import of "idontexist.css" as resource is missing.'
],
inlinedStylesheets: [],
warnings: []
}
```
## Clean-css for Gulp
An example of how you can include clean-css in gulp
```js
const { src, dest, series } = require('gulp');
const CleanCSS = require('clean-css');
const concat = require('gulp-concat');
function css() {
const options = {
compatibility: '*', // (default) - Internet Explorer 10+ compatibility mode
inline: ['all'], // enables all inlining, same as ['local', 'remote']
level: 2 // Optimization levels. The level option can be either 0, 1 (default), or 2, e.g.
// Please note that level 1 optimization options are generally safe while level 2 optimizations should be safe for most users.
};
return src('app/**/*.css')
.pipe(concat('style.min.css'))
.on('data', function(file) {
const buferFile = new CleanCSS(options).minify(file.contents)
return file.contents = Buffer.from(buferFile.styles)
})
.pipe(dest('build'))
}
exports.css = series(css)
```
## How to use clean-css with build tools?
There is a number of 3rd party plugins to popular build tools:
* [Broccoli](https://github.com/broccolijs/broccoli#broccoli): [broccoli-clean-css](https://github.com/shinnn/broccoli-clean-css)
* [Brunch](http://brunch.io/): [clean-css-brunch](https://github.com/brunch/clean-css-brunch)
* [Grunt](http://gruntjs.com): [grunt-contrib-cssmin](https://github.com/gruntjs/grunt-contrib-cssmin)
* [Gulp](http://gulpjs.com/): [gulp-clean-css](https://github.com/scniro/gulp-clean-css)
* [Gulp](http://gulpjs.com/): [using vinyl-map as a wrapper - courtesy of @sogko](https://github.com/clean-css/clean-css/issues/342)
* [component-builder2](https://github.com/component/builder2.js): [builder-clean-css](https://github.com/poying/builder-clean-css)
* [Metalsmith](http://metalsmith.io): [metalsmith-clean-css](https://github.com/aymericbeaumet/metalsmith-clean-css)
* [Lasso](https://github.com/lasso-js/lasso): [lasso-clean-css](https://github.com/yomed/lasso-clean-css)
* [Start](https://github.com/start-runner/start): [start-clean-css](https://github.com/start-runner/clean-css)
## How to use clean-css from web browser?
* https://clean-css.github.io/ (official web interface)
* http://refresh-sf.com/
* http://adamburgess.github.io/clean-css-online/
# Contributing
See [CONTRIBUTING.md](https://github.com/clean-css/clean-css/blob/master/CONTRIBUTING.md).
## How to get started?
First clone the sources:
```bash
git clone git@github.com:clean-css/clean-css.git
```
then install dependencies:
```bash
cd clean-css
npm install
```
then use any of the following commands to verify your copy:
```bash
npm run bench # for clean-css benchmarks (see [test/bench.js](https://github.com/clean-css/clean-css/blob/master/test/bench.js) for details)
npm run browserify # to create the browser-ready clean-css version
npm run check # to lint JS sources with [JSHint](https://github.com/jshint/jshint/)
npm test # to run all tests
```
# Acknowledgments
Sorted alphabetically by GitHub handle:
* [@abarre](https://github.com/abarre) (Anthony Barre) for improvements to `@import` processing;
* [@alexlamsl](https://github.com/alexlamsl) (Alex Lam S.L.) for testing early clean-css 4 versions, reporting bugs, and suggesting numerous improvements.
* [@altschuler](https://github.com/altschuler) (Simon Altschuler) for fixing `@import` processing inside comments;
* [@ben-eb](https://github.com/ben-eb) (Ben Briggs) for sharing ideas about CSS optimizations;
* [@davisjam](https://github.com/davisjam) (Jamie Davis) for disclosing ReDOS vulnerabilities;
* [@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/clean-css/clean-css/issues/125) for pushing it forward;
* [@madwizard-thomas](https://github.com/madwizard-thomas) for sharing ideas about `@import` inlining and URL rebasing.
* [@ngyikp](https://github.com/ngyikp) (Ng Yik Phang) for testing early clean-css 4 versions, reporting bugs, and suggesting numerous improvements.
* [@wagenet](https://github.com/wagenet) (Peter Wagenet) for suggesting improvements to `@import` inlining behavior;
* [@venemo](https://github.com/venemo) (Timur Kristóf) for an outstanding contribution of advanced property optimizer for 2.2 release;
* [@vvo](https://github.com/vvo) (Vincent Voyer) for a patch with better empty element regex and for inspiring us to do many performance improvements in 0.4 release;
* [@xhmikosr](https://github.com/xhmikosr) for suggesting new features, like option to remove special comments and strip out URLs quotation, and pointing out numerous improvements like JSHint, media queries, etc.
# License
clean-css is released under the [MIT License](https://github.com/clean-css/clean-css/blob/master/LICENSE).

1
node_modules/clean-css/index.js generated vendored Normal file
View File

@ -0,0 +1 @@
module.exports = require('./lib/clean');

241
node_modules/clean-css/lib/clean.js generated vendored Normal file
View File

@ -0,0 +1,241 @@
/**
* Clean-css - https://github.com/clean-css/clean-css
* Released under the terms of MIT license
*/
var level0Optimize = require('./optimizer/level-0/optimize');
var level1Optimize = require('./optimizer/level-1/optimize');
var level2Optimize = require('./optimizer/level-2/optimize');
var validator = require('./optimizer/validator');
var compatibilityFrom = require('./options/compatibility');
var fetchFrom = require('./options/fetch');
var formatFrom = require('./options/format').formatFrom;
var inlineFrom = require('./options/inline');
var inlineRequestFrom = require('./options/inline-request');
var inlineTimeoutFrom = require('./options/inline-timeout');
var OptimizationLevel = require('./options/optimization-level').OptimizationLevel;
var optimizationLevelFrom = require('./options/optimization-level').optimizationLevelFrom;
var pluginsFrom = require('./options/plugins');
var rebaseFrom = require('./options/rebase');
var rebaseToFrom = require('./options/rebase-to');
var inputSourceMapTracker = require('./reader/input-source-map-tracker');
var readSources = require('./reader/read-sources');
var serializeStyles = require('./writer/simple');
var serializeStylesAndSourceMap = require('./writer/source-maps');
var CleanCSS = module.exports = function CleanCSS(options) {
options = options || {};
this.options = {
batch: !!options.batch,
compatibility: compatibilityFrom(options.compatibility),
explicitRebaseTo: 'rebaseTo' in options,
fetch: fetchFrom(options.fetch),
format: formatFrom(options.format),
inline: inlineFrom(options.inline),
inlineRequest: inlineRequestFrom(options.inlineRequest),
inlineTimeout: inlineTimeoutFrom(options.inlineTimeout),
level: optimizationLevelFrom(options.level),
plugins: pluginsFrom(options.plugins),
rebase: rebaseFrom(options.rebase, options.rebaseTo),
rebaseTo: rebaseToFrom(options.rebaseTo),
returnPromise: !!options.returnPromise,
sourceMap: !!options.sourceMap,
sourceMapInlineSources: !!options.sourceMapInlineSources
};
};
// for compatibility with optimize-css-assets-webpack-plugin
CleanCSS.process = function(input, opts) {
var cleanCss;
var optsTo = opts.to;
delete opts.to;
cleanCss = new CleanCSS(Object.assign({
returnPromise: true, rebaseTo: optsTo
}, opts));
return cleanCss.minify(input)
.then(function(output) {
return { css: output.styles };
});
};
CleanCSS.prototype.minify = function(input, maybeSourceMap, maybeCallback) {
var options = this.options;
if (options.returnPromise) {
return new Promise(function(resolve, reject) {
minifyAll(input, options, maybeSourceMap, function(errors, output) {
return errors
? reject(errors)
: resolve(output);
});
});
}
return minifyAll(input, options, maybeSourceMap, maybeCallback);
};
function minifyAll(input, options, maybeSourceMap, maybeCallback) {
if (options.batch && Array.isArray(input)) {
return minifyInBatchesFromArray(input, options, maybeSourceMap, maybeCallback);
} if (options.batch && (typeof input == 'object')) {
return minifyInBatchesFromHash(input, options, maybeSourceMap, maybeCallback);
}
return minify(input, options, maybeSourceMap, maybeCallback);
}
function minifyInBatchesFromArray(input, options, maybeSourceMap, maybeCallback) {
var callback = typeof maybeCallback == 'function'
? maybeCallback
: (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
var errors = [];
var outputAsHash = {};
var inputValue;
var i, l;
function whenHashBatchDone(innerErrors, output) {
outputAsHash = Object.assign(outputAsHash, output);
if (innerErrors !== null) {
errors = errors.concat(innerErrors);
}
}
for (i = 0, l = input.length; i < l; i++) {
if (typeof input[i] == 'object') {
minifyInBatchesFromHash(input[i], options, whenHashBatchDone);
} else {
inputValue = input[i];
outputAsHash[inputValue] = minify([inputValue], options);
errors = errors.concat(outputAsHash[inputValue].errors);
}
}
return callback
? callback(errors.length > 0 ? errors : null, outputAsHash)
: outputAsHash;
}
function minifyInBatchesFromHash(input, options, maybeSourceMap, maybeCallback) {
var callback = typeof maybeCallback == 'function'
? maybeCallback
: (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
var errors = [];
var outputAsHash = {};
var inputKey;
var inputValue;
for (inputKey in input) {
inputValue = input[inputKey];
outputAsHash[inputKey] = minify(inputValue.styles, options, inputValue.sourceMap);
errors = errors.concat(outputAsHash[inputKey].errors);
}
return callback
? callback(errors.length > 0 ? errors : null, outputAsHash)
: outputAsHash;
}
function minify(input, options, maybeSourceMap, maybeCallback) {
var sourceMap = typeof maybeSourceMap != 'function'
? maybeSourceMap
: null;
var callback = typeof maybeCallback == 'function'
? maybeCallback
: (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
var context = {
stats: {
efficiency: 0,
minifiedSize: 0,
originalSize: 0,
startedAt: Date.now(),
timeSpent: 0
},
cache: { specificity: {} },
errors: [],
inlinedStylesheets: [],
inputSourceMapTracker: inputSourceMapTracker(),
localOnly: !callback,
options: options,
source: null,
sourcesContent: {},
validator: validator(options.compatibility),
warnings: []
};
var implicitRebaseToWarning;
if (sourceMap) {
context.inputSourceMapTracker.track(undefined, sourceMap);
}
if (options.rebase && !options.explicitRebaseTo) {
implicitRebaseToWarning = 'You have set `rebase: true` without giving `rebaseTo` option, which, in this case, defaults to the current working directory. '
+ 'You are then warned this can lead to unexpected URL rebasing (aka here be dragons)! '
+ 'If you are OK with the clean-css output, then you can get rid of this warning by giving clean-css a `rebaseTo: process.cwd()` option.';
context.warnings.push(implicitRebaseToWarning);
}
return runner(context.localOnly)(function() {
return readSources(input, context, function(tokens) {
var serialize = context.options.sourceMap
? serializeStylesAndSourceMap
: serializeStyles;
var optimizedTokens = optimize(tokens, context);
var optimizedStyles = serialize(optimizedTokens, context);
var output = withMetadata(optimizedStyles, context);
return callback
? callback(context.errors.length > 0 ? context.errors : null, output)
: output;
});
});
}
function runner(localOnly) {
// to always execute code asynchronously when a callback is given
// more at blog.izs.me/post/59142742143/designing-apis-for-asynchrony
return localOnly
? function(callback) { return callback(); }
: process.nextTick;
}
function optimize(tokens, context) {
var optimized = level0Optimize(tokens, context);
optimized = OptimizationLevel.One in context.options.level
? level1Optimize(tokens, context)
: tokens;
optimized = OptimizationLevel.Two in context.options.level
? level2Optimize(tokens, context, true)
: optimized;
return optimized;
}
function withMetadata(output, context) {
output.stats = calculateStatsFrom(output.styles, context);
output.errors = context.errors;
output.inlinedStylesheets = context.inlinedStylesheets;
output.warnings = context.warnings;
return output;
}
function calculateStatsFrom(styles, context) {
var finishedAt = Date.now();
var timeSpent = finishedAt - context.stats.startedAt;
delete context.stats.startedAt;
context.stats.timeSpent = timeSpent;
context.stats.efficiency = 1 - styles.length / context.stats.originalSize;
context.stats.minifiedSize = styles.length;
return context.stats;
}

33
node_modules/clean-css/lib/optimizer/clone.js generated vendored Normal file
View File

@ -0,0 +1,33 @@
var wrapSingle = require('./wrap-for-optimizing').single;
var Token = require('../tokenizer/token');
function deep(property) {
var cloned = shallow(property);
for (var i = property.components.length - 1; i >= 0; i--) {
var component = shallow(property.components[i]);
component.value = property.components[i].value.slice(0);
cloned.components.unshift(component);
}
cloned.dirty = true;
cloned.value = property.value.slice(0);
return cloned;
}
function shallow(property) {
var cloned = wrapSingle([
Token.PROPERTY,
[Token.PROPERTY_NAME, property.name]
]);
cloned.important = property.important;
cloned.hack = property.hack;
cloned.unused = false;
return cloned;
}
module.exports = {
deep: deep,
shallow: shallow
};

1640
node_modules/clean-css/lib/optimizer/configuration.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,656 @@
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');
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.isColor(value[1]) || validator.isPrefixed(value[1]);
};
}
function _styleFilter(validator) {
return function(value) {
return value[1] != 'inherit' && validator.isStyleKeyword(value[1]) && !validator.isColorFunction(value[1]);
};
}
function _wrapDefault(name, property, configuration) {
var descriptor = configuration[name];
if (descriptor.doubleValues && descriptor.defaultValue.length == 2) {
return wrapSingle([
Token.PROPERTY,
[Token.PROPERTY_NAME, name],
[Token.PROPERTY_VALUE, descriptor.defaultValue[0]],
[Token.PROPERTY_VALUE, descriptor.defaultValue[1]]
]);
} if (descriptor.doubleValues && descriptor.defaultValue.length == 1) {
return wrapSingle([
Token.PROPERTY,
[Token.PROPERTY_NAME, name],
[Token.PROPERTY_VALUE, descriptor.defaultValue[0]]
]);
}
return wrapSingle([
Token.PROPERTY,
[Token.PROPERTY_NAME, name],
[Token.PROPERTY_VALUE, descriptor.defaultValue]
]);
}
function _widthFilter(validator) {
return function(value) {
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, configuration, validator) {
var duration = _wrapDefault(property.name + '-duration', property, configuration);
var timing = _wrapDefault(property.name + '-timing-function', property, configuration);
var delay = _wrapDefault(property.name + '-delay', property, configuration);
var iteration = _wrapDefault(property.name + '-iteration-count', property, configuration);
var direction = _wrapDefault(property.name + '-direction', property, configuration);
var fill = _wrapDefault(property.name + '-fill-mode', property, configuration);
var play = _wrapDefault(property.name + '-play-state', property, configuration);
var name = _wrapDefault(property.name + '-name', property, configuration);
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') {
// eslint-disable-next-line max-len
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.isTimingFunction(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, configuration, validator) {
var image = _wrapDefault('background-image', property, configuration);
var position = _wrapDefault('background-position', property, configuration);
var size = _wrapDefault('background-size', property, configuration);
var repeat = _wrapDefault('background-repeat', property, configuration);
var attachment = _wrapDefault('background-attachment', property, configuration);
var origin = _wrapDefault('background-origin', property, configuration);
var clip = _wrapDefault('background-clip', property, configuration);
var color = _wrapDefault('background-color', property, configuration);
var components = [image, position, size, repeat, attachment, origin, clip, color];
var values = property.value;
var positionSet = false;
var clipSet = false;
var originSet = false;
var repeatSet = false;
var anyValueSet = false;
if (property.value.length == 1 && property.value[0][1] == 'inherit') {
// NOTE: 'inherit' is not a valid value for background-attachment
color.value = image.value = repeat.value = position.value = size.value = origin.value = clip.value = property.value;
return components;
}
if (property.value.length == 1 && property.value[0][1] == '0 0') {
return components;
}
for (var i = values.length - 1; i >= 0; i--) {
var value = values[i];
if (validator.isBackgroundAttachmentKeyword(value[1])) {
attachment.value = [value];
anyValueSet = true;
} else if (validator.isBackgroundClipKeyword(value[1]) || validator.isBackgroundOriginKeyword(value[1])) {
if (clipSet) {
origin.value = [value];
originSet = true;
} else {
clip.value = [value];
clipSet = true;
}
anyValueSet = true;
} else if (validator.isBackgroundRepeatKeyword(value[1])) {
if (repeatSet) {
repeat.value.unshift(value);
} else {
repeat.value = [value];
repeatSet = true;
}
anyValueSet = true;
} 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] == Marker.FORWARD_SLASH) {
size.value = [value];
} else if (i > 1 && values[i - 2][1] == Marker.FORWARD_SLASH) {
size.value = [previousValue, value];
i -= 2;
} else {
if (!positionSet) { position.value = []; }
position.value.unshift(value);
positionSet = true;
}
} else {
if (!positionSet) { position.value = []; }
position.value.unshift(value);
positionSet = true;
}
anyValueSet = true;
} else if ((color.value[0][1] == configuration[color.name].defaultValue || color.value[0][1] == 'none') && (validator.isColor(value[1]) || validator.isPrefixed(value[1]))) {
color.value = [value];
anyValueSet = true;
} else if (validator.isUrl(value[1]) || validator.isFunction(value[1])) {
image.value = [value];
anyValueSet = true;
}
}
if (clipSet && !originSet) { origin.value = clip.value.slice(0); }
if (!anyValueSet) {
throw new InvalidPropertyError('Invalid background value at ' + formatPosition(values[0][2][0]) + '. Ignoring.');
}
return components;
}
function borderRadius(property, configuration) {
var values = property.value;
var splitAt = -1;
for (var i = 0, l = values.length; i < l; i++) {
if (values[i][1] == Marker.FORWARD_SLASH) {
splitAt = i;
break;
}
}
if (splitAt === 0 || splitAt === values.length - 1) {
throw new InvalidPropertyError('Invalid border-radius value at ' + formatPosition(values[0][2][0]) + '. Ignoring.');
}
var target = _wrapDefault(property.name, property, configuration);
target.value = splitAt > -1
? values.slice(0, splitAt)
: values.slice(0);
target.components = fourValues(target, configuration);
var remainder = _wrapDefault(property.name, property, configuration);
remainder.value = splitAt > -1
? values.slice(splitAt + 1)
: values.slice(0);
remainder.components = fourValues(remainder, configuration);
for (var j = 0; j < 4; j++) {
target.components[j].multiplex = true;
target.components[j].value = target.components[j].value.concat(remainder.components[j].value);
}
return target.components;
}
function font(property, configuration, validator) {
var style = _wrapDefault('font-style', property, configuration);
var variant = _wrapDefault('font-variant', property, configuration);
var weight = _wrapDefault('font-weight', property, configuration);
var stretch = _wrapDefault('font-stretch', property, configuration);
var size = _wrapDefault('font-size', property, configuration);
var height = _wrapDefault('line-height', property, configuration);
var family = _wrapDefault('font-family', property, configuration);
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 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 < 2 || !_anyIsFontSize(values, validator) || !_anyIsFontFamily(values, validator)) {
throw new InvalidPropertyError('Invalid font values at ' + formatPosition(property.all[property.position][1][2][0]) + '. Ignoring.');
}
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]];
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 (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 _anyIsFontSize(values, validator) {
var value;
var i, l;
for (i = 0, l = values.length; i < l; i++) {
value = values[i];
if (validator.isFontSizeKeyword(value[1])
|| validator.isUnit(value[1])
&& !validator.isDynamicUnit(value[1])
|| validator.isFunction(value[1])) {
return true;
}
}
return false;
}
function _anyIsFontFamily(values, validator) {
var value;
var i, l;
for (i = 0, l = values.length; i < l; i++) {
value = values[i];
if (validator.isIdentifier(value[1]) || validator.isQuotedText(value[1])) {
return true;
}
}
return false;
}
function fourValues(property, configuration) {
var componentNames = configuration[property.name].components;
var components = [];
var value = property.value;
if (value.length < 1) { return []; }
if (value.length < 2) { value[1] = value[0].slice(0); }
if (value.length < 3) { value[2] = value[0].slice(0); }
if (value.length < 4) { value[3] = value[1].slice(0); }
for (var i = componentNames.length - 1; i >= 0; i--) {
var component = wrapSingle([
Token.PROPERTY,
[Token.PROPERTY_NAME, componentNames[i]]
]);
component.value = [value[i]];
components.unshift(component);
}
return components;
}
function multiplex(splitWith) {
return function(property, configuration, validator) {
var splitsAt = [];
var values = property.value;
var i, j, l, m;
// find split commas
for (i = 0, l = values.length; i < l; i++) {
if (values[i][1] == ',') { splitsAt.push(i); }
}
if (splitsAt.length === 0) { return splitWith(property, configuration, validator); }
var splitComponents = [];
// split over commas, and into components
for (i = 0, l = splitsAt.length; i <= l; i++) {
var from = i === 0 ? 0 : splitsAt[i - 1] + 1;
var to = i < l ? splitsAt[i] : values.length;
var _property = _wrapDefault(property.name, property, configuration);
_property.value = values.slice(from, to);
if (_property.value.length > 0) {
splitComponents.push(splitWith(_property, configuration, validator));
}
}
var components = splitComponents[0];
// group component values from each split
for (i = 0, l = components.length; i < l; i++) {
components[i].multiplex = true;
for (j = 1, m = splitComponents.length; j < m; j++) {
components[i].value.push([Token.PROPERTY_VALUE, Marker.COMMA]);
Array.prototype.push.apply(components[i].value, splitComponents[j][i].value);
}
}
return components;
};
}
function listStyle(property, configuration, validator) {
var type = _wrapDefault('list-style-type', property, configuration);
var position = _wrapDefault('list-style-position', property, configuration);
var image = _wrapDefault('list-style-image', property, configuration);
var components = [type, position, image];
if (property.value.length == 1 && property.value[0][1] == 'inherit') {
type.value = position.value = image.value = [property.value[0]];
return components;
}
var values = property.value.slice(0);
var total = values.length;
var index = 0;
// `image` first...
for (index = 0, total = values.length; index < total; index++) {
if (validator.isUrl(values[index][1]) || values[index][1] == '0') {
image.value = [values[index]];
values.splice(index, 1);
break;
}
}
// ... then `position`
for (index = 0, total = values.length; index < total; index++) {
if (validator.isListStylePositionKeyword(values[index][1])) {
position.value = [values[index]];
values.splice(index, 1);
break;
}
}
// ... 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;
}
function transition(property, configuration, validator) {
var prop = _wrapDefault(property.name + '-property', property, configuration);
var duration = _wrapDefault(property.name + '-duration', property, configuration);
var timing = _wrapDefault(property.name + '-timing-function', property, configuration);
var delay = _wrapDefault(property.name + '-delay', property, configuration);
var components = [prop, duration, timing, delay];
var values = property.value;
var value;
var durationSet = false;
var delaySet = false;
var propSet = false;
var timingSet = false;
var i;
var l;
if (property.value.length == 1 && property.value[0][1] == 'inherit') {
prop.value = duration.value = timing.value = delay.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.isTimingFunction(value[1])) && !timingSet) {
timing.value = [value];
timingSet = true;
} else if (validator.isIdentifier(value[1]) && !propSet) {
prop.value = [value];
propSet = true;
} else {
throw new InvalidPropertyError('Invalid animation value at ' + formatPosition(value[2][0]) + '. Ignoring.');
}
}
return components;
}
function widthStyleColor(property, configuration, validator) {
var descriptor = configuration[property.name];
var components = [
_wrapDefault(descriptor.components[0], property, configuration),
_wrapDefault(descriptor.components[1], property, configuration),
_wrapDefault(descriptor.components[2], property, configuration)
];
var color, style, width;
for (var i = 0; i < 3; i++) {
var component = components[i];
if (component.name.indexOf('color') > 0) { color = component; } else if (component.name.indexOf('style') > 0) { style = component; } else { width = component; }
}
if ((property.value.length == 1 && property.value[0][1] == 'inherit')
|| (property.value.length == 3 && property.value[0][1] == 'inherit' && property.value[1][1] == 'inherit' && property.value[2][1] == 'inherit')) {
color.value = style.value = width.value = [property.value[0]];
return components;
}
var values = property.value.slice(0);
var match, matches;
// NOTE: usually users don't follow the required order of parts in this shorthand,
// so we'll try to parse it caring as little about order as possible
if (values.length > 0) {
matches = values.filter(_widthFilter(validator));
match = matches.length > 1 && (matches[0][1] == 'none' || matches[0][1] == 'auto') ? matches[1] : matches[0];
if (match) {
width.value = [match];
values.splice(values.indexOf(match), 1);
}
}
if (values.length > 0) {
match = values.filter(_styleFilter(validator))[0];
if (match) {
style.value = [match];
values.splice(values.indexOf(match), 1);
}
}
if (values.length > 0) {
match = values.filter(_colorFilter(validator))[0];
if (match) {
color.value = [match];
values.splice(values.indexOf(match), 1);
}
}
return components;
}
module.exports = {
animation: animation,
background: background,
border: widthStyleColor,
borderRadius: borderRadius,
font: font,
fourValues: fourValues,
listStyle: listStyle,
multiplex: multiplex,
outline: widthStyleColor,
transition: transition
};

View File

@ -0,0 +1,314 @@
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;
} 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;
} if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
}
return validator.isAnimationNameKeyword(value2) || validator.isIdentifier(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('('));
var function1Value = value1.substring(function1Name.length + 1, value1.length - 1);
var function2Value = value2.substring(function2Name.length + 1, value2.length - 1);
if (validator.isFunction(function1Value) || validator.isFunction(function2Value)) {
return function1Name === function2Name && areSameFunction(validator, function1Value, function2Value);
}
return function1Name === function2Name;
}
function backgroundPosition(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true)
&& !(validator.isBackgroundPositionKeyword(value2) || validator.isGlobal(value2))) {
return false;
} if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
} if (validator.isBackgroundPositionKeyword(value2) || validator.isGlobal(value2)) {
return true;
}
return unit(validator, value1, value2);
}
function backgroundSize(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true)
&& !(validator.isBackgroundSizeKeyword(value2) || validator.isGlobal(value2))) {
return false;
} if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
} if (validator.isBackgroundSizeKeyword(value2) || validator.isGlobal(value2)) {
return true;
}
return unit(validator, value1, value2);
}
function color(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !validator.isColor(value2)) {
return false;
} if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
} if (!validator.colorOpacity && (validator.isRgbColor(value1) || validator.isHslColor(value1))) {
return false;
} if (!validator.colorOpacity && (validator.isRgbColor(value2) || validator.isHslColor(value2))) {
return false;
} if (!validator.colorHexAlpha && (validator.isHexAlphaColor(value1) || validator.isHexAlphaColor(value2))) {
return false;
} if (validator.isColor(value1) && validator.isColor(value2)) {
return true;
}
return sameFunctionOrValue(validator, value1, value2);
}
function components(overrideCheckers) {
return function(validator, value1, value2, position) {
return overrideCheckers[position](validator, value1, value2);
};
}
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.isImage(value2)) {
return false;
} if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
} if (validator.isImage(value2)) {
return true;
} if (validator.isImage(value1)) {
return false;
}
return sameFunctionOrValue(validator, value1, value2);
}
function keyword(propertyName) {
return function(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !validator.isKeyword(propertyName)(value2)) {
return false;
} if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
}
return validator.isKeyword(propertyName)(value2);
};
}
function keywordWithGlobal(propertyName) {
return function(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true)
&& !(validator.isKeyword(propertyName)(value2) || validator.isGlobal(value2))) {
return false;
} if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
}
return validator.isKeyword(propertyName)(value2) || validator.isGlobal(value2);
};
}
function propertyName(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !validator.isIdentifier(value2)) {
return false;
} if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
}
return validator.isIdentifier(value2);
}
function sameFunctionOrValue(validator, value1, value2) {
return areSameFunction(validator, value1, value2)
? true
: value1 === value2;
}
function textShadow(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true)
&& !(validator.isUnit(value2)
|| validator.isColor(value2)
|| validator.isGlobal(value2))) {
return false;
} 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;
} if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
} if (validator.isTime(value1) && !validator.isTime(value2)) {
return false;
} if (validator.isTime(value2)) {
return true;
} if (validator.isTime(value1)) {
return false;
} if (validator.isFunction(value1)
&& !validator.isPrefixed(value1)
&& validator.isFunction(value2)
&& !validator.isPrefixed(value2)) {
return true;
}
return sameFunctionOrValue(validator, value1, value2);
}
function timingFunction(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true)
&& !(validator.isTimingFunction(value2) || validator.isGlobal(value2))) {
return false;
} if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
}
return validator.isTimingFunction(value2) || validator.isGlobal(value2);
}
function unit(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !validator.isUnit(value2)) {
return false;
} if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
} if (validator.isUnit(value1) && !validator.isUnit(value2)) {
return false;
} if (validator.isUnit(value2)) {
return true;
} if (validator.isUnit(value1)) {
return false;
} if (validator.isFunction(value1)
&& !validator.isPrefixed(value1)
&& validator.isFunction(value2)
&& !validator.isPrefixed(value2)) {
return true;
}
return sameFunctionOrValue(validator, value1, value2);
}
function unitOrKeywordWithGlobal(propertyName) {
var byKeyword = keywordWithGlobal(propertyName);
return function(validator, value1, value2) {
return unit(validator, value1, value2) || byKeyword(validator, value1, value2);
};
}
function unitOrNumber(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true)
&& !(validator.isUnit(value2)
|| validator.isNumber(value2))) {
return false;
} if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
} if ((validator.isUnit(value1)
|| validator.isNumber(value1))
&& !(validator.isUnit(value2)
|| validator.isNumber(value2))) {
return false;
} if (validator.isUnit(value2) || validator.isNumber(value2)) {
return true;
} if (validator.isUnit(value1) || validator.isNumber(value1)) {
return false;
} if (validator.isFunction(value1)
&& !validator.isPrefixed(value1)
&& validator.isFunction(value2)
&& !validator.isPrefixed(value2)) {
return true;
}
return sameFunctionOrValue(validator, value1, value2);
}
function zIndex(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !validator.isZIndex(value2)) {
return false;
} if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
}
return validator.isZIndex(value2);
}
module.exports = {
generic: {
color: color,
components: components,
image: image,
propertyName: propertyName,
time: time,
timingFunction: timingFunction,
unit: unit,
unitOrNumber: unitOrNumber
},
property: {
animationDirection: keywordWithGlobal('animation-direction'),
animationFillMode: keyword('animation-fill-mode'),
animationIterationCount: animationIterationCount,
animationName: animationName,
animationPlayState: keywordWithGlobal('animation-play-state'),
backgroundAttachment: keyword('background-attachment'),
backgroundClip: keywordWithGlobal('background-clip'),
backgroundOrigin: keyword('background-origin'),
backgroundPosition: backgroundPosition,
backgroundRepeat: keyword('background-repeat'),
backgroundSize: backgroundSize,
bottom: unitOrKeywordWithGlobal('bottom'),
borderCollapse: keyword('border-collapse'),
borderStyle: keywordWithGlobal('*-style'),
clear: keywordWithGlobal('clear'),
cursor: keywordWithGlobal('cursor'),
display: keywordWithGlobal('display'),
float: keywordWithGlobal('float'),
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'),
outlineStyle: keywordWithGlobal('*-style'),
overflow: keywordWithGlobal('overflow'),
position: keywordWithGlobal('position'),
right: unitOrKeywordWithGlobal('right'),
textAlign: keywordWithGlobal('text-align'),
textDecoration: keywordWithGlobal('text-decoration'),
textOverflow: keywordWithGlobal('text-overflow'),
textShadow: textShadow,
top: unitOrKeywordWithGlobal('top'),
transform: sameFunctionOrValue,
verticalAlign: unitOrKeywordWithGlobal('vertical-align'),
visibility: keywordWithGlobal('visibility'),
whiteSpace: keywordWithGlobal('white-space'),
zIndex: zIndex
}
};

View File

@ -0,0 +1,15 @@
var sameVendorPrefixes = require('../../vendor-prefixes').same;
function understandable(validator, value1, value2, _position, isPaired) {
if (!sameVendorPrefixes(value1, value2)) {
return false;
}
if (isPaired && validator.isVariable(value1) !== validator.isVariable(value2)) {
return false;
}
return true;
}
module.exports = understandable;

View File

@ -0,0 +1,294 @@
var shallowClone = require('../clone').shallow;
var Token = require('../../tokenizer/token');
var Marker = require('../../tokenizer/marker');
function isInheritOnly(values) {
for (var i = 0, l = values.length; i < l; i++) {
var value = values[i][1];
if (value != 'inherit' && value != Marker.COMMA && value != Marker.FORWARD_SLASH) { return false; }
}
return true;
}
function background(property, configuration, lastInMultiplex) {
var components = property.components;
var restored = [];
var needsOne, needsBoth;
function restoreValue(component) {
Array.prototype.unshift.apply(restored, component.value);
}
function isDefaultValue(component) {
var descriptor = configuration[component.name];
if (descriptor.doubleValues && descriptor.defaultValue.length == 1) {
return component.value[0][1] == descriptor.defaultValue[0]
&& (component.value[1]
? component.value[1][1] == descriptor.defaultValue[0]
: true);
} if (descriptor.doubleValues && descriptor.defaultValue.length != 1) {
return component.value[0][1] == descriptor.defaultValue[0]
&& ((component.value[1] ? component.value[1][1] : component.value[0][1])
== descriptor.defaultValue[1]);
}
return component.value[0][1] == descriptor.defaultValue;
}
for (var i = components.length - 1; i >= 0; i--) {
var component = components[i];
var isDefault = isDefaultValue(component);
if (component.name == 'background-clip') {
var originComponent = components[i - 1];
var isOriginDefault = isDefaultValue(originComponent);
needsOne = component.value[0][1] == originComponent.value[0][1];
needsBoth = !needsOne && (
(isOriginDefault && !isDefault)
|| (!isOriginDefault && !isDefault)
|| (!isOriginDefault && isDefault && component.value[0][1] != originComponent.value[0][1]));
if (needsOne) {
restoreValue(originComponent);
} else if (needsBoth) {
restoreValue(component);
restoreValue(originComponent);
}
i--;
} else if (component.name == 'background-size') {
var positionComponent = components[i - 1];
var isPositionDefault = isDefaultValue(positionComponent);
needsOne = !isPositionDefault && isDefault;
needsBoth = !needsOne
&& (isPositionDefault && !isDefault || !isPositionDefault && !isDefault);
if (needsOne) {
restoreValue(positionComponent);
} else if (needsBoth) {
restoreValue(component);
restored.unshift([Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]);
restoreValue(positionComponent);
} else if (positionComponent.value.length == 1) {
restoreValue(positionComponent);
}
i--;
} else {
if (isDefault || configuration[component.name].multiplexLastOnly && !lastInMultiplex) { continue; }
restoreValue(component);
}
}
if (restored.length === 0 && property.value.length == 1 && property.value[0][1] == '0') { restored.push(property.value[0]); }
if (restored.length === 0) { restored.push([Token.PROPERTY_VALUE, configuration[property.name].defaultValue]); }
if (isInheritOnly(restored)) { return [restored[0]]; }
return restored;
}
function borderRadius(property) {
if (property.multiplex) {
var horizontal = shallowClone(property);
var vertical = shallowClone(property);
for (var i = 0; i < 4; i++) {
var component = property.components[i];
var horizontalComponent = shallowClone(property);
horizontalComponent.value = [component.value[0]];
horizontal.components.push(horizontalComponent);
var verticalComponent = shallowClone(property);
// FIXME: only shorthand compactor (see breakup#borderRadius) knows that border radius
// longhands have two values, whereas tokenizer does not care about populating 2nd value
// if it's missing, hence this fallback
verticalComponent.value = [component.value[1] || component.value[0]];
vertical.components.push(verticalComponent);
}
var horizontalValues = fourValues(horizontal);
var verticalValues = fourValues(vertical);
if (horizontalValues.length == verticalValues.length
&& horizontalValues[0][1] == verticalValues[0][1]
&& (horizontalValues.length > 1 ? horizontalValues[1][1] == verticalValues[1][1] : true)
&& (horizontalValues.length > 2 ? horizontalValues[2][1] == verticalValues[2][1] : true)
&& (horizontalValues.length > 3 ? horizontalValues[3][1] == verticalValues[3][1] : true)) {
return horizontalValues;
}
return horizontalValues.concat([[Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]]).concat(verticalValues);
}
return fourValues(property);
}
function font(property, configuration) {
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] != configuration[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] != configuration[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];
var value2 = components[1].value[0];
var value3 = components[2].value[0];
var value4 = components[3].value[0];
if (value1[1] == value2[1] && value1[1] == value3[1] && value1[1] == value4[1]) {
return [value1];
} if (value1[1] == value3[1] && value2[1] == value4[1]) {
return [value1, value2];
} if (value2[1] == value4[1]) {
return [value1, value2, value3];
}
return [value1, value2, value3, value4];
}
function multiplex(restoreWith) {
return function(property, configuration) {
if (!property.multiplex) { return restoreWith(property, configuration, true); }
var multiplexSize = 0;
var restored = [];
var componentMultiplexSoFar = {};
var i, l;
// At this point we don't know what's the multiplex size, e.g. how many background layers are there
for (i = 0, l = property.components[0].value.length; i < l; i++) {
if (property.components[0].value[i][1] == Marker.COMMA) { multiplexSize++; }
}
for (i = 0; i <= multiplexSize; i++) {
var _property = shallowClone(property);
// We split multiplex into parts and restore them one by one
for (var j = 0, m = property.components.length; j < m; j++) {
var componentToClone = property.components[j];
var _component = shallowClone(componentToClone);
_property.components.push(_component);
// The trick is some properties has more than one value, so we iterate over values looking for
// a multiplex separator - a comma
for (var k = componentMultiplexSoFar[_component.name] || 0, n = componentToClone.value.length; k < n; k++) {
if (componentToClone.value[k][1] == Marker.COMMA) {
componentMultiplexSoFar[_component.name] = k + 1;
break;
}
_component.value.push(componentToClone.value[k]);
}
}
// No we can restore shorthand value
var lastInMultiplex = i == multiplexSize;
var _restored = restoreWith(_property, configuration, lastInMultiplex);
Array.prototype.push.apply(restored, _restored);
if (i < multiplexSize) { restored.push([Token.PROPERTY_VALUE, Marker.COMMA]); }
}
return restored;
};
}
function withoutDefaults(property, configuration) {
var components = property.components;
var restored = [];
for (var i = components.length - 1; i >= 0; i--) {
var component = components[i];
var descriptor = configuration[component.name];
if (component.value[0][1] != descriptor.defaultValue || ('keepUnlessDefault' in descriptor) && !isDefault(components, configuration, descriptor.keepUnlessDefault)) {
restored.unshift(component.value[0]);
}
}
if (restored.length === 0) { restored.push([Token.PROPERTY_VALUE, configuration[property.name].defaultValue]); }
if (isInheritOnly(restored)) { return [restored[0]]; }
return restored;
}
function isDefault(components, configuration, propertyName) {
var component;
var i, l;
for (i = 0, l = components.length; i < l; i++) {
component = components[i];
if (component.name == propertyName && component.value[0][1] == configuration[propertyName].defaultValue) {
return true;
}
}
return false;
}
module.exports = {
background: background,
borderRadius: borderRadius,
font: font,
fourValues: fourValues,
multiplex: multiplex,
withoutDefaults: withoutDefaults
};

8
node_modules/clean-css/lib/optimizer/hack.js generated vendored Normal file
View File

@ -0,0 +1,8 @@
var Hack = {
ASTERISK: 'asterisk',
BANG: 'bang',
BACKSLASH: 'backslash',
UNDERSCORE: 'underscore'
};
module.exports = Hack;

View File

@ -0,0 +1,10 @@
function InvalidPropertyError(message) {
this.name = 'InvalidPropertyError';
this.message = message;
this.stack = (new Error()).stack;
}
InvalidPropertyError.prototype = Object.create(Error.prototype);
InvalidPropertyError.prototype.constructor = InvalidPropertyError;
module.exports = InvalidPropertyError;

View File

@ -0,0 +1,6 @@
function level0Optimize(tokens) {
// noop as level 0 means no optimizations!
return tokens;
}
module.exports = level0Optimize;

View File

@ -0,0 +1,325 @@
var sortSelectors = require('./sort-selectors');
var tidyRules = require('./tidy-rules');
var tidyBlock = require('./tidy-block');
var tidyAtRule = require('./tidy-at-rule');
var Hack = require('../hack');
var removeUnused = require('../remove-unused');
var restoreFromOptimizing = require('../restore-from-optimizing');
var wrapForOptimizing = require('../wrap-for-optimizing').all;
var configuration = require('../configuration');
var optimizers = require('./value-optimizers');
var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel;
var Token = require('../../tokenizer/token');
var Marker = require('../../tokenizer/marker');
var formatPosition = require('../../utils/format-position');
var serializeRules = require('../../writer/one-time').rules;
var CHARSET_TOKEN = '@charset';
var CHARSET_REGEXP = new RegExp('^' + CHARSET_TOKEN, 'i');
var DEFAULT_ROUNDING_PRECISION = require('../../options/rounding-precision').DEFAULT;
var VARIABLE_PROPERTY_NAME_PATTERN = /^--\S+$/;
var PROPERTY_NAME_PATTERN = /^(?:-chrome-|-[\w-]+\w|\w[\w-]+\w|\w{1,})$/;
var IMPORT_PREFIX_PATTERN = /^@import/i;
var URL_PREFIX_PATTERN = /^url\(/i;
function startsAsUrl(value) {
return URL_PREFIX_PATTERN.test(value);
}
function isImport(token) {
return IMPORT_PREFIX_PATTERN.test(token[1]);
}
function isLegacyFilter(property) {
var value;
if (property.name == 'filter' || property.name == '-ms-filter') {
value = property.value[0][1];
return value.indexOf('progid') > -1
|| value.indexOf('alpha') === 0
|| value.indexOf('chroma') === 0;
}
return false;
}
function noop() {}
function noopValueOptimizer(_name, value, _options) { return value; }
function optimizeBody(rule, properties, context) {
var options = context.options;
var valueOptimizers;
var property, name, type, value;
var propertyToken;
var propertyOptimizer;
var serializedRule = serializeRules(rule);
var _properties = wrapForOptimizing(properties);
var pluginValueOptimizers = context.options.plugins.level1Value;
var pluginPropertyOptimizers = context.options.plugins.level1Property;
var isVariable;
var i, l;
for (i = 0, l = _properties.length; i < l; i++) {
var j, k, m, n;
property = _properties[i];
name = property.name;
propertyOptimizer = configuration[name] && configuration[name].propertyOptimizer || noop;
valueOptimizers = configuration[name] && configuration[name].valueOptimizers || [optimizers.whiteSpace];
isVariable = VARIABLE_PROPERTY_NAME_PATTERN.test(name);
if (isVariable) {
valueOptimizers = options.variableOptimizers.length > 0
? options.variableOptimizers
: [optimizers.whiteSpace];
}
if (!isVariable && !PROPERTY_NAME_PATTERN.test(name)) {
propertyToken = property.all[property.position];
context.warnings.push('Invalid property name \'' + name + '\' at ' + formatPosition(propertyToken[1][2][0]) + '. Ignoring.');
property.unused = true;
continue;
}
if (property.value.length === 0) {
propertyToken = property.all[property.position];
context.warnings.push('Empty property \'' + name + '\' at ' + formatPosition(propertyToken[1][2][0]) + '. Ignoring.');
property.unused = true;
continue;
}
if (property.hack && (
(property.hack[0] == Hack.ASTERISK || property.hack[0] == Hack.UNDERSCORE)
&& !options.compatibility.properties.iePrefixHack
|| property.hack[0] == Hack.BACKSLASH && !options.compatibility.properties.ieSuffixHack
|| property.hack[0] == Hack.BANG && !options.compatibility.properties.ieBangHack)) {
property.unused = true;
continue;
}
if (!options.compatibility.properties.ieFilters && isLegacyFilter(property)) {
property.unused = true;
continue;
}
if (property.block) {
optimizeBody(rule, property.value[0][1], context);
continue;
}
for (j = 0, m = property.value.length; j < m; j++) {
type = property.value[j][0];
value = property.value[j][1];
if (type == Token.PROPERTY_BLOCK) {
property.unused = true;
context.warnings.push('Invalid value token at ' + formatPosition(value[0][1][2][0]) + '. Ignoring.');
break;
}
if (startsAsUrl(value) && !context.validator.isUrl(value)) {
property.unused = true;
context.warnings.push('Broken URL \'' + value + '\' at ' + formatPosition(property.value[j][2][0]) + '. Ignoring.');
break;
}
for (k = 0, n = valueOptimizers.length; k < n; k++) {
value = valueOptimizers[k](name, value, options);
}
for (k = 0, n = pluginValueOptimizers.length; k < n; k++) {
value = pluginValueOptimizers[k](name, value, options);
}
property.value[j][1] = value;
}
propertyOptimizer(serializedRule, property, options);
for (j = 0, m = pluginPropertyOptimizers.length; j < m; j++) {
pluginPropertyOptimizers[j](serializedRule, property, options);
}
}
restoreFromOptimizing(_properties);
removeUnused(_properties);
removeComments(properties, options);
}
function removeComments(tokens, options) {
var token;
var i;
for (i = 0; i < tokens.length; i++) {
token = tokens[i];
if (token[0] != Token.COMMENT) {
continue;
}
optimizeComment(token, options);
if (token[1].length === 0) {
tokens.splice(i, 1);
i--;
}
}
}
function optimizeComment(token, options) {
if (token[1][2] == Marker.EXCLAMATION && (options.level[OptimizationLevel.One].specialComments == 'all' || options.commentsKept < options.level[OptimizationLevel.One].specialComments)) {
options.commentsKept++;
return;
}
token[1] = [];
}
function cleanupCharsets(tokens) {
var hasCharset = false;
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
if (token[0] != Token.AT_RULE) { continue; }
if (!CHARSET_REGEXP.test(token[1])) { continue; }
if (hasCharset || token[1].indexOf(CHARSET_TOKEN) == -1) {
tokens.splice(i, 1);
i--;
l--;
} else {
hasCharset = true;
tokens.splice(i, 1);
tokens.unshift([Token.AT_RULE, token[1].replace(CHARSET_REGEXP, CHARSET_TOKEN)]);
}
}
}
function buildUnitRegexp(options) {
var units = ['px', 'em', 'ex', 'cm', 'mm', 'in', 'pt', 'pc', '%'];
var otherUnits = ['ch', 'rem', 'vh', 'vm', 'vmax', 'vmin', 'vw'];
otherUnits.forEach(function(unit) {
if (options.compatibility.units[unit]) {
units.push(unit);
}
});
return new RegExp('(^|\\s|\\(|,)0(?:' + units.join('|') + ')(\\W|$)', 'g');
}
function buildPrecisionOptions(roundingPrecision) {
var precisionOptions = {
matcher: null,
units: {}
};
var optimizable = [];
var unit;
var value;
for (unit in roundingPrecision) {
value = roundingPrecision[unit];
if (value != DEFAULT_ROUNDING_PRECISION) {
precisionOptions.units[unit] = {};
precisionOptions.units[unit].value = value;
precisionOptions.units[unit].multiplier = 10 ** value;
optimizable.push(unit);
}
}
if (optimizable.length > 0) {
precisionOptions.enabled = true;
precisionOptions.decimalPointMatcher = new RegExp('(\\d)\\.($|' + optimizable.join('|') + ')($|\\W)', 'g');
precisionOptions.zeroMatcher = new RegExp('(\\d*)(\\.\\d+)(' + optimizable.join('|') + ')', 'g');
}
return precisionOptions;
}
function buildVariableOptimizers(options) {
return options.level[OptimizationLevel.One].variableValueOptimizers.map(function(optimizer) {
if (typeof (optimizer) == 'string') {
return optimizers[optimizer] || noopValueOptimizer;
}
return optimizer;
});
}
function level1Optimize(tokens, context) {
var options = context.options;
var levelOptions = options.level[OptimizationLevel.One];
var ie7Hack = options.compatibility.selectors.ie7Hack;
var adjacentSpace = options.compatibility.selectors.adjacentSpace;
var spaceAfterClosingBrace = options.compatibility.properties.spaceAfterClosingBrace;
var format = options.format;
var mayHaveCharset = false;
var afterRules = false;
options.unitsRegexp = options.unitsRegexp || buildUnitRegexp(options);
options.precision = options.precision || buildPrecisionOptions(levelOptions.roundingPrecision);
options.commentsKept = options.commentsKept || 0;
options.variableOptimizers = options.variableOptimizers || buildVariableOptimizers(options);
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
switch (token[0]) {
case Token.AT_RULE:
token[1] = isImport(token) && afterRules ? '' : token[1];
token[1] = levelOptions.tidyAtRules ? tidyAtRule(token[1]) : token[1];
mayHaveCharset = true;
break;
case Token.AT_RULE_BLOCK:
optimizeBody(token[1], token[2], context);
afterRules = true;
break;
case Token.NESTED_BLOCK:
token[1] = levelOptions.tidyBlockScopes ? tidyBlock(token[1], spaceAfterClosingBrace) : token[1];
level1Optimize(token[2], context);
afterRules = true;
break;
case Token.COMMENT:
optimizeComment(token, options);
break;
case Token.RULE:
token[1] = levelOptions.tidySelectors
? tidyRules(token[1], !ie7Hack, adjacentSpace, format, context.warnings)
: token[1];
token[1] = token[1].length > 1 ? sortSelectors(token[1], levelOptions.selectorsSortingMethod) : token[1];
optimizeBody(token[1], token[2], context);
afterRules = true;
break;
}
if (token[0] == Token.COMMENT
&& token[1].length === 0
|| levelOptions.removeEmpty
&& (token[1].length === 0 || (token[2] && token[2].length === 0))) {
tokens.splice(i, 1);
i--;
l--;
}
}
if (levelOptions.cleanupCharsets && mayHaveCharset) {
cleanupCharsets(tokens);
}
return tokens;
}
module.exports = level1Optimize;

View File

@ -0,0 +1,10 @@
module.exports = {
background: require('./property-optimizers/background').level1.property,
boxShadow: require('./property-optimizers/box-shadow').level1.property,
borderRadius: require('./property-optimizers/border-radius').level1.property,
filter: require('./property-optimizers/filter').level1.property,
fontWeight: require('./property-optimizers/font-weight').level1.property,
margin: require('./property-optimizers/margin').level1.property,
outline: require('./property-optimizers/outline').level1.property,
padding: require('./property-optimizers/padding').level1.property
};

View File

@ -0,0 +1,23 @@
var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel;
var plugin = {
level1: {
property: function background(_rule, property, options) {
var values = property.value;
if (!options.level[OptimizationLevel.One].optimizeBackground) {
return;
}
if (values.length == 1 && values[0][1] == 'none') {
values[0][1] = '0 0';
}
if (values.length == 1 && values[0][1] == 'transparent') {
values[0][1] = '0 0';
}
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,29 @@
var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel;
var plugin = {
level1: {
property: function borderRadius(_rule, property, options) {
var values = property.value;
if (!options.level[OptimizationLevel.One].optimizeBorderRadius) {
return;
}
if (values.length == 3 && values[1][1] == '/' && values[0][1] == values[2][1]) {
property.value.splice(1);
property.dirty = true;
} else if (values.length == 5 && values[2][1] == '/' && values[0][1] == values[3][1] && values[1][1] == values[4][1]) {
property.value.splice(2);
property.dirty = true;
} else if (values.length == 7 && values[3][1] == '/' && values[0][1] == values[4][1] && values[1][1] == values[5][1] && values[2][1] == values[6][1]) {
property.value.splice(3);
property.dirty = true;
} else if (values.length == 9 && values[4][1] == '/' && values[0][1] == values[5][1] && values[1][1] == values[6][1] && values[2][1] == values[7][1] && values[3][1] == values[8][1]) {
property.value.splice(4);
property.dirty = true;
}
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,15 @@
var plugin = {
level1: {
property: function boxShadow(_rule, property) {
var values = property.value;
// remove multiple zeros
if (values.length == 4 && values[0][1] === '0' && values[1][1] === '0' && values[2][1] === '0' && values[3][1] === '0') {
property.value.splice(2);
property.dirty = true;
}
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,34 @@
var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel;
var ALPHA_OR_CHROMA_FILTER_PATTERN = /progid:DXImageTransform\.Microsoft\.(Alpha|Chroma)(\W)/;
var NO_SPACE_AFTER_COMMA_PATTERN = /,(\S)/g;
var WHITESPACE_AROUND_EQUALS_PATTERN = / ?= ?/g;
var plugin = {
level1: {
property: function filter(_rule, property, options) {
if (!options.compatibility.properties.ieFilters) {
return;
}
if (!options.level[OptimizationLevel.One].optimizeFilter) {
return;
}
if (property.value.length == 1) {
property.value[0][1] = property.value[0][1].replace(
ALPHA_OR_CHROMA_FILTER_PATTERN,
function(match, filter, suffix) {
return filter.toLowerCase() + suffix;
}
);
}
property.value[0][1] = property.value[0][1]
.replace(NO_SPACE_AFTER_COMMA_PATTERN, ', $1')
.replace(WHITESPACE_AROUND_EQUALS_PATTERN, '=');
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,23 @@
var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel;
var plugin = {
level1: {
property: function fontWeight(_rule, property, options) {
var value = property.value[0][1];
if (!options.level[OptimizationLevel.One].optimizeFontWeight) {
return;
}
if (value == 'normal') {
value = '400';
} else if (value == 'bold') {
value = '700';
}
property.value[0][1] = value;
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,21 @@
var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel;
var plugin = {
level1: {
property: function margin(_rule, property, options) {
var values = property.value;
if (!options.level[OptimizationLevel.One].replaceMultipleZeros) {
return;
}
// remove multiple zeros
if (values.length == 4 && values[0][1] === '0' && values[1][1] === '0' && values[2][1] === '0' && values[3][1] === '0') {
property.value.splice(1);
property.dirty = true;
}
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,19 @@
var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel;
var plugin = {
level1: {
property: function outline(_rule, property, options) {
var values = property.value;
if (!options.level[OptimizationLevel.One].optimizeOutline) {
return;
}
if (values.length == 1 && values[0][1] == 'none') {
values[0][1] = '0';
}
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,32 @@
var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel;
function isNegative(value) {
return value && value[1][0] == '-' && parseFloat(value[1]) < 0;
}
var plugin = {
level1: {
property: function padding(_rule, property, options) {
var values = property.value;
// remove multiple zeros
if (values.length == 4 && values[0][1] === '0' && values[1][1] === '0' && values[2][1] === '0' && values[3][1] === '0') {
property.value.splice(1);
property.dirty = true;
}
// remove negative paddings
if (options.level[OptimizationLevel.One].removeNegativePaddings
&& (
isNegative(property.value[0])
|| isNegative(property.value[1])
|| isNegative(property.value[2])
|| isNegative(property.value[3])
)) {
property.unused = true;
}
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,23 @@
var naturalCompare = require('../../utils/natural-compare');
function naturalSorter(scope1, scope2) {
return naturalCompare(scope1[1], scope2[1]);
}
function standardSorter(scope1, scope2) {
return scope1[1] > scope2[1] ? 1 : -1;
}
function sortSelectors(selectors, method) {
switch (method) {
case 'natural':
return selectors.sort(naturalSorter);
case 'standard':
return selectors.sort(standardSorter);
case 'none':
case false:
return selectors;
}
}
module.exports = sortSelectors;

View File

@ -0,0 +1,9 @@
function tidyAtRule(value) {
return value
.replace(/\s+/g, ' ')
.replace(/url\(\s+/g, 'url(')
.replace(/\s+\)/g, ')')
.trim();
}
module.exports = tidyAtRule;

View File

@ -0,0 +1,34 @@
var SUPPORTED_COMPACT_BLOCK_MATCHER = /^@media\W/;
var SUPPORTED_QUOTE_REMOVAL_MATCHER = /^@(?:keyframes|-moz-keyframes|-o-keyframes|-webkit-keyframes)\W/;
function tidyBlock(values, spaceAfterClosingBrace) {
var withoutSpaceAfterClosingBrace;
var withoutQuotes;
var i;
for (i = values.length - 1; i >= 0; i--) {
withoutSpaceAfterClosingBrace = !spaceAfterClosingBrace && SUPPORTED_COMPACT_BLOCK_MATCHER.test(values[i][1]);
withoutQuotes = SUPPORTED_QUOTE_REMOVAL_MATCHER.test(values[i][1]);
values[i][1] = values[i][1]
.replace(/\n|\r\n/g, ' ')
.replace(/\s+/g, ' ')
.replace(/(,|:|\() /g, '$1')
.replace(/ \)/g, ')');
if (withoutQuotes) {
values[i][1] = values[i][1]
.replace(/'([a-zA-Z][a-zA-Z\d\-_]+)'/, '$1')
.replace(/"([a-zA-Z][a-zA-Z\d\-_]+)"/, '$1');
}
if (withoutSpaceAfterClosingBrace) {
values[i][1] = values[i][1]
.replace(/\) /g, ')');
}
}
return values;
}
module.exports = tidyBlock;

View File

@ -0,0 +1,265 @@
var Spaces = require('../../options/format').Spaces;
var Marker = require('../../tokenizer/marker');
var formatPosition = require('../../utils/format-position');
var CASE_ATTRIBUTE_PATTERN = /[\s"'][iI]\s*\]/;
var CASE_RESTORE_PATTERN = /([\d\w])([iI])\]/g;
var DOUBLE_QUOTE_CASE_PATTERN = /="([a-zA-Z][a-zA-Z\d\-_]+)"([iI])/g;
var DOUBLE_QUOTE_PATTERN = /="([a-zA-Z][a-zA-Z\d\-_]+)"(\s|\])/g;
var HTML_COMMENT_PATTERN = /^(?:(?:<!--|-->)\s*)+/;
var SINGLE_QUOTE_CASE_PATTERN = /='([a-zA-Z][a-zA-Z\d\-_]+)'([iI])/g;
var SINGLE_QUOTE_PATTERN = /='([a-zA-Z][a-zA-Z\d\-_]+)'(\s|\])/g;
var RELATION_PATTERN = /[>+~]/;
var WHITESPACE_PATTERN = /\s/;
var ASTERISK_PLUS_HTML_HACK = '*+html ';
var ASTERISK_FIRST_CHILD_PLUS_HTML_HACK = '*:first-child+html ';
var LESS_THAN = '<';
var PSEUDO_CLASSES_WITH_SELECTORS = [
':current',
':future',
':has',
':host',
':host-context',
':is',
':not',
':past',
':where'
];
function hasInvalidCharacters(value) {
var isEscaped;
var isInvalid = false;
var character;
var isQuote = false;
var i, l;
for (i = 0, l = value.length; i < l; i++) {
character = value[i];
if (isEscaped) {
// continue as always
} else if (character == Marker.SINGLE_QUOTE || character == Marker.DOUBLE_QUOTE) {
isQuote = !isQuote;
} else if (!isQuote
&& (character == Marker.CLOSE_CURLY_BRACKET
|| character == Marker.EXCLAMATION
|| character == LESS_THAN
|| character == Marker.SEMICOLON)
) {
isInvalid = true;
break;
} else if (!isQuote && i === 0 && RELATION_PATTERN.test(character)) {
isInvalid = true;
break;
}
isEscaped = character == Marker.BACK_SLASH;
}
return isInvalid;
}
function removeWhitespace(value, format) {
var stripped = [];
var character;
var isNewLineNix;
var isNewLineWin;
var isEscaped;
var wasEscaped;
var isQuoted;
var isSingleQuoted;
var isDoubleQuoted;
var isAttribute;
var isRelation;
var isWhitespace;
var isSpaceAwarePseudoClass;
var roundBracketLevel = 0;
var wasComma = false;
var wasRelation = false;
var wasWhitespace = false;
var withCaseAttribute = CASE_ATTRIBUTE_PATTERN.test(value);
var spaceAroundRelation = format && format.spaces[Spaces.AroundSelectorRelation];
var i, l;
for (i = 0, l = value.length; i < l; i++) {
character = value[i];
isNewLineNix = character == Marker.NEW_LINE_NIX;
isNewLineWin = character == Marker.NEW_LINE_NIX && value[i - 1] == Marker.CARRIAGE_RETURN;
isQuoted = isSingleQuoted || isDoubleQuoted;
isRelation = !isAttribute && !isEscaped && roundBracketLevel === 0 && RELATION_PATTERN.test(character);
isWhitespace = WHITESPACE_PATTERN.test(character);
isSpaceAwarePseudoClass = roundBracketLevel == 1 && character == Marker.CLOSE_ROUND_BRACKET
? false
: isSpaceAwarePseudoClass
|| (roundBracketLevel === 0 && character == Marker.COLON && isPseudoClassWithSelectors(value, i));
if (wasEscaped && isQuoted && isNewLineWin) {
// swallow escaped new windows lines in comments
stripped.pop();
stripped.pop();
} else if (isEscaped && isQuoted && isNewLineNix) {
// swallow escaped new *nix lines in comments
stripped.pop();
} else if (isEscaped) {
stripped.push(character);
} else if (character == Marker.OPEN_SQUARE_BRACKET && !isQuoted) {
stripped.push(character);
isAttribute = true;
} else if (character == Marker.CLOSE_SQUARE_BRACKET && !isQuoted) {
stripped.push(character);
isAttribute = false;
} else if (character == Marker.OPEN_ROUND_BRACKET && !isQuoted) {
stripped.push(character);
roundBracketLevel++;
} else if (character == Marker.CLOSE_ROUND_BRACKET && !isQuoted) {
stripped.push(character);
roundBracketLevel--;
} else if (character == Marker.SINGLE_QUOTE && !isQuoted) {
stripped.push(character);
isSingleQuoted = true;
} else if (character == Marker.DOUBLE_QUOTE && !isQuoted) {
stripped.push(character);
isDoubleQuoted = true;
} else if (character == Marker.SINGLE_QUOTE && isQuoted) {
stripped.push(character);
isSingleQuoted = false;
} else if (character == Marker.DOUBLE_QUOTE && isQuoted) {
stripped.push(character);
isDoubleQuoted = false;
} else if (isWhitespace && wasRelation && !spaceAroundRelation) {
continue;
} else if (!isWhitespace && wasRelation && spaceAroundRelation) {
stripped.push(Marker.SPACE);
stripped.push(character);
} else if (isWhitespace && !wasWhitespace && wasComma && roundBracketLevel > 0 && isSpaceAwarePseudoClass) {
// skip space
} else if (isWhitespace && !wasWhitespace && roundBracketLevel > 0 && isSpaceAwarePseudoClass) {
stripped.push(character);
} else if (isWhitespace && (isAttribute || roundBracketLevel > 0) && !isQuoted) {
// skip space
} else if (isWhitespace && wasWhitespace && !isQuoted) {
// skip extra space
} else if ((isNewLineWin || isNewLineNix) && (isAttribute || roundBracketLevel > 0) && isQuoted) {
// skip newline
} else if (isRelation && wasWhitespace && !spaceAroundRelation) {
stripped.pop();
stripped.push(character);
} else if (isRelation && !wasWhitespace && spaceAroundRelation) {
stripped.push(Marker.SPACE);
stripped.push(character);
} else if (isWhitespace) {
stripped.push(Marker.SPACE);
} else {
stripped.push(character);
}
wasEscaped = isEscaped;
isEscaped = character == Marker.BACK_SLASH;
wasRelation = isRelation;
wasWhitespace = isWhitespace;
wasComma = character == Marker.COMMA;
}
return withCaseAttribute
? stripped.join('').replace(CASE_RESTORE_PATTERN, '$1 $2]')
: stripped.join('');
}
function isPseudoClassWithSelectors(value, colonPosition) {
var pseudoClass = value.substring(colonPosition, value.indexOf(Marker.OPEN_ROUND_BRACKET, colonPosition));
return PSEUDO_CLASSES_WITH_SELECTORS.indexOf(pseudoClass) > -1;
}
function removeQuotes(value) {
if (value.indexOf('\'') == -1 && value.indexOf('"') == -1) {
return value;
}
return value
.replace(SINGLE_QUOTE_CASE_PATTERN, '=$1 $2')
.replace(SINGLE_QUOTE_PATTERN, '=$1$2')
.replace(DOUBLE_QUOTE_CASE_PATTERN, '=$1 $2')
.replace(DOUBLE_QUOTE_PATTERN, '=$1$2');
}
function replacePseudoClasses(value) {
return value
.replace('nth-child(1)', 'first-child')
.replace('nth-of-type(1)', 'first-of-type')
.replace('nth-of-type(even)', 'nth-of-type(2n)')
.replace('nth-child(even)', 'nth-child(2n)')
.replace('nth-of-type(2n+1)', 'nth-of-type(odd)')
.replace('nth-child(2n+1)', 'nth-child(odd)')
.replace('nth-last-child(1)', 'last-child')
.replace('nth-last-of-type(1)', 'last-of-type')
.replace('nth-last-of-type(even)', 'nth-last-of-type(2n)')
.replace('nth-last-child(even)', 'nth-last-child(2n)')
.replace('nth-last-of-type(2n+1)', 'nth-last-of-type(odd)')
.replace('nth-last-child(2n+1)', 'nth-last-child(odd)');
}
function tidyRules(rules, removeUnsupported, adjacentSpace, format, warnings) {
var list = [];
var repeated = [];
function removeHTMLComment(rule, match) {
warnings.push('HTML comment \'' + match + '\' at ' + formatPosition(rule[2][0]) + '. Removing.');
return '';
}
for (var i = 0, l = rules.length; i < l; i++) {
var rule = rules[i];
var reduced = rule[1];
reduced = reduced.replace(HTML_COMMENT_PATTERN, removeHTMLComment.bind(null, rule));
if (hasInvalidCharacters(reduced)) {
warnings.push('Invalid selector \'' + rule[1] + '\' at ' + formatPosition(rule[2][0]) + '. Ignoring.');
continue;
}
reduced = removeWhitespace(reduced, format);
reduced = removeQuotes(reduced);
if (adjacentSpace && reduced.indexOf('nav') > 0) {
reduced = reduced.replace(/\+nav(\S|$)/, '+ nav$1');
}
if (removeUnsupported && reduced.indexOf(ASTERISK_PLUS_HTML_HACK) > -1) {
continue;
}
if (removeUnsupported && reduced.indexOf(ASTERISK_FIRST_CHILD_PLUS_HTML_HACK) > -1) {
continue;
}
if (reduced.indexOf('*') > -1) {
reduced = reduced
.replace(/\*([:#.[])/g, '$1')
.replace(/^(:first-child)?\+html/, '*$1+html');
}
if (repeated.indexOf(reduced) > -1) {
continue;
}
reduced = replacePseudoClasses(reduced);
rule[1] = reduced;
repeated.push(reduced);
list.push(rule);
}
if (list.length == 1 && list[0][1].length === 0) {
warnings.push('Empty selector \'' + list[0][1] + '\' at ' + formatPosition(list[0][2][0]) + '. Ignoring.');
list = [];
}
return list;
}
module.exports = tidyRules;

View File

@ -0,0 +1,14 @@
module.exports = {
color: require('./value-optimizers/color').level1.value,
degrees: require('./value-optimizers/degrees').level1.value,
fraction: require('./value-optimizers/fraction').level1.value,
precision: require('./value-optimizers/precision').level1.value,
textQuotes: require('./value-optimizers/text-quotes').level1.value,
time: require('./value-optimizers/time').level1.value,
unit: require('./value-optimizers/unit').level1.value,
urlPrefix: require('./value-optimizers/url-prefix').level1.value,
urlQuotes: require('./value-optimizers/url-quotes').level1.value,
urlWhiteSpace: require('./value-optimizers/url-whitespace').level1.value,
whiteSpace: require('./value-optimizers/whitespace').level1.value,
zero: require('./value-optimizers/zero').level1.value
};

View File

@ -0,0 +1,89 @@
var shortenHex = require('./color/shorten-hex');
var shortenHsl = require('./color/shorten-hsl');
var shortenRgb = require('./color/shorten-rgb');
var split = require('../../../utils/split');
var ANY_COLOR_FUNCTION_PATTERN = /(rgb|rgba|hsl|hsla)\(([^()]+)\)/gi;
var COLOR_PREFIX_PATTERN = /#|rgb|hsl/gi;
var HEX_LONG_PATTERN = /(^|[^='"])#([0-9a-f]{6})/gi;
var HEX_SHORT_PATTERN = /(^|[^='"])#([0-9a-f]{3})/gi;
var HEX_VALUE_PATTERN = /[0-9a-f]/i;
var HSL_PATTERN = /hsl\((-?\d+),(-?\d+)%?,(-?\d+)%?\)/gi;
var RGBA_HSLA_PATTERN = /(rgb|hsl)a?\((-?\d+),(-?\d+%?),(-?\d+%?),(0*[1-9]+[0-9]*(\.?\d*)?)\)/gi;
var RGB_PATTERN = /rgb\((-?\d+),(-?\d+),(-?\d+)\)/gi;
var TRANSPARENT_FUNCTION_PATTERN = /(?:rgba|hsla)\(0,0%?,0%?,0\)/g;
var plugin = {
level1: {
value: function color(name, value, options) {
if (!options.compatibility.properties.colors) {
return value;
}
if (!value.match(COLOR_PREFIX_PATTERN)) {
return shortenHex(value);
}
value = value
.replace(RGBA_HSLA_PATTERN, function(match, colorFn, p1, p2, p3, alpha) {
return (parseInt(alpha) >= 1 ? colorFn + '(' + [p1, p2, p3].join(',') + ')' : match);
})
.replace(RGB_PATTERN, function(match, red, green, blue) {
return shortenRgb(red, green, blue);
})
.replace(HSL_PATTERN, function(match, hue, saturation, lightness) {
return shortenHsl(hue, saturation, lightness);
})
.replace(HEX_LONG_PATTERN, function(match, prefix, color, at, inputValue) {
var suffix = inputValue[at + match.length];
if (suffix && HEX_VALUE_PATTERN.test(suffix)) {
return match;
} if (color[0] == color[1] && color[2] == color[3] && color[4] == color[5]) {
return (prefix + '#' + color[0] + color[2] + color[4]).toLowerCase();
}
return (prefix + '#' + color).toLowerCase();
})
.replace(HEX_SHORT_PATTERN, function(match, prefix, color) {
return prefix + '#' + color.toLowerCase();
})
.replace(ANY_COLOR_FUNCTION_PATTERN, function(match, colorFunction, colorDef) {
var tokens = colorDef.split(',');
var colorFnLowercase = colorFunction && colorFunction.toLowerCase();
var applies = (colorFnLowercase == 'hsl' && tokens.length == 3)
|| (colorFnLowercase == 'hsla' && tokens.length == 4)
|| (colorFnLowercase == 'rgb' && tokens.length === 3 && colorDef.indexOf('%') > 0)
|| (colorFnLowercase == 'rgba' && tokens.length == 4 && tokens[0].indexOf('%') > 0);
if (!applies) {
return match;
}
if (tokens[1].indexOf('%') == -1) {
tokens[1] += '%';
}
if (tokens[2].indexOf('%') == -1) {
tokens[2] += '%';
}
return colorFunction + '(' + tokens.join(',') + ')';
});
if (options.compatibility.colors.opacity && name.indexOf('background') == -1) {
value = value.replace(TRANSPARENT_FUNCTION_PATTERN, function(match) {
if (split(value, ',').pop().indexOf('gradient(') > -1) {
return match;
}
return 'transparent';
});
}
return shortenHex(value);
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,189 @@
var COLORS = {
aliceblue: '#f0f8ff',
antiquewhite: '#faebd7',
aqua: '#0ff',
aquamarine: '#7fffd4',
azure: '#f0ffff',
beige: '#f5f5dc',
bisque: '#ffe4c4',
black: '#000',
blanchedalmond: '#ffebcd',
blue: '#00f',
blueviolet: '#8a2be2',
brown: '#a52a2a',
burlywood: '#deb887',
cadetblue: '#5f9ea0',
chartreuse: '#7fff00',
chocolate: '#d2691e',
coral: '#ff7f50',
cornflowerblue: '#6495ed',
cornsilk: '#fff8dc',
crimson: '#dc143c',
cyan: '#0ff',
darkblue: '#00008b',
darkcyan: '#008b8b',
darkgoldenrod: '#b8860b',
darkgray: '#a9a9a9',
darkgreen: '#006400',
darkgrey: '#a9a9a9',
darkkhaki: '#bdb76b',
darkmagenta: '#8b008b',
darkolivegreen: '#556b2f',
darkorange: '#ff8c00',
darkorchid: '#9932cc',
darkred: '#8b0000',
darksalmon: '#e9967a',
darkseagreen: '#8fbc8f',
darkslateblue: '#483d8b',
darkslategray: '#2f4f4f',
darkslategrey: '#2f4f4f',
darkturquoise: '#00ced1',
darkviolet: '#9400d3',
deeppink: '#ff1493',
deepskyblue: '#00bfff',
dimgray: '#696969',
dimgrey: '#696969',
dodgerblue: '#1e90ff',
firebrick: '#b22222',
floralwhite: '#fffaf0',
forestgreen: '#228b22',
fuchsia: '#f0f',
gainsboro: '#dcdcdc',
ghostwhite: '#f8f8ff',
gold: '#ffd700',
goldenrod: '#daa520',
gray: '#808080',
green: '#008000',
greenyellow: '#adff2f',
grey: '#808080',
honeydew: '#f0fff0',
hotpink: '#ff69b4',
indianred: '#cd5c5c',
indigo: '#4b0082',
ivory: '#fffff0',
khaki: '#f0e68c',
lavender: '#e6e6fa',
lavenderblush: '#fff0f5',
lawngreen: '#7cfc00',
lemonchiffon: '#fffacd',
lightblue: '#add8e6',
lightcoral: '#f08080',
lightcyan: '#e0ffff',
lightgoldenrodyellow: '#fafad2',
lightgray: '#d3d3d3',
lightgreen: '#90ee90',
lightgrey: '#d3d3d3',
lightpink: '#ffb6c1',
lightsalmon: '#ffa07a',
lightseagreen: '#20b2aa',
lightskyblue: '#87cefa',
lightslategray: '#778899',
lightslategrey: '#778899',
lightsteelblue: '#b0c4de',
lightyellow: '#ffffe0',
lime: '#0f0',
limegreen: '#32cd32',
linen: '#faf0e6',
magenta: '#ff00ff',
maroon: '#800000',
mediumaquamarine: '#66cdaa',
mediumblue: '#0000cd',
mediumorchid: '#ba55d3',
mediumpurple: '#9370db',
mediumseagreen: '#3cb371',
mediumslateblue: '#7b68ee',
mediumspringgreen: '#00fa9a',
mediumturquoise: '#48d1cc',
mediumvioletred: '#c71585',
midnightblue: '#191970',
mintcream: '#f5fffa',
mistyrose: '#ffe4e1',
moccasin: '#ffe4b5',
navajowhite: '#ffdead',
navy: '#000080',
oldlace: '#fdf5e6',
olive: '#808000',
olivedrab: '#6b8e23',
orange: '#ffa500',
orangered: '#ff4500',
orchid: '#da70d6',
palegoldenrod: '#eee8aa',
palegreen: '#98fb98',
paleturquoise: '#afeeee',
palevioletred: '#db7093',
papayawhip: '#ffefd5',
peachpuff: '#ffdab9',
peru: '#cd853f',
pink: '#ffc0cb',
plum: '#dda0dd',
powderblue: '#b0e0e6',
purple: '#800080',
rebeccapurple: '#663399',
red: '#f00',
rosybrown: '#bc8f8f',
royalblue: '#4169e1',
saddlebrown: '#8b4513',
salmon: '#fa8072',
sandybrown: '#f4a460',
seagreen: '#2e8b57',
seashell: '#fff5ee',
sienna: '#a0522d',
silver: '#c0c0c0',
skyblue: '#87ceeb',
slateblue: '#6a5acd',
slategray: '#708090',
slategrey: '#708090',
snow: '#fffafa',
springgreen: '#00ff7f',
steelblue: '#4682b4',
tan: '#d2b48c',
teal: '#008080',
thistle: '#d8bfd8',
tomato: '#ff6347',
turquoise: '#40e0d0',
violet: '#ee82ee',
wheat: '#f5deb3',
white: '#fff',
whitesmoke: '#f5f5f5',
yellow: '#ff0',
yellowgreen: '#9acd32'
};
var toHex = {};
var toName = {};
for (var name in COLORS) {
var hex = COLORS[name];
if (name.length < hex.length) {
toName[hex] = name;
} else {
toHex[name] = hex;
}
}
var toHexPattern = new RegExp('(^| |,|\\))(' + Object.keys(toHex).join('|') + ')( |,|\\)|$)', 'ig');
var toNamePattern = new RegExp('(' + Object.keys(toName).join('|') + ')([^a-f0-9]|$)', 'ig');
function hexConverter(match, prefix, colorValue, suffix) {
return prefix + toHex[colorValue.toLowerCase()] + suffix;
}
function nameConverter(match, colorValue, suffix) {
return toName[colorValue.toLowerCase()] + suffix;
}
function shortenHex(value) {
var hasHex = value.indexOf('#') > -1;
var shortened = value.replace(toHexPattern, hexConverter);
if (shortened != value) {
shortened = shortened.replace(toHexPattern, hexConverter);
}
return hasHex
? shortened.replace(toNamePattern, nameConverter)
: shortened;
}
module.exports = shortenHex;

View File

@ -0,0 +1,54 @@
// HSL to RGB converter. Both methods adapted from:
// http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
function hslToRgb(h, s, l) {
var r, g, b;
// normalize hue orientation b/w 0 and 360 degrees
h %= 360;
if (h < 0) { h += 360; }
h = ~~h / 360;
if (s < 0) { s = 0; } else if (s > 100) { s = 100; }
s = ~~s / 100;
if (l < 0) { l = 0; } else if (l > 100) { l = 100; }
l = ~~l / 100;
if (s === 0) {
r = g = b = l; // achromatic
} else {
var q = l < 0.5
? l * (1 + s)
: l + s - l * s;
var p = 2 * l - q;
r = hueToRgb(p, q, h + 1 / 3);
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - 1 / 3);
}
return [~~(r * 255), ~~(g * 255), ~~(b * 255)];
}
function hueToRgb(p, q, t) {
if (t < 0) { t += 1; }
if (t > 1) { t -= 1; }
if (t < 1 / 6) { return p + (q - p) * 6 * t; }
if (t < 1 / 2) { return q; }
if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6; }
return p;
}
function shortenHsl(hue, saturation, lightness) {
var asRgb = hslToRgb(hue, saturation, lightness);
var redAsHex = asRgb[0].toString(16);
var greenAsHex = asRgb[1].toString(16);
var blueAsHex = asRgb[2].toString(16);
return '#'
+ ((redAsHex.length == 1 ? '0' : '') + redAsHex)
+ ((greenAsHex.length == 1 ? '0' : '') + greenAsHex)
+ ((blueAsHex.length == 1 ? '0' : '') + blueAsHex);
}
module.exports = shortenHsl;

View File

@ -0,0 +1,10 @@
function shortenRgb(red, green, blue) {
var normalizedRed = Math.max(0, Math.min(parseInt(red), 255));
var normalizedGreen = Math.max(0, Math.min(parseInt(green), 255));
var normalizedBlue = Math.max(0, Math.min(parseInt(blue), 255));
// Credit: Asen http://jsbin.com/UPUmaGOc/2/edit?js,console
return '#' + ('00000' + (normalizedRed << 16 | normalizedGreen << 8 | normalizedBlue).toString(16)).slice(-6);
}
module.exports = shortenRgb;

View File

@ -0,0 +1,19 @@
var ZERO_DEG_PATTERN = /\(0deg\)/g;
var plugin = {
level1: {
value: function degrees(_name, value, options) {
if (!options.compatibility.properties.zeroUnits) {
return value;
}
if (value.indexOf('0deg') == -1) {
return value;
}
return value.replace(ZERO_DEG_PATTERN, '(0)');
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,72 @@
var split = require('../../../utils/split');
var startsAsUrl = require('./starts-as-url');
var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel;
var EXPRESSION_PATTERN = /^expression\(.*\)$/;
var ANY_FUNCTION_PATTERN = /^(-(?:moz|ms|o|webkit)-[a-z-]+|[a-z-]+)\((.+)\)$/;
var TOKEN_SEPARATOR_PATTERN = /([\s,/])/;
var DOT_ZERO_PATTERN = /(^|\D)\.0+(\D|$)/g;
var FRACTION_PATTERN = /\.([1-9]*)0+(\D|$)/g;
var LEADING_ZERO_FRACTION_PATTERN = /(^|\D)0\.(\d)/g;
var MINUS_ZERO_FRACTION_PATTERN = /([^\w\d-]|^)-0([^.]|$)/g;
var ZERO_PREFIXED_UNIT_PATTERN = /(^|\s)0+([1-9])/g;
function optimizeRecursively(value) {
var functionTokens;
var tokens;
if (startsAsUrl(value)) {
return value;
}
if (EXPRESSION_PATTERN.test(value)) {
return value;
}
functionTokens = ANY_FUNCTION_PATTERN.exec(value);
if (!functionTokens) {
return optimizeFractions(value);
}
tokens = split(functionTokens[2], TOKEN_SEPARATOR_PATTERN)
.map(function(token) { return optimizeRecursively(token); });
return functionTokens[1] + '(' + tokens.join('') + ')';
}
function optimizeFractions(value) {
if (value.indexOf('0') == -1) {
return value;
}
if (value.indexOf('-') > -1) {
value = value
.replace(MINUS_ZERO_FRACTION_PATTERN, '$10$2')
.replace(MINUS_ZERO_FRACTION_PATTERN, '$10$2');
}
return value
.replace(ZERO_PREFIXED_UNIT_PATTERN, '$1$2')
.replace(DOT_ZERO_PATTERN, '$10$2')
.replace(FRACTION_PATTERN, function(match, nonZeroPart, suffix) {
return (nonZeroPart.length > 0 ? '.' : '') + nonZeroPart + suffix;
})
.replace(LEADING_ZERO_FRACTION_PATTERN, '$1.$2');
}
var plugin = {
level1: {
value: function fraction(name, value, options) {
if (!options.level[OptimizationLevel.One].replaceZeroUnits) {
return value;
}
return optimizeRecursively(value);
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,22 @@
var plugin = {
level1: {
value: function precision(_name, value, options) {
if (!options.precision.enabled || value.indexOf('.') === -1) {
return value;
}
return value
.replace(options.precision.decimalPointMatcher, '$1$2$3')
.replace(options.precision.zeroMatcher, function(match, integerPart, fractionPart, unit) {
var multiplier = options.precision.units[unit].multiplier;
var parsedInteger = parseInt(integerPart);
var integer = Number.isNaN(parsedInteger) ? 0 : parsedInteger;
var fraction = parseFloat(fractionPart);
return Math.round((integer + fraction) * multiplier) / multiplier + unit;
});
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,7 @@
var URL_PREFIX_PATTERN = /^url\(/i;
function startsAsUrl(value) {
return URL_PREFIX_PATTERN.test(value);
}
module.exports = startsAsUrl;

View File

@ -0,0 +1,31 @@
var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel;
var LOCAL_PREFIX_PATTERN = /^local\(/i;
var QUOTED_PATTERN = /^('.*'|".*")$/;
var QUOTED_BUT_SAFE_PATTERN = /^['"][a-zA-Z][a-zA-Z\d\-_]+['"]$/;
// eslint-disable-next-line max-len
var GENERIC_FONT_FAMILY_PATTERN = /^['"](?:cursive|default|emoji|fangsong|fantasy|inherit|initial|math|monospace|revert|revert-layer|sans-serif|serif|system-ui|ui-monospace|ui-rounded|ui-sans-serif|ui-serif|unset)['"]$/;
var plugin = {
level1: {
value: function textQuotes(name, value, options) {
if ((name == 'font-family' || name == 'font') && GENERIC_FONT_FAMILY_PATTERN.test(value)) {
return value;
}
if (!options.level[OptimizationLevel.One].removeQuotes) {
return value;
}
if (!QUOTED_PATTERN.test(value) && !LOCAL_PREFIX_PATTERN.test(value)) {
return value;
}
return QUOTED_BUT_SAFE_PATTERN.test(value)
? value.substring(1, value.length - 1)
: value;
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,31 @@
var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel;
var TIME_VALUE = /^(-?[\d.]+)(m?s)$/;
var plugin = {
level1: {
value: function time(name, value, options) {
if (!options.level[OptimizationLevel.One].replaceTimeUnits) {
return value;
}
if (!TIME_VALUE.test(value)) {
return value;
}
return value.replace(TIME_VALUE, function(match, val, unit) {
var newValue;
if (unit == 'ms') {
newValue = parseInt(val) / 1000 + 's';
} else if (unit == 's') {
newValue = parseFloat(val) * 1000 + 'ms';
}
return newValue.length < match.length ? newValue : match;
});
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,46 @@
var WHOLE_PIXEL_VALUE = /(?:^|\s|\()(-?\d+)px/;
var plugin = {
level1: {
value: function unit(_name, value, options) {
if (!WHOLE_PIXEL_VALUE.test(value)) {
return value;
}
return value.replace(WHOLE_PIXEL_VALUE, function(match, val) {
var newValue;
var intVal = parseInt(val);
if (intVal === 0) {
return match;
}
if (options.compatibility.properties.shorterLengthUnits
&& options.compatibility.units.pt
&& intVal * 3 % 4 === 0) {
newValue = intVal * 3 / 4 + 'pt';
}
if (options.compatibility.properties.shorterLengthUnits
&& options.compatibility.units.pc
&& intVal % 16 === 0) {
newValue = intVal / 16 + 'pc';
}
if (options.compatibility.properties.shorterLengthUnits
&& options.compatibility.units.in
&& intVal % 96 === 0) {
newValue = intVal / 96 + 'in';
}
if (newValue) {
newValue = match.substring(0, match.indexOf(val)) + newValue;
}
return newValue && newValue.length < match.length ? newValue : match;
});
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,23 @@
var startsAsUrl = require('./starts-as-url');
var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel;
var URL_PREFIX_PATTERN = /^url\(/i;
var plugin = {
level1: {
value: function urlPrefix(_name, value, options) {
if (!options.level[OptimizationLevel.One].normalizeUrls) {
return value;
}
if (!startsAsUrl(value)) {
return value;
}
return value.replace(URL_PREFIX_PATTERN, 'url(');
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,22 @@
var QUOTED_URL_PATTERN = /^url\(['"].+['"]\)$/;
var QUOTED_URL_WITH_WHITESPACE_PATTERN = /^url\(['"].*[*\s()'"].*['"]\)$/;
var QUOTES_PATTERN = /["']/g;
var URL_DATA_PATTERN = /^url\(['"]data:[^;]+;charset/;
var plugin = {
level1: {
value: function urlQuotes(_name, value, options) {
if (options.compatibility.properties.urlQuotes) {
return value;
}
return QUOTED_URL_PATTERN.test(value)
&& !QUOTED_URL_WITH_WHITESPACE_PATTERN.test(value)
&& !URL_DATA_PATTERN.test(value)
? value.replace(QUOTES_PATTERN, '')
: value;
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,22 @@
var startsAsUrl = require('./starts-as-url');
var WHITESPACE_PATTERN = /\\?\n|\\?\r\n/g;
var WHITESPACE_PREFIX_PATTERN = /(\()\s+/g;
var WHITESPACE_SUFFIX_PATTERN = /\s+(\))/g;
var plugin = {
level1: {
value: function urlWhitespace(_name, value) {
if (!startsAsUrl(value)) {
return value;
}
return value
.replace(WHITESPACE_PATTERN, '')
.replace(WHITESPACE_PREFIX_PATTERN, '$1')
.replace(WHITESPACE_SUFFIX_PATTERN, '$1');
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,48 @@
var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel;
var Marker = require('../../../tokenizer/marker');
var CALC_DIVISION_WHITESPACE_PATTERN = /\) ?\/ ?/g;
var COMMA_AND_SPACE_PATTERN = /, /g;
var LINE_BREAK_PATTERN = /\r?\n/g;
var MULTI_WHITESPACE_PATTERN = /\s+/g;
var FUNCTION_CLOSING_BRACE_WHITESPACE_PATTERN = /\s+(;?\))/g;
var FUNCTION_OPENING_BRACE_WHITESPACE_PATTERN = /(\(;?)\s+/g;
var VARIABLE_NAME_PATTERN = /^--\S+$/;
var VARIABLE_VALUE_PATTERN = /^var\(\s*--\S+\s*\)$/;
var plugin = {
level1: {
value: function whitespace(name, value, options) {
if (!options.level[OptimizationLevel.One].removeWhitespace) {
return value;
}
if (VARIABLE_NAME_PATTERN.test(name) && !VARIABLE_VALUE_PATTERN.test(value)) {
return value;
}
if ((value.indexOf(' ') == -1 && value.indexOf('\n') == -1) || value.indexOf('expression') === 0) {
return value;
}
if (value.indexOf(Marker.SINGLE_QUOTE) > -1 || value.indexOf(Marker.DOUBLE_QUOTE) > -1) {
return value;
}
value = value.replace(LINE_BREAK_PATTERN, '');
value = value.replace(MULTI_WHITESPACE_PATTERN, ' ');
if (value.indexOf('calc') > -1) {
value = value.replace(CALC_DIVISION_WHITESPACE_PATTERN, ')/ ');
}
return value
.replace(FUNCTION_OPENING_BRACE_WHITESPACE_PATTERN, '$1')
.replace(FUNCTION_CLOSING_BRACE_WHITESPACE_PATTERN, '$1')
.replace(COMMA_AND_SPACE_PATTERN, ',');
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,49 @@
var split = require('../../../utils/split');
var ANY_FUNCTION_PATTERN = /^(-(?:moz|ms|o|webkit)-[a-z-]+|[a-z-]+)\((.+)\)$/;
var SKIP_FUNCTION_PATTERN = /^(?:-moz-calc|-webkit-calc|calc|rgb|hsl|rgba|hsla|min|max|clamp|expression)\(/;
var TOKEN_SEPARATOR_PATTERN = /([\s,/])/;
function removeRecursively(value, options) {
var functionTokens;
var tokens;
if (SKIP_FUNCTION_PATTERN.test(value)) {
return value;
}
functionTokens = ANY_FUNCTION_PATTERN.exec(value);
if (!functionTokens) {
return removeZeros(value, options);
}
tokens = split(functionTokens[2], TOKEN_SEPARATOR_PATTERN)
.map(function(token) { return removeRecursively(token, options); });
return functionTokens[1] + '(' + tokens.join('') + ')';
}
function removeZeros(value, options) {
return value
.replace(options.unitsRegexp, '$10$2')
.replace(options.unitsRegexp, '$10$2');
}
var plugin = {
level1: {
value: function zero(name, value, options) {
if (!options.compatibility.properties.zeroUnits) {
return value;
}
if (value.indexOf('%') > 0 && (name == 'height' || name == 'max-height' || name == 'width' || name == 'max-width')) {
return value;
}
return removeRecursively(value, options);
}
}
};
module.exports = plugin;

View File

@ -0,0 +1,61 @@
// This extractor is used in level 2 optimizations
// IMPORTANT: Mind Token class and this code is not related!
// Properties will be tokenized in one step, see #429
var Token = require('../../tokenizer/token');
var serializeRules = require('../../writer/one-time').rules;
var serializeValue = require('../../writer/one-time').value;
function extractProperties(token) {
var properties = [];
var inSpecificSelector;
var property;
var name;
var value;
var i, l;
if (token[0] == Token.RULE) {
inSpecificSelector = !/[.+>~]/.test(serializeRules(token[1]));
for (i = 0, l = token[2].length; i < l; i++) {
property = token[2][i];
if (property[0] != Token.PROPERTY) { continue; }
name = property[1][1];
if (name.length === 0) { continue; }
value = serializeValue(property, i);
properties.push([
name,
value,
findNameRoot(name),
token[2][i],
name + ':' + value,
token[1],
inSpecificSelector
]);
}
} else if (token[0] == Token.NESTED_BLOCK) {
for (i = 0, l = token[2].length; i < l; i++) {
properties = properties.concat(extractProperties(token[2][i]));
}
}
return properties;
}
function findNameRoot(name) {
if (name == 'list-style') { return name; }
if (name.indexOf('-radius') > 0) { return 'border-radius'; }
if (name == 'border-collapse' || name == 'border-spacing' || name == 'border-image') { return name; }
if (name.indexOf('border-') === 0 && /^border-\w+-\w+$/.test(name)) { return name.match(/border-\w+/)[0]; }
if (name.indexOf('border-') === 0 && /^border-\w+$/.test(name)) { return 'border'; }
if (name.indexOf('text-') === 0) { return name; }
if (name == '-chrome-') { return name; }
return name.replace(/^-\w+-/, '').match(/([a-zA-Z]+)/)[0].toLowerCase();
}
module.exports = extractProperties;

View File

@ -0,0 +1,273 @@
var Marker = require('../../tokenizer/marker');
var split = require('../../utils/split');
var DEEP_SELECTOR_PATTERN = /\/deep\//;
var DOUBLE_COLON_PATTERN = /^::/;
var VENDOR_PREFIXED_PATTERN = /:(-moz-|-ms-|-o-|-webkit-)/;
var NOT_PSEUDO = ':not';
var PSEUDO_CLASSES_WITH_ARGUMENTS = [
':dir',
':lang',
':not',
':nth-child',
':nth-last-child',
':nth-last-of-type',
':nth-of-type'
];
var RELATION_PATTERN = /[>+~]/;
var 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',
SINGLE_QUOTE: 'single-quote',
ROOT: 'root'
};
function isMergeable(selector, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) {
var singleSelectors = split(selector, Marker.COMMA);
var singleSelector;
var i, l;
for (i = 0, l = singleSelectors.length; i < l; i++) {
singleSelector = singleSelectors[i];
if (singleSelector.length === 0
|| isDeepSelector(singleSelector)
|| isVendorPrefixed(singleSelector)
|| (singleSelector.indexOf(Marker.COLON) > -1
&& !areMergeable(
singleSelector,
extractPseudoFrom(singleSelector),
mergeablePseudoClasses,
mergeablePseudoElements,
multiplePseudoMerging
))) {
return false;
}
}
return true;
}
function isDeepSelector(selector) {
return DEEP_SELECTOR_PATTERN.test(selector);
}
function isVendorPrefixed(selector) {
return VENDOR_PREFIXED_PATTERN.test(selector);
}
function extractPseudoFrom(selector) {
var list = [];
var character;
var buffer = [];
var level = Level.ROOT;
var roundBracketLevel = 0;
var isQuoted;
var isEscaped;
var isPseudo = false;
var isRelation;
var wasColon = false;
var index;
var len;
for (index = 0, len = selector.length; index < len; index++) {
character = selector[index];
isRelation = !isEscaped && RELATION_PATTERN.test(character);
isQuoted = level == Level.DOUBLE_QUOTE || level == Level.SINGLE_QUOTE;
if (isEscaped) {
buffer.push(character);
} else if (character == Marker.DOUBLE_QUOTE && level == Level.ROOT) {
buffer.push(character);
level = Level.DOUBLE_QUOTE;
} else if (character == Marker.DOUBLE_QUOTE && level == Level.DOUBLE_QUOTE) {
buffer.push(character);
level = Level.ROOT;
} else if (character == Marker.SINGLE_QUOTE && level == Level.ROOT) {
buffer.push(character);
level = Level.SINGLE_QUOTE;
} else if (character == Marker.SINGLE_QUOTE && level == Level.SINGLE_QUOTE) {
buffer.push(character);
level = Level.ROOT;
} else if (isQuoted) {
buffer.push(character);
} else if (character == Marker.OPEN_ROUND_BRACKET) {
buffer.push(character);
roundBracketLevel++;
} else if (character == Marker.CLOSE_ROUND_BRACKET && roundBracketLevel == 1 && isPseudo) {
buffer.push(character);
list.push(buffer.join(''));
roundBracketLevel--;
buffer = [];
isPseudo = false;
} else if (character == Marker.CLOSE_ROUND_BRACKET) {
buffer.push(character);
roundBracketLevel--;
} else if (character == Marker.COLON && roundBracketLevel === 0 && isPseudo && !wasColon) {
list.push(buffer.join(''));
buffer = [];
buffer.push(character);
} else if (character == Marker.COLON && roundBracketLevel === 0 && !wasColon) {
buffer = [];
buffer.push(character);
isPseudo = true;
} else if (character == Marker.SPACE && roundBracketLevel === 0 && isPseudo) {
list.push(buffer.join(''));
buffer = [];
isPseudo = false;
} else if (isRelation && roundBracketLevel === 0 && isPseudo) {
list.push(buffer.join(''));
buffer = [];
isPseudo = false;
} else {
buffer.push(character);
}
isEscaped = character == Marker.BACK_SLASH;
wasColon = character == Marker.COLON;
}
if (buffer.length > 0 && isPseudo) {
list.push(buffer.join(''));
}
return list;
}
function areMergeable(selector, matches, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) {
return areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements)
&& needArguments(matches)
&& (matches.length < 2 || !someIncorrectlyChained(selector, matches))
&& (matches.length < 2 || multiplePseudoMerging && allMixable(matches));
}
function areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) {
var match;
var name;
var i, l;
for (i = 0, l = matches.length; i < l; i++) {
match = matches[i];
name = match.indexOf(Marker.OPEN_ROUND_BRACKET) > -1
? match.substring(0, match.indexOf(Marker.OPEN_ROUND_BRACKET))
: match;
if (mergeablePseudoClasses.indexOf(name) === -1 && mergeablePseudoElements.indexOf(name) === -1) {
return false;
}
}
return true;
}
function needArguments(matches) {
var match;
var name;
var bracketOpensAt;
var hasArguments;
var i, l;
for (i = 0, l = matches.length; i < l; i++) {
match = matches[i];
bracketOpensAt = match.indexOf(Marker.OPEN_ROUND_BRACKET);
hasArguments = bracketOpensAt > -1;
name = hasArguments
? match.substring(0, bracketOpensAt)
: match;
if (hasArguments && PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) == -1) {
return false;
}
if (!hasArguments && PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) > -1) {
return false;
}
}
return true;
}
function someIncorrectlyChained(selector, matches) {
var positionInSelector = 0;
var match;
var matchAt;
var nextMatch;
var nextMatchAt;
var name;
var nextName;
var areChained;
var i, l;
for (i = 0, l = matches.length; i < l; i++) {
match = matches[i];
nextMatch = matches[i + 1];
if (!nextMatch) {
break;
}
matchAt = selector.indexOf(match, positionInSelector);
nextMatchAt = selector.indexOf(match, matchAt + 1);
positionInSelector = nextMatchAt;
areChained = matchAt + match.length == nextMatchAt;
if (areChained) {
name = match.indexOf(Marker.OPEN_ROUND_BRACKET) > -1
? match.substring(0, match.indexOf(Marker.OPEN_ROUND_BRACKET))
: match;
nextName = nextMatch.indexOf(Marker.OPEN_ROUND_BRACKET) > -1
? nextMatch.substring(0, nextMatch.indexOf(Marker.OPEN_ROUND_BRACKET))
: nextMatch;
if (name != NOT_PSEUDO || nextName != NOT_PSEUDO) {
return true;
}
}
}
return false;
}
function allMixable(matches) {
var unmixableMatches = 0;
var match;
var i, l;
for (i = 0, l = matches.length; i < l; i++) {
match = matches[i];
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 true;
}
function isPseudoElement(pseudo) {
return DOUBLE_COLON_PATTERN.test(pseudo);
}
module.exports = isMergeable;

View File

@ -0,0 +1,55 @@
var isMergeable = require('./is-mergeable');
var optimizeProperties = require('./properties/optimize');
var sortSelectors = require('../level-1/sort-selectors');
var tidyRules = require('../level-1/tidy-rules');
var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel;
var serializeBody = require('../../writer/one-time').body;
var serializeRules = require('../../writer/one-time').rules;
var Token = require('../../tokenizer/token');
function mergeAdjacent(tokens, context) {
var lastToken = [null, [], []];
var options = context.options;
var adjacentSpace = options.compatibility.selectors.adjacentSpace;
var selectorsSortingMethod = options.level[OptimizationLevel.One].selectorsSortingMethod;
var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses;
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements;
var mergeLimit = options.compatibility.selectors.mergeLimit;
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging;
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
if (token[0] != Token.RULE) {
lastToken = [null, [], []];
continue;
}
if (lastToken[0] == Token.RULE && serializeRules(token[1]) == serializeRules(lastToken[1])) {
Array.prototype.push.apply(lastToken[2], token[2]);
optimizeProperties(lastToken[2], true, true, context);
token[2] = [];
} else if (lastToken[0] == Token.RULE && serializeBody(token[2]) == serializeBody(lastToken[2])
&& isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements, 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];
token[2] = [];
} else {
lastToken = token;
}
}
}
module.exports = mergeAdjacent;

View File

@ -0,0 +1,105 @@
var canReorder = require('./reorderable').canReorder;
var canReorderSingle = require('./reorderable').canReorderSingle;
var extractProperties = require('./extract-properties');
var rulesOverlap = require('./rules-overlap');
var serializeRules = require('../../writer/one-time').rules;
var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel;
var Token = require('../../tokenizer/token');
function mergeMediaQueries(tokens, context) {
var mergeSemantically = context.options.level[OptimizationLevel.Two].mergeSemantically;
var specificityCache = context.cache.specificity;
var candidates = {};
var reduced = [];
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
if (token[0] != Token.NESTED_BLOCK) {
continue;
}
var key = serializeRules(token[1]);
var candidate = candidates[key];
if (!candidate) {
candidate = [];
candidates[key] = candidate;
}
candidate.push(i);
}
for (var name in candidates) {
var positions = candidates[name];
positionLoop:
for (var j = positions.length - 1; j > 0; j--) {
var positionOne = positions[j];
var tokenOne = tokens[positionOne];
var positionTwo = positions[j - 1];
var tokenTwo = tokens[positionTwo];
directionLoop:
for (var direction = 1; direction >= -1; direction -= 2) {
var topToBottom = direction == 1;
var from = topToBottom ? positionOne + 1 : positionTwo - 1;
var to = topToBottom ? positionTwo : positionOne;
var delta = topToBottom ? 1 : -1;
var source = topToBottom ? tokenOne : tokenTwo;
var target = topToBottom ? tokenTwo : tokenOne;
var movedProperties = extractProperties(source);
while (from != to) {
var traversedProperties = extractProperties(tokens[from]);
from += delta;
if (mergeSemantically
&& allSameRulePropertiesCanBeReordered(movedProperties, traversedProperties, specificityCache)
) {
continue;
}
if (!canReorder(movedProperties, traversedProperties, specificityCache)) { continue directionLoop; }
}
target[2] = topToBottom
? source[2].concat(target[2])
: target[2].concat(source[2]);
source[2] = [];
reduced.push(target);
continue positionLoop;
}
}
}
return reduced;
}
function allSameRulePropertiesCanBeReordered(movedProperties, traversedProperties, specificityCache) {
var movedProperty;
var movedRule;
var traversedProperty;
var traversedRule;
var i, l;
var j, m;
for (i = 0, l = movedProperties.length; i < l; i++) {
movedProperty = movedProperties[i];
movedRule = movedProperty[5];
for (j = 0, m = traversedProperties.length; j < m; j++) {
traversedProperty = traversedProperties[j];
traversedRule = traversedProperty[5];
if (rulesOverlap(movedRule, traversedRule, true)
&& !canReorderSingle(movedProperty, traversedProperty, specificityCache)) {
return false;
}
}
}
return true;
}
module.exports = mergeMediaQueries;

View File

@ -0,0 +1,88 @@
var isMergeable = require('./is-mergeable');
var sortSelectors = require('../level-1/sort-selectors');
var tidyRules = require('../level-1/tidy-rules');
var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel;
var serializeBody = require('../../writer/one-time').body;
var serializeRules = require('../../writer/one-time').rules;
var Token = require('../../tokenizer/token');
function unsafeSelector(value) {
return /\.|\*| :/.test(value);
}
function isBemElement(token) {
var asString = serializeRules(token[1]);
return asString.indexOf('__') > -1 || asString.indexOf('--') > -1;
}
function withoutModifier(selector) {
return selector.replace(/--[^ ,>+~:]+/g, '');
}
function removeAnyUnsafeElements(left, candidates) {
var leftSelector = withoutModifier(serializeRules(left[1]));
for (var body in candidates) {
var right = candidates[body];
var rightSelector = withoutModifier(serializeRules(right[1]));
if (rightSelector.indexOf(leftSelector) > -1 || leftSelector.indexOf(rightSelector) > -1) {
delete candidates[body];
}
}
}
function mergeNonAdjacentByBody(tokens, context) {
var options = context.options;
var mergeSemantically = options.level[OptimizationLevel.Two].mergeSemantically;
var adjacentSpace = options.compatibility.selectors.adjacentSpace;
var selectorsSortingMethod = options.level[OptimizationLevel.One].selectorsSortingMethod;
var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses;
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements;
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging;
var candidates = {};
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
if (token[0] != Token.RULE) { continue; }
if (token[2].length > 0 && (!mergeSemantically && unsafeSelector(serializeRules(token[1])))) { candidates = {}; }
if (token[2].length > 0 && mergeSemantically && isBemElement(token)) { removeAnyUnsafeElements(token, candidates); }
var candidateBody = serializeBody(token[2]);
var oldToken = candidates[candidateBody];
if (oldToken
&& isMergeable(
serializeRules(token[1]),
mergeablePseudoClasses,
mergeablePseudoElements,
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);
token[1] = token[1].length > 1 ? sortSelectors(token[1], selectorsSortingMethod) : token[1];
} else {
token[1] = oldToken[1].concat(token[1]);
}
oldToken[2] = [];
candidates[candidateBody] = null;
}
candidates[serializeBody(token[2])] = token;
}
}
module.exports = mergeNonAdjacentByBody;

View File

@ -0,0 +1,73 @@
var canReorder = require('./reorderable').canReorder;
var extractProperties = require('./extract-properties');
var optimizeProperties = require('./properties/optimize');
var serializeRules = require('../../writer/one-time').rules;
var Token = require('../../tokenizer/token');
function mergeNonAdjacentBySelector(tokens, context) {
var specificityCache = context.cache.specificity;
var allSelectors = {};
var repeatedSelectors = [];
var i;
for (i = tokens.length - 1; i >= 0; i--) {
if (tokens[i][0] != Token.RULE) { continue; }
if (tokens[i][2].length === 0) { continue; }
var selector = serializeRules(tokens[i][1]);
allSelectors[selector] = [i].concat(allSelectors[selector] || []);
if (allSelectors[selector].length == 2) { repeatedSelectors.push(selector); }
}
for (i = repeatedSelectors.length - 1; i >= 0; i--) {
var positions = allSelectors[repeatedSelectors[i]];
selectorIterator:
for (var j = positions.length - 1; j > 0; j--) {
var positionOne = positions[j - 1];
var tokenOne = tokens[positionOne];
var positionTwo = positions[j];
var tokenTwo = tokens[positionTwo];
directionIterator:
for (var direction = 1; direction >= -1; direction -= 2) {
var topToBottom = direction == 1;
var from = topToBottom ? positionOne + 1 : positionTwo - 1;
var to = topToBottom ? positionTwo : positionOne;
var delta = topToBottom ? 1 : -1;
var moved = topToBottom ? tokenOne : tokenTwo;
var target = topToBottom ? tokenTwo : tokenOne;
var movedProperties = extractProperties(moved);
while (from != to) {
var traversedProperties = extractProperties(tokens[from]);
from += delta;
// traversed then moved as we move selectors towards the start
var reorderable = topToBottom
? canReorder(movedProperties, traversedProperties, specificityCache)
: canReorder(traversedProperties, movedProperties, specificityCache);
if (!reorderable && !topToBottom) { continue selectorIterator; }
if (!reorderable && topToBottom) { continue directionIterator; }
}
if (topToBottom) {
Array.prototype.push.apply(moved[2], target[2]);
target[2] = moved[2];
} else {
Array.prototype.push.apply(target[2], moved[2]);
}
optimizeProperties(target[2], true, true, context);
moved[2] = [];
}
}
}
}
module.exports = mergeNonAdjacentBySelector;

View File

@ -0,0 +1,139 @@
var mergeAdjacent = require('./merge-adjacent');
var mergeMediaQueries = require('./merge-media-queries');
var mergeNonAdjacentByBody = require('./merge-non-adjacent-by-body');
var mergeNonAdjacentBySelector = require('./merge-non-adjacent-by-selector');
var reduceNonAdjacent = require('./reduce-non-adjacent');
var removeDuplicateFontAtRules = require('./remove-duplicate-font-at-rules');
var removeDuplicateMediaQueries = require('./remove-duplicate-media-queries');
var removeDuplicates = require('./remove-duplicates');
var removeUnusedAtRules = require('./remove-unused-at-rules');
var restructure = require('./restructure');
var optimizeProperties = require('./properties/optimize');
var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel;
var Token = require('../../tokenizer/token');
function removeEmpty(tokens) {
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
var isEmpty = false;
switch (token[0]) {
case Token.RULE:
isEmpty = token[1].length === 0 || token[2].length === 0;
break;
case Token.NESTED_BLOCK:
removeEmpty(token[2]);
isEmpty = token[2].length === 0;
break;
case Token.AT_RULE:
isEmpty = token[1].length === 0;
break;
case Token.AT_RULE_BLOCK:
isEmpty = token[2].length === 0;
}
if (isEmpty) {
tokens.splice(i, 1);
i--;
l--;
}
}
}
function recursivelyOptimizeBlocks(tokens, context) {
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
if (token[0] == Token.NESTED_BLOCK) {
var isKeyframes = /@(-moz-|-o-|-webkit-)?keyframes/.test(token[1][0][1]);
level2Optimize(token[2], context, !isKeyframes);
}
}
}
function recursivelyOptimizeProperties(tokens, context) {
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
switch (token[0]) {
case Token.RULE:
optimizeProperties(token[2], true, true, context);
break;
case Token.NESTED_BLOCK:
recursivelyOptimizeProperties(token[2], context);
}
}
}
function level2Optimize(tokens, context, withRestructuring) {
var levelOptions = context.options.level[OptimizationLevel.Two];
var level2Plugins = context.options.plugins.level2Block;
var reduced;
var i;
recursivelyOptimizeBlocks(tokens, context);
recursivelyOptimizeProperties(tokens, context);
if (levelOptions.removeDuplicateRules) {
removeDuplicates(tokens, context);
}
if (levelOptions.mergeAdjacentRules) {
mergeAdjacent(tokens, context);
}
if (levelOptions.reduceNonAdjacentRules) {
reduceNonAdjacent(tokens, context);
}
if (levelOptions.mergeNonAdjacentRules && levelOptions.mergeNonAdjacentRules != 'body') {
mergeNonAdjacentBySelector(tokens, context);
}
if (levelOptions.mergeNonAdjacentRules && levelOptions.mergeNonAdjacentRules != 'selector') {
mergeNonAdjacentByBody(tokens, context);
}
if (levelOptions.restructureRules && levelOptions.mergeAdjacentRules && withRestructuring) {
restructure(tokens, context);
mergeAdjacent(tokens, context);
}
if (levelOptions.restructureRules && !levelOptions.mergeAdjacentRules && withRestructuring) {
restructure(tokens, context);
}
if (levelOptions.removeDuplicateFontRules) {
removeDuplicateFontAtRules(tokens, context);
}
if (levelOptions.removeDuplicateMediaBlocks) {
removeDuplicateMediaQueries(tokens, context);
}
if (levelOptions.removeUnusedAtRules) {
removeUnusedAtRules(tokens, context);
}
if (levelOptions.mergeMedia) {
reduced = mergeMediaQueries(tokens, context);
for (i = reduced.length - 1; i >= 0; i--) {
level2Optimize(reduced[i][2], context, false);
}
}
for (i = 0; i < level2Plugins.length; i++) {
level2Plugins[i](tokens);
}
if (levelOptions.removeEmpty) {
removeEmpty(tokens);
}
return tokens;
}
module.exports = level2Optimize;

View File

@ -0,0 +1,28 @@
var Marker = require('../../../tokenizer/marker');
function everyValuesPair(fn, left, right) {
var leftSize = left.value.length;
var rightSize = right.value.length;
var total = Math.max(leftSize, rightSize);
var lowerBound = Math.min(leftSize, rightSize) - 1;
var leftValue;
var rightValue;
var position;
for (position = 0; position < total; position++) {
leftValue = left.value[position] && left.value[position][1] || leftValue;
rightValue = right.value[position] && right.value[position][1] || rightValue;
if (leftValue == Marker.COMMA || rightValue == Marker.COMMA) {
continue;
}
if (!fn(leftValue, rightValue, position, position <= lowerBound)) {
return false;
}
}
return true;
}
module.exports = everyValuesPair;

View File

@ -0,0 +1,38 @@
var configuration = require('../../configuration');
function findComponentIn(shorthand, longhand) {
var comparator = nameComparator(longhand);
return findInDirectComponents(shorthand, comparator) || findInSubComponents(shorthand, comparator);
}
function nameComparator(to) {
return function(property) {
return to.name === property.name;
};
}
function findInDirectComponents(shorthand, comparator) {
return shorthand.components.filter(comparator)[0];
}
function findInSubComponents(shorthand, comparator) {
var shorthandComponent;
var longhandMatch;
var i, l;
if (!configuration[shorthand.name].shorthandComponents) {
return;
}
for (i = 0, l = shorthand.components.length; i < l; i++) {
shorthandComponent = shorthand.components[i];
longhandMatch = findInDirectComponents(shorthandComponent, comparator);
if (longhandMatch) {
return longhandMatch;
}
}
}
module.exports = findComponentIn;

View File

@ -0,0 +1,9 @@
function hasInherit(property) {
for (var i = property.value.length - 1; i >= 0; i--) {
if (property.value[i][1] == 'inherit') { return true; }
}
return false;
}
module.exports = hasInherit;

View File

@ -0,0 +1,14 @@
function hasSameValues(property) {
var firstValue = property.value[0][1];
var i, l;
for (i = 1, l = property.value.length; i < l; i++) {
if (property.value[i][1] != firstValue) {
return false;
}
}
return true;
}
module.exports = hasSameValues;

View File

@ -0,0 +1,9 @@
function hasUnset(property) {
for (var i = property.value.length - 1; i >= 0; i--) {
if (property.value[i][1] == 'unset') { return true; }
}
return false;
}
module.exports = hasUnset;

View File

@ -0,0 +1,22 @@
var configuration = require('../../configuration');
function isComponentOf(property1, property2, shallow) {
return isDirectComponentOf(property1, property2)
|| !shallow && !!configuration[property1.name].shorthandComponents && isSubComponentOf(property1, property2);
}
function isDirectComponentOf(property1, property2) {
var descriptor = configuration[property1.name];
return 'components' in descriptor && descriptor.components.indexOf(property2.name) > -1;
}
function isSubComponentOf(property1, property2) {
return property1
.components
.some(function(component) {
return isDirectComponentOf(component, property2);
});
}
module.exports = isComponentOf;

View File

@ -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;

View File

@ -0,0 +1,496 @@
var everyValuesPair = require('./every-values-pair');
var hasInherit = require('./has-inherit');
var hasSameValues = require('./has-same-values');
var populateComponents = require('./populate-components');
var configuration = require('../../configuration');
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 = configuration[property.name];
if (property.dynamic) {
continue;
}
if (property.unused) {
continue;
}
if (property.hack) {
continue;
}
if (property.block) {
continue;
}
if (descriptor && descriptor.singleTypeComponents && !hasSameValues(property)) {
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;
var replacedCandidates = [];
var i;
for (shorthandName in candidates) {
if (undefined !== invalidatedBy && shorthandName == invalidatedBy.name) {
continue;
}
shorthandDescriptor = configuration[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);
}
replacedCandidates.push(shorthandName);
}
for (i = replacedCandidates.length - 1; i >= 0; i--) {
delete candidates[replacedCandidates[i]];
}
}
function invalidates(candidates, shorthandName, invalidatedBy) {
var shorthandDescriptor = configuration[shorthandName];
var invalidatedByDescriptor = configuration[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 (componentName in components) {
if (undefined !== important && components[componentName].important != important) {
return true;
}
important = components[componentName].important;
}
return false;
}
function overridable(components, shorthandName, validator) {
var descriptor = configuration[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 = configuration[component.name].canOverride || sameValue;
if (!everyValuesPair(mayOverride.bind(null, validator), newProperty.components[i], component)) {
return false;
}
}
return true;
}
function sameValue(_validator, value1, value2) {
return value1 === value2;
}
function mergeable(components) {
var lastCount = null;
var currentCount;
var componentName;
var component;
var descriptor;
var values;
for (componentName in components) {
component = components[componentName];
descriptor = configuration[componentName];
if (!('restore' in descriptor)) {
continue;
}
restoreFromOptimizing([component.all[component.position]], restoreWithComponents);
values = descriptor.restore(component, configuration);
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 lastComponent = candidateComponents[Object.keys(candidateComponents).pop()];
var all = lastComponent.all;
var insertAt = lastComponent.position;
var componentName;
var oldComponent;
var newComponent;
var newToken;
newProperty.position = insertAt;
newProperty.shorthand = true;
newProperty.important = lastComponent.important;
newProperty.multiplex = false;
newProperty.dirty = true;
newProperty.all = all;
newProperty.all[insertAt] = newTokensSequence[0];
properties.splice(insertAt, 1, newProperty);
for (componentName in candidateComponents) {
oldComponent = candidateComponents[componentName];
oldComponent.unused = true;
newProperty.multiplex = newProperty.multiplex || oldComponent.multiplex;
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 = configuration[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;
}
}
newProperty.important = components[Object.keys(components).pop()].important;
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 = configuration[propertyName];
if ('oppositeTo' in descriptor) {
return components[descriptor.oppositeTo].value;
}
return [[Token.PROPERTY_VALUE, descriptor.defaultValue]];
}
function joinMetadata(components, at) {
var metadata = [];
var component;
var originalValue;
var componentMetadata;
var componentName;
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.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;
} if (line1 === line2) {
return column1 < column2 ? -1 : 1;
}
return 1;
}
function buildSequenceWithInheritShorthand(components, shorthandName, validator) {
var tokensSequence = [];
var inheritComponents = {};
var nonInheritComponents = {};
var descriptor = configuration[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 = configuration[shorthandName];
var nameMetadata;
var valueMetadata;
var newValuePlaceholder = [
Token.PROPERTY,
[Token.PROPERTY_NAME, shorthandName],
[Token.PROPERTY_VALUE, descriptor.defaultValue]
];
var all;
var insertAt = inferInsertAtFrom(properties, candidateComponents, shorthandName);
var newProperty = wrapSingle(newValuePlaceholder);
newProperty.shorthand = true;
newProperty.dirty = true;
newProperty.multiplex = false;
populateComponents([newProperty], validator, []);
for (var i = 0, l = descriptor.components.length; i < l; i++) {
var component = candidateComponents[descriptor.components[i]];
newProperty.components[i] = deepClone(component);
newProperty.important = component.important;
newProperty.multiplex = newProperty.multiplex || component.multiplex;
all = component.all;
}
for (var componentName in candidateComponents) {
candidateComponents[componentName].unused = true;
}
nameMetadata = joinMetadata(candidateComponents, 1);
newValuePlaceholder[1].push(nameMetadata);
valueMetadata = joinMetadata(candidateComponents, 2);
newValuePlaceholder[2].push(valueMetadata);
newProperty.position = insertAt;
newProperty.all = all;
newProperty.all[insertAt] = newValuePlaceholder;
properties.splice(insertAt, 1, newProperty);
}
function inferInsertAtFrom(properties, candidateComponents, shorthandName) {
var candidateComponentNames = Object.keys(candidateComponents);
var firstCandidatePosition = candidateComponents[candidateComponentNames[0]].position;
var lastCandidatePosition = candidateComponents[candidateComponentNames[candidateComponentNames.length - 1]].position;
if (shorthandName == 'border' && traversesVia(properties.slice(firstCandidatePosition, lastCandidatePosition), 'border-image')) {
return firstCandidatePosition;
}
return lastCandidatePosition;
}
function traversesVia(properties, propertyName) {
for (var i = properties.length - 1; i >= 0; i--) {
if (properties[i].name == propertyName) {
return true;
}
}
return false;
}
module.exports = mergeIntoShorthands;

View File

@ -0,0 +1,40 @@
var mergeIntoShorthands = require('./merge-into-shorthands');
var overrideProperties = require('./override-properties');
var populateComponents = require('./populate-components');
var restoreWithComponents = require('../restore-with-components');
var wrapForOptimizing = require('../../wrap-for-optimizing').all;
var removeUnused = require('../../remove-unused');
var restoreFromOptimizing = require('../../restore-from-optimizing');
var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel;
function optimizeProperties(properties, withOverriding, withMerging, context) {
var levelOptions = context.options.level[OptimizationLevel.Two];
var _properties = wrapForOptimizing(properties, levelOptions.skipProperties);
var _property;
var i, l;
populateComponents(_properties, context.validator, context.warnings);
for (i = 0, l = _properties.length; i < l; i++) {
_property = _properties[i];
if (_property.block) {
optimizeProperties(_property.value[0][1], withOverriding, withMerging, context);
}
}
if (withMerging && levelOptions.mergeIntoShorthands) {
mergeIntoShorthands(_properties, context.validator);
}
if (withOverriding && levelOptions.overrideProperties) {
overrideProperties(_properties, withMerging, context.options.compatibility, context.validator);
}
restoreFromOptimizing(_properties, restoreWithComponents);
removeUnused(_properties);
}
module.exports = optimizeProperties;

View File

@ -0,0 +1,461 @@
var hasInherit = require('./has-inherit');
var hasUnset = require('./has-unset');
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;
var configuration = require('../../configuration');
var deepClone = require('../../clone').deep;
var restoreWithComponents = require('../restore-with-components');
var shallowClone = require('../../clone').shallow;
var restoreFromOptimizing = require('../../restore-from-optimizing');
var Token = require('../../../tokenizer/token');
var Marker = require('../../../tokenizer/marker');
var serializeProperty = require('../../../writer/one-time').property;
function sameValue(_validator, value1, value2) {
return value1 === value2;
}
function wouldBreakCompatibility(property, validator) {
for (var i = 0; i < property.components.length; i++) {
var component = property.components[i];
var descriptor = configuration[component.name];
var canOverride = descriptor && descriptor.canOverride || sameValue;
var _component = shallowClone(component);
_component.value = [[Token.PROPERTY_VALUE, descriptor.defaultValue]];
if (!everyValuesPair(canOverride.bind(null, validator), _component, component)) {
return true;
}
}
return false;
}
function overrideIntoMultiplex(property, by) {
by.unused = true;
turnIntoMultiplex(by, multiplexSize(property));
property.value = by.value;
}
function overrideByMultiplex(property, by) {
by.unused = true;
property.multiplex = true;
property.value = by.value;
}
function overrideSimple(property, by) {
by.unused = true;
property.value = by.value;
}
function override(property, by) {
if (by.multiplex) {
overrideByMultiplex(property, by);
} else if (property.multiplex) {
overrideIntoMultiplex(property, by);
} else {
overrideSimple(property, by);
}
}
function overrideShorthand(property, by) {
by.unused = true;
for (var i = 0, l = property.components.length; i < l; i++) {
override(property.components[i], by.components[i]);
}
}
function turnIntoMultiplex(property, size) {
property.multiplex = true;
if (configuration[property.name].shorthand) {
turnShorthandValueIntoMultiplex(property, size);
} else {
turnLonghandValueIntoMultiplex(property, size);
}
}
function turnShorthandValueIntoMultiplex(property, size) {
var component;
var i, l;
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 descriptor = configuration[property.name];
var withRealValue = descriptor.intoMultiplexMode == 'real';
var withValue = descriptor.intoMultiplexMode == 'real'
? property.value.slice(0)
: (descriptor.intoMultiplexMode == 'placeholder' ? descriptor.placeholderValue : descriptor.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]);
}
}
}
function multiplexSize(component) {
var size = 0;
for (var i = 0, l = component.value.length; i < l; i++) {
if (component.value[i][1] == Marker.COMMA) { size++; }
}
return size + 1;
}
function lengthOf(property) {
var fakeAsArray = [
Token.PROPERTY,
[Token.PROPERTY_NAME, property.name]
].concat(property.value);
return serializeProperty([fakeAsArray], 0).length;
}
function moreSameShorthands(properties, startAt, name) {
// Since we run the main loop in `compactOverrides` backwards, at this point some
// properties may not be marked as unused.
// We should consider reverting the order if possible
var count = 0;
for (var i = startAt; i >= 0; i--) {
if (properties[i].name == name && !properties[i].unused) { count++; }
if (count > 1) { break; }
}
return count > 1;
}
function overridingFunction(shorthand, validator) {
for (var i = 0, l = shorthand.components.length; i < l; i++) {
if (!anyValue(validator.isUrl, shorthand.components[i])
&& anyValue(validator.isFunction, shorthand.components[i])) { return true; }
}
return false;
}
function anyValue(fn, property) {
for (var i = 0, l = property.value.length; i < l; i++) {
if (property.value[i][1] == Marker.COMMA) { continue; }
if (fn(property.value[i][1])) { return true; }
}
return false;
}
function wouldResultInLongerValue(left, right) {
if (!left.multiplex && !right.multiplex || left.multiplex && right.multiplex) { return false; }
var multiplex = left.multiplex ? left : right;
var simple = left.multiplex ? right : left;
var component;
var multiplexClone = deepClone(multiplex);
restoreFromOptimizing([multiplexClone], restoreWithComponents);
var simpleClone = deepClone(simple);
restoreFromOptimizing([simpleClone], restoreWithComponents);
var lengthBefore = lengthOf(multiplexClone) + 1 + lengthOf(simpleClone);
if (left.multiplex) {
component = findComponentIn(multiplexClone, simpleClone);
overrideIntoMultiplex(component, simpleClone);
} else {
component = findComponentIn(simpleClone, multiplexClone);
turnIntoMultiplex(simpleClone, multiplexSize(multiplexClone));
overrideByMultiplex(component, multiplexClone);
}
restoreFromOptimizing([simpleClone], restoreWithComponents);
var lengthAfter = lengthOf(simpleClone);
return lengthBefore <= lengthAfter;
}
function isCompactable(property) {
return property.name in configuration;
}
function noneOverrideHack(left, right) {
return !left.multiplex
&& (left.name == 'background' || left.name == 'background-image')
&& right.multiplex
&& (right.name == 'background' || right.name == 'background-image')
&& anyLayerIsNone(right.value);
}
function anyLayerIsNone(values) {
var layers = intoLayers(values);
for (var i = 0, l = layers.length; i < l; i++) {
if (layers[i].length == 1 && layers[i][0][1] == 'none') { return true; }
}
return false;
}
function intoLayers(values) {
var layers = [];
for (var i = 0, layer = [], l = values.length; i < l; i++) {
var value = values[i];
if (value[1] == Marker.COMMA) {
layers.push(layer);
layer = [];
} else {
layer.push(value);
}
}
layers.push(layer);
return layers;
}
function overrideProperties(properties, withMerging, compatibility, validator) {
var mayOverride, right, left, component;
var overriddenComponents;
var overriddenComponent;
var overridingComponent;
var overridable;
var i, j, k;
propertyLoop:
for (i = properties.length - 1; i >= 0; i--) {
right = properties[i];
if (!isCompactable(right)) { continue; }
if (right.block) { continue; }
mayOverride = configuration[right.name].canOverride || sameValue;
traverseLoop:
for (j = i - 1; j >= 0; j--) {
left = properties[j];
if (!isCompactable(left)) { continue; }
if (left.block) { continue; }
if (left.dynamic || right.dynamic) { continue; }
if (left.unused || right.unused) { continue; }
if (left.hack && !right.hack && !right.important || !left.hack && !left.important && right.hack) { continue; }
if (left.important == right.important && left.hack[0] != right.hack[0]) { continue; }
if (left.important == right.important
&& (left.hack[0] != right.hack[0] || (left.hack[1] && left.hack[1] != right.hack[1]))) { continue; }
if (hasInherit(right)) { continue; }
if (noneOverrideHack(left, right)) { continue; }
if (right.shorthand && isComponentOf(right, left)) {
// maybe `left` can be overridden by `right` which is a shorthand?
if (!right.important && left.important) { continue; }
if (!sameVendorPrefixesIn([left], right.components)) { continue; }
if (!anyValue(validator.isFunction, left) && overridingFunction(right, validator)) { continue; }
if (!isMergeableShorthand(right)) {
left.unused = true;
continue;
}
component = findComponentIn(right, left);
mayOverride = configuration[left.name].canOverride || sameValue;
if (everyValuesPair(mayOverride.bind(null, validator), left, component)) {
left.unused = true;
}
} else if (right.shorthand && overridesNonComponentShorthand(right, left)) {
// `right` is a shorthand while `left` can be overriden by it, think `border` and `border-top`
if (!right.important && left.important) {
continue;
}
if (!sameVendorPrefixesIn([left], right.components)) {
continue;
}
if (!anyValue(validator.isFunction, left) && overridingFunction(right, validator)) {
continue;
}
overriddenComponents = left.shorthand
? left.components
: [left];
for (k = overriddenComponents.length - 1; k >= 0; k--) {
overriddenComponent = overriddenComponents[k];
overridingComponent = findComponentIn(right, overriddenComponent);
mayOverride = configuration[overriddenComponent.name].canOverride || sameValue;
if (!everyValuesPair(mayOverride.bind(null, validator), left, overridingComponent)) {
continue traverseLoop;
}
}
left.unused = true;
} else if (withMerging && left.shorthand && !right.shorthand && isComponentOf(left, right, true)) {
// maybe `right` can be pulled into `left` which is a shorthand?
if (right.important && !left.important) { continue; }
if (!right.important && left.important) {
right.unused = true;
continue;
}
// Pending more clever algorithm in #527
if (moreSameShorthands(properties, i - 1, left.name)) { continue; }
if (overridingFunction(left, validator)) { continue; }
if (!isMergeableShorthand(left)) { continue; }
if (hasUnset(left) || hasUnset(right)) { continue; }
component = findComponentIn(left, right);
if (everyValuesPair(mayOverride.bind(null, validator), component, right)) {
var disabledBackgroundMerging = !compatibility.properties.backgroundClipMerging && component.name.indexOf('background-clip') > -1
|| !compatibility.properties.backgroundOriginMerging && component.name.indexOf('background-origin') > -1
|| !compatibility.properties.backgroundSizeMerging && component.name.indexOf('background-size') > -1;
var nonMergeableValue = configuration[right.name].nonMergeableValue === right.value[0][1];
if (disabledBackgroundMerging || nonMergeableValue) { continue; }
if (!compatibility.properties.merging && wouldBreakCompatibility(left, validator)) { continue; }
if (component.value[0][1] != right.value[0][1] && (hasInherit(left) || hasInherit(right))) { continue; }
if (wouldResultInLongerValue(left, right)) { continue; }
if (!left.multiplex && right.multiplex) { turnIntoMultiplex(left, multiplexSize(right)); }
override(component, right);
left.dirty = true;
}
} else if (withMerging && left.shorthand && right.shorthand && left.name == right.name) {
// merge if all components can be merged
if (!left.multiplex && right.multiplex) { continue; }
if (!right.important && left.important) {
right.unused = true;
continue propertyLoop;
}
if (right.important && !left.important) {
left.unused = true;
continue;
}
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];
mayOverride = configuration[leftComponent.name].canOverride || sameValue;
if (!everyValuesPair(mayOverride.bind(null, validator), leftComponent, rightComponent)) {
continue propertyLoop;
}
}
overrideShorthand(left, right);
left.dirty = true;
} else if (withMerging && left.shorthand && right.shorthand && isComponentOf(left, right)) {
// border is a shorthand but any of its components is a shorthand too
if (!left.important && right.important) { continue; }
component = findComponentIn(left, right);
mayOverride = configuration[right.name].canOverride || sameValue;
if (!everyValuesPair(mayOverride.bind(null, validator), component, right)) { continue; }
if (left.important && !right.important) {
right.unused = true;
continue;
}
var rightRestored = configuration[right.name].restore(right, configuration);
if (rightRestored.length > 1) { continue; }
component = findComponentIn(left, right);
override(component, right);
right.dirty = true;
} else if (left.name == right.name) {
// two non-shorthands should be merged based on understandability
overridable = true;
if (right.shorthand) {
for (k = right.components.length - 1; k >= 0 && overridable; k--) {
overriddenComponent = left.components[k];
overridingComponent = right.components[k];
mayOverride = configuration[overridingComponent.name].canOverride || sameValue;
overridable = everyValuesPair(mayOverride.bind(null, validator), overriddenComponent, overridingComponent);
}
} else {
mayOverride = configuration[right.name].canOverride || sameValue;
overridable = everyValuesPair(mayOverride.bind(null, validator), left, right);
}
if (left.important && !right.important && overridable) {
right.unused = true;
continue;
}
if (!left.important && right.important && overridable) {
left.unused = true;
continue;
}
if (!overridable) {
continue;
}
left.unused = true;
}
}
}
}
module.exports = overrideProperties;

View File

@ -0,0 +1,9 @@
var configuration = require('../../configuration');
function overridesNonComponentShorthand(property1, property2) {
return property1.name in configuration
&& 'overridesShorthands' in configuration[property1.name]
&& configuration[property1.name].overridesShorthands.indexOf(property2.name) > -1;
}
module.exports = overridesNonComponentShorthand;

View File

@ -0,0 +1,61 @@
var configuration = require('../../configuration');
var InvalidPropertyError = require('../../invalid-property-error');
function populateComponents(properties, validator, warnings) {
var component;
var j, m;
for (var i = properties.length - 1; i >= 0; i--) {
var property = properties[i];
var descriptor = configuration[property.name];
if (!property.dynamic && descriptor && descriptor.shorthand) {
if (onlyValueIsVariable(property, validator) || moreThanOneValueIsVariable(property, validator)) {
property.optimizable = false;
continue;
}
property.shorthand = true;
property.dirty = true;
try {
property.components = descriptor.breakUp(property, configuration, validator);
if (descriptor.shorthandComponents) {
for (j = 0, m = property.components.length; j < m; j++) {
component = property.components[j];
component.components = configuration[component.name].breakUp(component, configuration, validator);
}
}
} catch (e) {
if (e instanceof InvalidPropertyError) {
property.components = []; // this will set property.unused to true below
warnings.push(e.message);
} else {
throw e;
}
}
if (property.components.length > 0) {
property.multiplex = property.components[0].multiplex;
} else {
property.unused = true;
}
}
}
}
function onlyValueIsVariable(property, validator) {
return property.value.length == 1 && validator.isVariable(property.value[0][1]);
}
function moreThanOneValueIsVariable(property, validator) {
return property.value.length > 1
&& property.value.filter(
function(value) {
return validator.isVariable(value[1]);
}
).length > 1;
}
module.exports = populateComponents;

View File

@ -0,0 +1,174 @@
var isMergeable = require('./is-mergeable');
var optimizeProperties = require('./properties/optimize');
var cloneArray = require('../../utils/clone-array');
var Token = require('../../tokenizer/token');
var serializeBody = require('../../writer/one-time').body;
var serializeRules = require('../../writer/one-time').rules;
function reduceNonAdjacent(tokens, context) {
var options = context.options;
var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses;
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements;
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging;
var candidates = {};
var repeated = [];
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
if (token[0] != Token.RULE) {
continue;
} else if (token[2].length === 0) {
continue;
}
var selectorAsString = serializeRules(token[1]);
var isComplexAndNotSpecial = token[1].length > 1
&& isMergeable(selectorAsString, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging);
var wrappedSelectors = wrappedSelectorsFrom(token[1]);
var selectors = isComplexAndNotSpecial
? [selectorAsString].concat(wrappedSelectors)
: [selectorAsString];
for (var j = 0, m = selectors.length; j < m; j++) {
var selector = selectors[j];
if (!candidates[selector]) { candidates[selector] = []; } else { repeated.push(selector); }
candidates[selector].push({
where: i,
list: wrappedSelectors,
isPartial: isComplexAndNotSpecial && j > 0,
isComplex: isComplexAndNotSpecial && j === 0
});
}
}
reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, context);
reduceComplexNonAdjacentCases(tokens, candidates, options, context);
}
function wrappedSelectorsFrom(list) {
var wrapped = [];
for (var i = 0; i < list.length; i++) {
wrapped.push([list[i][1]]);
}
return wrapped;
}
function reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, context) {
function filterOut(idx, bodies) {
return data[idx].isPartial && bodies.length === 0;
}
function reduceBody(token, newBody, processedCount, tokenIdx) {
if (!data[processedCount - tokenIdx - 1].isPartial) { token[2] = newBody; }
}
for (var i = 0, l = repeated.length; i < l; i++) {
var selector = repeated[i];
var data = candidates[selector];
reduceSelector(tokens, data, {
filterOut: filterOut,
callback: reduceBody
}, options, context);
}
}
function reduceComplexNonAdjacentCases(tokens, candidates, options, context) {
var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses;
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements;
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging;
var localContext = {};
function filterOut(idx) {
return localContext.data[idx].where < localContext.intoPosition;
}
function collectReducedBodies(token, newBody, processedCount, tokenIdx) {
if (tokenIdx === 0) { localContext.reducedBodies.push(newBody); }
}
allSelectors:
for (var complexSelector in candidates) {
var into = candidates[complexSelector];
if (!into[0].isComplex) { continue; }
var intoPosition = into[into.length - 1].where;
var intoToken = tokens[intoPosition];
var reducedBodies = [];
var selectors = isMergeable(complexSelector, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging)
? into[0].list
: [complexSelector];
localContext.intoPosition = intoPosition;
localContext.reducedBodies = reducedBodies;
for (var j = 0, m = selectors.length; j < m; j++) {
var selector = selectors[j];
var data = candidates[selector];
if (data.length < 2) { continue allSelectors; }
localContext.data = data;
reduceSelector(tokens, data, {
filterOut: filterOut,
callback: collectReducedBodies
}, options, context);
if (serializeBody(reducedBodies[reducedBodies.length - 1]) != serializeBody(reducedBodies[0])) {
continue allSelectors;
}
}
intoToken[2] = reducedBodies[0];
}
}
function reduceSelector(tokens, data, context, options, outerContext) {
var bodies = [];
var bodiesAsList = [];
var processedTokens = [];
for (var j = data.length - 1; j >= 0; j--) {
if (context.filterOut(j, bodies)) { continue; }
var where = data[j].where;
var token = tokens[where];
var clonedBody = cloneArray(token[2]);
bodies = bodies.concat(clonedBody);
bodiesAsList.push(clonedBody);
processedTokens.push(where);
}
optimizeProperties(bodies, true, false, outerContext);
var processedCount = processedTokens.length;
var propertyIdx = bodies.length - 1;
var tokenIdx = processedCount - 1;
while (tokenIdx >= 0) {
if ((tokenIdx === 0
|| (bodies[propertyIdx] && bodiesAsList[tokenIdx].indexOf(bodies[propertyIdx]) > -1)) && propertyIdx > -1) {
propertyIdx--;
continue;
}
var newBody = bodies.splice(propertyIdx + 1);
context.callback(tokens[processedTokens[tokenIdx]], newBody, processedCount, tokenIdx);
tokenIdx--;
}
}
module.exports = reduceNonAdjacent;

View File

@ -0,0 +1,30 @@
var Token = require('../../tokenizer/token');
var serializeAll = require('../../writer/one-time').all;
var FONT_FACE_SCOPE = '@font-face';
function removeDuplicateFontAtRules(tokens) {
var fontAtRules = [];
var token;
var key;
var i, l;
for (i = 0, l = tokens.length; i < l; i++) {
token = tokens[i];
if (token[0] != Token.AT_RULE_BLOCK && token[1][0][1] != FONT_FACE_SCOPE) {
continue;
}
key = serializeAll([token]);
if (fontAtRules.indexOf(key) > -1) {
token[2] = [];
} else {
fontAtRules.push(key);
}
}
}
module.exports = removeDuplicateFontAtRules;

View File

@ -0,0 +1,30 @@
var Token = require('../../tokenizer/token');
var serializeAll = require('../../writer/one-time').all;
var serializeRules = require('../../writer/one-time').rules;
function removeDuplicateMediaQueries(tokens) {
var candidates = {};
var candidate;
var token;
var key;
var i, l;
for (i = 0, l = tokens.length; i < l; i++) {
token = tokens[i];
if (token[0] != Token.NESTED_BLOCK) {
continue;
}
key = serializeRules(token[1]) + '%' + serializeAll(token[2]);
candidate = candidates[key];
if (candidate) {
candidate[2] = [];
}
candidates[key] = token;
}
}
module.exports = removeDuplicateMediaQueries;

View File

@ -0,0 +1,36 @@
var Token = require('../../tokenizer/token');
var serializeBody = require('../../writer/one-time').body;
var serializeRules = require('../../writer/one-time').rules;
function removeDuplicates(tokens) {
var matched = {};
var moreThanOnce = [];
var id, token;
var body, bodies;
for (var i = 0, l = tokens.length; i < l; i++) {
token = tokens[i];
if (token[0] != Token.RULE) { continue; }
id = serializeRules(token[1]);
if (matched[id] && matched[id].length == 1) { moreThanOnce.push(id); } else { matched[id] = matched[id] || []; }
matched[id].push(i);
}
for (i = 0, l = moreThanOnce.length; i < l; i++) {
id = moreThanOnce[i];
bodies = [];
for (var j = matched[id].length - 1; j >= 0; j--) {
token = tokens[matched[id][j]];
body = serializeBody(token[2]);
if (bodies.indexOf(body) > -1) { token[2] = []; } else { bodies.push(body); }
}
}
}
module.exports = removeDuplicates;

View File

@ -0,0 +1,249 @@
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 /;
var importantRegex = /\s{0,31}!important$/;
var optionalMatchingQuotesRegex = /^(['"]?)(.*)\1$/;
function normalize(value) {
return value
.replace(optionalMatchingQuotesRegex, '$2')
.replace(importantRegex, '');
}
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 atRuleTokens;
var atRuleToken;
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) {
atRuleTokens = atRules[atRule];
for (i = 0, l = atRuleTokens.length; i < l; i++) {
atRuleToken = atRuleTokens[i];
zeroAt = atRuleToken[0] == Token.AT_RULE ? 1 : 2;
atRuleToken[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] = atRules[match] || [];
atRules[match].push(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 = normalize(property[2][1].toLowerCase());
atRules[match] = atRules[match] || [];
atRules[match].push(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 = normalize(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 = normalize(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] = atRules[match] || [];
atRules[match].push(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] = atRules[match] || [];
atRules[match].push(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;

View File

@ -0,0 +1,93 @@
// TODO: it'd be great to merge it with the other canReorder functionality
var rulesOverlap = require('./rules-overlap');
var specificitiesOverlap = require('./specificities-overlap');
var FLEX_PROPERTIES = /align-items|box-align|box-pack|flex|justify/;
var BORDER_PROPERTIES = /^border-(top|right|bottom|left|color|style|width|radius)/;
function canReorder(left, right, cache) {
for (var i = right.length - 1; i >= 0; i--) {
for (var j = left.length - 1; j >= 0; j--) {
if (!canReorderSingle(left[j], right[i], cache)) { return false; }
}
}
return true;
}
function canReorderSingle(left, right, cache) {
var leftName = left[0];
var leftValue = left[1];
var leftNameRoot = left[2];
var leftSelector = left[5];
var leftInSpecificSelector = left[6];
var rightName = right[0];
var rightValue = right[1];
var rightNameRoot = right[2];
var rightSelector = right[5];
var rightInSpecificSelector = right[6];
if (leftName == 'font' && rightName == 'line-height' || rightName == 'font' && leftName == 'line-height') { return false; }
if (FLEX_PROPERTIES.test(leftName) && FLEX_PROPERTIES.test(rightName)) { return false; }
if (leftNameRoot == rightNameRoot
&& unprefixed(leftName) == unprefixed(rightName)
&& (vendorPrefixed(leftName) ^ vendorPrefixed(rightName))) { return false; }
if (leftNameRoot == 'border' && BORDER_PROPERTIES.test(rightNameRoot) && (leftName == 'border' || leftName == rightNameRoot || (leftValue != rightValue && sameBorderComponent(leftName, rightName)))) { return false; }
if (rightNameRoot == 'border' && BORDER_PROPERTIES.test(leftNameRoot) && (rightName == 'border' || rightName == leftNameRoot || (leftValue != rightValue && sameBorderComponent(leftName, rightName)))) { return false; }
if (leftNameRoot == 'border' && rightNameRoot == 'border' && leftName != rightName && (isSideBorder(leftName) && isStyleBorder(rightName) || isStyleBorder(leftName) && isSideBorder(rightName))) { return false; }
if (leftNameRoot != rightNameRoot) { return true; }
if (leftName == rightName
&& leftNameRoot == rightNameRoot
&& (leftValue == rightValue || withDifferentVendorPrefix(leftValue, rightValue))) { return true; }
if (leftName != rightName
&& leftNameRoot == rightNameRoot
&& leftName != leftNameRoot
&& rightName != rightNameRoot) { return true; }
if (leftName != rightName
&& leftNameRoot == rightNameRoot
&& leftValue == rightValue) { return true; }
if (rightInSpecificSelector
&& leftInSpecificSelector
&& !inheritable(leftNameRoot)
&& !inheritable(rightNameRoot)
&& !rulesOverlap(rightSelector, leftSelector, false)) { return true; }
if (!specificitiesOverlap(leftSelector, rightSelector, cache)) { return true; }
return false;
}
function vendorPrefixed(name) {
return /^-(?:moz|webkit|ms|o)-/.test(name);
}
function unprefixed(name) {
return name.replace(/^-(?:moz|webkit|ms|o)-/, '');
}
function sameBorderComponent(name1, name2) {
return name1.split('-').pop() == name2.split('-').pop();
}
function isSideBorder(name) {
return name == 'border-top' || name == 'border-right' || name == 'border-bottom' || name == 'border-left';
}
function isStyleBorder(name) {
return name == 'border-color' || name == 'border-style' || name == 'border-width';
}
function withDifferentVendorPrefix(value1, value2) {
return vendorPrefixed(value1) && vendorPrefixed(value2) && value1.split('-')[1] != value2.split('-')[2];
}
function inheritable(name) {
// According to http://www.w3.org/TR/CSS21/propidx.html
// Others will be catched by other, preceeding rules
return name == 'font' || name == 'line-height' || name == 'list-style';
}
module.exports = {
canReorder: canReorder,
canReorderSingle: canReorderSingle
};

View File

@ -0,0 +1,12 @@
var configuration = require('../configuration');
function restoreWithComponents(property) {
var descriptor = configuration[property.name];
if (descriptor && descriptor.shorthand) {
return descriptor.restore(property, configuration);
}
return property.value;
}
module.exports = restoreWithComponents;

View File

@ -0,0 +1,392 @@
var canReorderSingle = require('./reorderable').canReorderSingle;
var extractProperties = require('./extract-properties');
var isMergeable = require('./is-mergeable');
var tidyRuleDuplicates = require('./tidy-rule-duplicates');
var Token = require('../../tokenizer/token');
var cloneArray = require('../../utils/clone-array');
var serializeBody = require('../../writer/one-time').body;
var serializeRules = require('../../writer/one-time').rules;
function naturalSorter(a, b) {
return a > b ? 1 : -1;
}
function cloneAndMergeSelectors(propertyA, propertyB) {
var cloned = cloneArray(propertyA);
cloned[5] = cloned[5].concat(propertyB[5]);
return cloned;
}
function restructure(tokens, context) {
var options = context.options;
var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses;
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements;
var mergeLimit = options.compatibility.selectors.mergeLimit;
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging;
var specificityCache = context.cache.specificity;
var movableTokens = {};
var movedProperties = [];
var multiPropertyMoveCache = {};
var movedToBeDropped = [];
var maxCombinationsLevel = 2;
var ID_JOIN_CHARACTER = '%';
function sendToMultiPropertyMoveCache(position, movedProperty, allFits) {
for (var i = allFits.length - 1; i >= 0; i--) {
var fit = allFits[i][0];
var id = addToCache(movedProperty, fit);
if (multiPropertyMoveCache[id].length > 1 && processMultiPropertyMove(position, multiPropertyMoveCache[id])) {
removeAllMatchingFromCache(id);
break;
}
}
}
function addToCache(movedProperty, fit) {
var id = cacheId(fit);
multiPropertyMoveCache[id] = multiPropertyMoveCache[id] || [];
multiPropertyMoveCache[id].push([movedProperty, fit]);
return id;
}
function removeAllMatchingFromCache(matchId) {
var matchSelectors = matchId.split(ID_JOIN_CHARACTER);
var forRemoval = [];
var i;
for (var id in multiPropertyMoveCache) {
var selectors = id.split(ID_JOIN_CHARACTER);
for (i = selectors.length - 1; i >= 0; i--) {
if (matchSelectors.indexOf(selectors[i]) > -1) {
forRemoval.push(id);
break;
}
}
}
for (i = forRemoval.length - 1; i >= 0; i--) {
delete multiPropertyMoveCache[forRemoval[i]];
}
}
function cacheId(cachedTokens) {
var id = [];
for (var i = 0, l = cachedTokens.length; i < l; i++) {
id.push(serializeRules(cachedTokens[i][1]));
}
return id.join(ID_JOIN_CHARACTER);
}
function tokensToMerge(sourceTokens) {
var uniqueTokensWithBody = [];
var mergeableTokens = [];
for (var i = sourceTokens.length - 1; i >= 0; i--) {
if (!isMergeable(
serializeRules(sourceTokens[i][1]),
mergeablePseudoClasses,
mergeablePseudoElements,
multiplePseudoMerging
)) {
continue;
}
mergeableTokens.unshift(sourceTokens[i]);
if (sourceTokens[i][2].length > 0
&& uniqueTokensWithBody.indexOf(sourceTokens[i]) == -1) {
uniqueTokensWithBody.push(sourceTokens[i]);
}
}
return uniqueTokensWithBody.length > 1
? mergeableTokens
: [];
}
function shortenIfPossible(position, movedProperty) {
var name = movedProperty[0];
var value = movedProperty[1];
var key = movedProperty[4];
var valueSize = name.length + value.length + 1;
var allSelectors = [];
var qualifiedTokens = [];
var mergeableTokens = tokensToMerge(movableTokens[key]);
if (mergeableTokens.length < 2) { return; }
var allFits = findAllFits(mergeableTokens, valueSize, 1);
var bestFit = allFits[0];
if (bestFit[1] > 0) { return sendToMultiPropertyMoveCache(position, movedProperty, allFits); }
for (var i = bestFit[0].length - 1; i >= 0; i--) {
allSelectors = bestFit[0][i][1].concat(allSelectors);
qualifiedTokens.unshift(bestFit[0][i]);
}
allSelectors = tidyRuleDuplicates(allSelectors);
dropAsNewTokenAt(position, [movedProperty], allSelectors, qualifiedTokens);
}
function fitSorter(fit1, fit2) {
return fit1[1] > fit2[1] ? 1 : (fit1[1] == fit2[1] ? 0 : -1);
}
function findAllFits(mergeableTokens, propertySize, propertiesCount) {
var combinations = allCombinations(mergeableTokens, propertySize, propertiesCount, maxCombinationsLevel - 1);
return combinations.sort(fitSorter);
}
function allCombinations(tokensVariant, propertySize, propertiesCount, level) {
var differenceVariants = [[tokensVariant, sizeDifference(tokensVariant, propertySize, propertiesCount)]];
if (tokensVariant.length > 2 && level > 0) {
for (var i = tokensVariant.length - 1; i >= 0; i--) {
var subVariant = Array.prototype.slice.call(tokensVariant, 0);
subVariant.splice(i, 1);
differenceVariants = differenceVariants.concat(
allCombinations(subVariant, propertySize, propertiesCount, level - 1)
);
}
}
return differenceVariants;
}
function sizeDifference(tokensVariant, propertySize, propertiesCount) {
var allSelectorsSize = 0;
for (var i = tokensVariant.length - 1; i >= 0; i--) {
allSelectorsSize += tokensVariant[i][2].length > propertiesCount
? serializeRules(tokensVariant[i][1]).length
: -1;
}
return allSelectorsSize - (tokensVariant.length - 1) * propertySize + 1;
}
function dropAsNewTokenAt(position, properties, allSelectors, mergeableTokens) {
var i, j, k, m;
var allProperties = [];
for (i = mergeableTokens.length - 1; i >= 0; i--) {
var mergeableToken = mergeableTokens[i];
for (j = mergeableToken[2].length - 1; j >= 0; j--) {
var mergeableProperty = mergeableToken[2][j];
for (k = 0, m = properties.length; k < m; k++) {
var property = properties[k];
var mergeablePropertyName = mergeableProperty[1][1];
var propertyName = property[0];
var propertyBody = property[4];
if (mergeablePropertyName == propertyName && serializeBody([mergeableProperty]) == propertyBody) {
mergeableToken[2].splice(j, 1);
break;
}
}
}
}
for (i = properties.length - 1; i >= 0; i--) {
allProperties.unshift(properties[i][3]);
}
var newToken = [Token.RULE, allSelectors, allProperties];
tokens.splice(position, 0, newToken);
}
function dropPropertiesAt(position, movedProperty) {
var key = movedProperty[4];
var toMove = movableTokens[key];
if (toMove && toMove.length > 1) {
if (!shortenMultiMovesIfPossible(position, movedProperty)) { shortenIfPossible(position, movedProperty); }
}
}
function shortenMultiMovesIfPossible(position, movedProperty) {
var candidates = [];
var propertiesAndMergableTokens = [];
var key = movedProperty[4];
var j, k;
var mergeableTokens = tokensToMerge(movableTokens[key]);
if (mergeableTokens.length < 2) { return; }
movableLoop:
for (var value in movableTokens) {
var tokensList = movableTokens[value];
for (j = mergeableTokens.length - 1; j >= 0; j--) {
if (tokensList.indexOf(mergeableTokens[j]) == -1) { continue movableLoop; }
}
candidates.push(value);
}
if (candidates.length < 2) { return false; }
for (j = candidates.length - 1; j >= 0; j--) {
for (k = movedProperties.length - 1; k >= 0; k--) {
if (movedProperties[k][4] == candidates[j]) {
propertiesAndMergableTokens.unshift([movedProperties[k], mergeableTokens]);
break;
}
}
}
return processMultiPropertyMove(position, propertiesAndMergableTokens);
}
function processMultiPropertyMove(position, propertiesAndMergableTokens) {
var valueSize = 0;
var properties = [];
var property;
for (var i = propertiesAndMergableTokens.length - 1; i >= 0; i--) {
property = propertiesAndMergableTokens[i][0];
var fullValue = property[4];
valueSize += fullValue.length + (i > 0 ? 1 : 0);
properties.push(property);
}
var mergeableTokens = propertiesAndMergableTokens[0][1];
var bestFit = findAllFits(mergeableTokens, valueSize, properties.length)[0];
if (bestFit[1] > 0) { return false; }
var allSelectors = [];
var qualifiedTokens = [];
for (i = bestFit[0].length - 1; i >= 0; i--) {
allSelectors = bestFit[0][i][1].concat(allSelectors);
qualifiedTokens.unshift(bestFit[0][i]);
}
allSelectors = tidyRuleDuplicates(allSelectors);
dropAsNewTokenAt(position, properties, allSelectors, qualifiedTokens);
for (i = properties.length - 1; i >= 0; i--) {
property = properties[i];
var index = movedProperties.indexOf(property);
delete movableTokens[property[4]];
if (index > -1 && movedToBeDropped.indexOf(index) == -1) { movedToBeDropped.push(index); }
}
return true;
}
function boundToAnotherPropertyInCurrrentToken(property, movedProperty, token) {
var propertyName = property[0];
var movedPropertyName = movedProperty[0];
if (propertyName != movedPropertyName) { return false; }
var key = movedProperty[4];
var toMove = movableTokens[key];
return toMove && toMove.indexOf(token) > -1;
}
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
var isRule;
var j, k, m;
var samePropertyAt;
if (token[0] == Token.RULE) {
isRule = true;
} else if (token[0] == Token.NESTED_BLOCK) {
isRule = false;
} else {
continue;
}
// We cache movedProperties.length as it may change in the loop
var movedCount = movedProperties.length;
var properties = extractProperties(token);
movedToBeDropped = [];
var unmovableInCurrentToken = [];
for (j = properties.length - 1; j >= 0; j--) {
for (k = j - 1; k >= 0; k--) {
if (!canReorderSingle(properties[j], properties[k], specificityCache)) {
unmovableInCurrentToken.push(j);
break;
}
}
}
for (j = properties.length - 1; j >= 0; j--) {
var property = properties[j];
var movedSameProperty = false;
for (k = 0; k < movedCount; k++) {
var movedProperty = movedProperties[k];
if (movedToBeDropped.indexOf(k) == -1 && (
!canReorderSingle(property, movedProperty, specificityCache)
&& !boundToAnotherPropertyInCurrrentToken(property, movedProperty, token)
|| movableTokens[movedProperty[4]] && movableTokens[movedProperty[4]].length === mergeLimit)
) {
dropPropertiesAt(i + 1, movedProperty);
if (movedToBeDropped.indexOf(k) == -1) {
movedToBeDropped.push(k);
delete movableTokens[movedProperty[4]];
}
}
if (!movedSameProperty) {
movedSameProperty = property[0] == movedProperty[0] && property[1] == movedProperty[1];
if (movedSameProperty) {
samePropertyAt = k;
}
}
}
if (!isRule || unmovableInCurrentToken.indexOf(j) > -1) { continue; }
var key = property[4];
if (movedSameProperty && movedProperties[samePropertyAt][5].length + property[5].length > mergeLimit) {
dropPropertiesAt(i + 1, movedProperties[samePropertyAt]);
movedProperties.splice(samePropertyAt, 1);
movableTokens[key] = [token];
movedSameProperty = false;
} else {
movableTokens[key] = movableTokens[key] || [];
movableTokens[key].push(token);
}
if (movedSameProperty) {
movedProperties[samePropertyAt] = cloneAndMergeSelectors(movedProperties[samePropertyAt], property);
} else {
movedProperties.push(property);
}
}
movedToBeDropped = movedToBeDropped.sort(naturalSorter);
for (j = 0, m = movedToBeDropped.length; j < m; j++) {
var dropAt = movedToBeDropped[j] - j;
movedProperties.splice(dropAt, 1);
}
}
var position = tokens[0] && tokens[0][0] == Token.AT_RULE && tokens[0][1].indexOf('@charset') === 0 ? 1 : 0;
for (; position < tokens.length - 1; position++) {
var isImportRule = tokens[position][0] === Token.AT_RULE && tokens[position][1].indexOf('@import') === 0;
var isComment = tokens[position][0] === Token.COMMENT;
if (!(isImportRule || isComment)) { break; }
}
for (i = 0; i < movedProperties.length; i++) {
dropPropertiesAt(position, movedProperties[i]);
}
}
module.exports = restructure;

View File

@ -0,0 +1,32 @@
var MODIFIER_PATTERN = /--.+$/;
function rulesOverlap(rule1, rule2, bemMode) {
var scope1;
var scope2;
var i, l;
var j, m;
for (i = 0, l = rule1.length; i < l; i++) {
scope1 = rule1[i][1];
for (j = 0, m = rule2.length; j < m; j++) {
scope2 = rule2[j][1];
if (scope1 == scope2) {
return true;
}
if (bemMode && withoutModifiers(scope1) == withoutModifiers(scope2)) {
return true;
}
}
}
return false;
}
function withoutModifiers(scope) {
return scope.replace(MODIFIER_PATTERN, '');
}
module.exports = rulesOverlap;

View File

@ -0,0 +1,36 @@
var specificity = require('./specificity');
function specificitiesOverlap(selector1, selector2, cache) {
var specificity1;
var specificity2;
var i, l;
var j, m;
for (i = 0, l = selector1.length; i < l; i++) {
specificity1 = findSpecificity(selector1[i][1], cache);
for (j = 0, m = selector2.length; j < m; j++) {
specificity2 = findSpecificity(selector2[j][1], cache);
if (specificity1[0] === specificity2[0]
&& specificity1[1] === specificity2[1]
&& specificity1[2] === specificity2[2]) {
return true;
}
}
}
return false;
}
function findSpecificity(selector, cache) {
var value;
if (!(selector in cache)) {
cache[selector] = value = specificity(selector);
}
return value || cache[selector];
}
module.exports = specificitiesOverlap;

View File

@ -0,0 +1,77 @@
var Marker = require('../../tokenizer/marker');
var Selector = {
ADJACENT_SIBLING: '+',
DESCENDANT: '>',
DOT: '.',
HASH: '#',
NON_ADJACENT_SIBLING: '~',
PSEUDO: ':'
};
var LETTER_PATTERN = /[a-zA-Z]/;
var NOT_PREFIX = ':not(';
var SEPARATOR_PATTERN = /[\s,(>~+]/;
function specificity(selector) {
var result = [0, 0, 0];
var character;
var isEscaped;
var isSingleQuoted;
var isDoubleQuoted;
var roundBracketLevel = 0;
var couldIntroduceNewTypeSelector;
var withinNotPseudoClass = false;
var wasPseudoClass = false;
var i, l;
for (i = 0, l = selector.length; i < l; i++) {
character = selector[i];
if (isEscaped) {
// noop
} else if (character == Marker.SINGLE_QUOTE && !isDoubleQuoted && !isSingleQuoted) {
isSingleQuoted = true;
} else if (character == Marker.SINGLE_QUOTE && !isDoubleQuoted && isSingleQuoted) {
isSingleQuoted = false;
} else if (character == Marker.DOUBLE_QUOTE && !isDoubleQuoted && !isSingleQuoted) {
isDoubleQuoted = true;
} else if (character == Marker.DOUBLE_QUOTE && isDoubleQuoted && !isSingleQuoted) {
isDoubleQuoted = false;
} else if (isSingleQuoted || isDoubleQuoted) {
continue;
} else if (roundBracketLevel > 0 && !withinNotPseudoClass) {
// noop
} else if (character == Marker.OPEN_ROUND_BRACKET) {
roundBracketLevel++;
} else if (character == Marker.CLOSE_ROUND_BRACKET && roundBracketLevel == 1) {
roundBracketLevel--;
withinNotPseudoClass = false;
} else if (character == Marker.CLOSE_ROUND_BRACKET) {
roundBracketLevel--;
} else if (character == Selector.HASH) {
result[0]++;
} else if (character == Selector.DOT || character == Marker.OPEN_SQUARE_BRACKET) {
result[1]++;
} else if (character == Selector.PSEUDO && !wasPseudoClass && !isNotPseudoClass(selector, i)) {
result[1]++;
withinNotPseudoClass = false;
} else if (character == Selector.PSEUDO) {
withinNotPseudoClass = true;
} else if ((i === 0 || couldIntroduceNewTypeSelector) && LETTER_PATTERN.test(character)) {
result[2]++;
}
isEscaped = character == Marker.BACK_SLASH;
wasPseudoClass = character == Selector.PSEUDO;
couldIntroduceNewTypeSelector = !isEscaped && SEPARATOR_PATTERN.test(character);
}
return result;
}
function isNotPseudoClass(selector, index) {
return selector.indexOf(NOT_PREFIX, index) === index;
}
module.exports = specificity;

View File

@ -0,0 +1,21 @@
function ruleSorter(s1, s2) {
return s1[1] > s2[1] ? 1 : -1;
}
function tidyRuleDuplicates(rules) {
var list = [];
var repeated = [];
for (var i = 0, l = rules.length; i < l; i++) {
var rule = rules[i];
if (repeated.indexOf(rule[1]) == -1) {
repeated.push(rule[1]);
list.push(rule);
}
}
return list.sort(ruleSorter);
}
module.exports = tidyRuleDuplicates;

11
node_modules/clean-css/lib/optimizer/remove-unused.js generated vendored Normal file
View File

@ -0,0 +1,11 @@
function removeUnused(properties) {
for (var i = properties.length - 1; i >= 0; i--) {
var property = properties[i];
if (property.unused) {
property.all.splice(property.position, 1);
}
}
}
module.exports = removeUnused;

View File

@ -0,0 +1,78 @@
var Hack = require('./hack');
var Marker = require('../tokenizer/marker');
var ASTERISK_HACK = '*';
var BACKSLASH_HACK = '\\';
var IMPORTANT_TOKEN = '!important';
var UNDERSCORE_HACK = '_';
var BANG_HACK = '!ie';
function restoreFromOptimizing(properties, restoreCallback) {
var property;
var restored;
var current;
var i;
for (i = properties.length - 1; i >= 0; i--) {
property = properties[i];
if (property.dynamic && property.important) {
restoreImportant(property);
continue;
}
if (property.dynamic) {
continue;
}
if (property.unused) {
continue;
}
if (!property.dirty && !property.important && !property.hack) {
continue;
}
if (property.optimizable && restoreCallback) {
restored = restoreCallback(property);
property.value = restored;
} else {
restored = property.value;
}
if (property.important) {
restoreImportant(property);
}
if (property.hack) {
restoreHack(property);
}
if ('all' in property) {
current = property.all[property.position];
current[1][1] = property.name;
current.splice(2, current.length - 1);
Array.prototype.push.apply(current, restored);
}
}
}
function restoreImportant(property) {
property.value[property.value.length - 1][1] += IMPORTANT_TOKEN;
}
function restoreHack(property) {
if (property.hack[0] == Hack.UNDERSCORE) {
property.name = UNDERSCORE_HACK + property.name;
} else if (property.hack[0] == Hack.ASTERISK) {
property.name = ASTERISK_HACK + property.name;
} else if (property.hack[0] == Hack.BACKSLASH) {
property.value[property.value.length - 1][1] += BACKSLASH_HACK + property.hack[1];
} else if (property.hack[0] == Hack.BANG) {
property.value[property.value.length - 1][1] += Marker.SPACE + BANG_HACK;
}
}
module.exports = restoreFromOptimizing;

561
node_modules/clean-css/lib/optimizer/validator.js generated vendored Normal file
View File

@ -0,0 +1,561 @@
var functionNoVendorRegexStr = '[A-Z]+(\\-|[A-Z]|[0-9])+\\(.*?\\)';
var functionVendorRegexStr = '\\-(\\-|[A-Z]|[0-9])+\\(.*?\\)';
var variableRegexStr = 'var\\(\\-\\-[^\\)]+\\)';
var functionAnyRegexStr = '(' + variableRegexStr + '|' + functionNoVendorRegexStr + '|' + functionVendorRegexStr + ')';
var calcRegex = new RegExp('^(\\-moz\\-|\\-webkit\\-)?calc\\([^\\)]+\\)$', 'i');
var decimalRegex = /[0-9]/;
var functionAnyRegex = new RegExp('^' + functionAnyRegexStr + '$', 'i');
var hexAlphaColorRegex = /^#(?:[0-9a-f]{4}|[0-9a-f]{8})$/i;
// eslint-disable-next-line max-len
var hslColorRegex = /^hsl\(\s{0,31}[-.]?\d+\s{0,31},\s{0,31}\d*\.?\d+%\s{0,31},\s{0,31}\d*\.?\d+%\s{0,31}\)|hsla\(\s{0,31}[-.]?\d+\s{0,31},\s{0,31}\d*\.?\d+%\s{0,31},\s{0,31}\d*\.?\d+%\s{0,31},\s{0,31}\.?\d+\s{0,31}\)$/;
// eslint-disable-next-line max-len
var hslColorWithSpacesRegex = /^hsl\(\s{0,31}[-.]?\d+(deg)?\s{1,31}\d*\.?\d+%\s{1,31}\d*\.?\d+%\s{0,31}\)|hsla\(\s{0,31}[-.]?\d+(deg)?\s{1,31}\d*\.?\d+%\s{1,31}\d*\.?\d+%\s{1,31}\/\s{1,31}\d*\.?\d+%?\s{0,31}\)$/;
var identifierRegex = /^(-[a-z0-9_][a-z0-9\-_]*|[a-z_][a-z0-9\-_]*)$/i;
var namedEntityRegex = /^[a-z]+$/i;
var prefixRegex = /^-([a-z0-9]|-)*$/i;
var quotedTextRegex = /^("[^"]*"|'[^']*')$/i;
// eslint-disable-next-line max-len
var rgbColorRegex = /^rgb\(\s{0,31}[\d]{1,3}\s{0,31},\s{0,31}[\d]{1,3}\s{0,31},\s{0,31}[\d]{1,3}\s{0,31}\)|rgba\(\s{0,31}[\d]{1,3}\s{0,31},\s{0,31}[\d]{1,3}\s{0,31},\s{0,31}[\d]{1,3}\s{0,31},\s{0,31}[.\d]+\s{0,31}\)$/i;
// eslint-disable-next-line max-len
var rgbColorWithSpacesRegex = /^rgb\(\s{0,31}[\d]{1,3}\s{1,31}[\d]{1,3}\s{1,31}[\d]{1,3}\s{0,31}\)|rgba\(\s{0,31}[\d]{1,3}\s{1,31}[\d]{1,3}\s{1,31}[\d]{1,3}\s{1,31}\/\s{1,31}[\d]*\.?[.\d]+%?\s{0,31}\)$/i;
var timeUnitPattern = /\d+(s|ms)/;
var timingFunctionRegex = /^(cubic-bezier|steps)\([^)]+\)$/;
var validTimeUnits = ['ms', 's'];
var urlRegex = /^url\([\s\S]+\)$/i;
var variableRegex = new RegExp('^' + variableRegexStr + '$', 'i');
var eightValueColorRegex = /^#[0-9a-f]{8}$/i;
var fourValueColorRegex = /^#[0-9a-f]{4}$/i;
var sixValueColorRegex = /^#[0-9a-f]{6}$/i;
var threeValueColorRegex = /^#[0-9a-f]{3}$/i;
var DECIMAL_DOT = '.';
var MINUS_SIGN = '-';
var PLUS_SIGN = '+';
var Keywords = {
'^': [
'inherit',
'initial',
'unset'
],
'*-style': [
'auto',
'dashed',
'dotted',
'double',
'groove',
'hidden',
'inset',
'none',
'outset',
'ridge',
'solid'
],
'*-timing-function': [
'ease',
'ease-in',
'ease-in-out',
'ease-out',
'linear',
'step-end',
'step-start'
],
'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'
],
'background-attachment': [
'fixed',
'inherit',
'local',
'scroll'
],
'background-clip': [
'border-box',
'content-box',
'inherit',
'padding-box',
'text'
],
'background-origin': [
'border-box',
'content-box',
'inherit',
'padding-box'
],
'background-position': [
'bottom',
'center',
'left',
'right',
'top'
],
'background-repeat': [
'no-repeat',
'inherit',
'repeat',
'repeat-x',
'repeat-y',
'round',
'space'
],
'background-size': [
'auto',
'cover',
'contain'
],
'border-collapse': [
'collapse',
'inherit',
'separate'
],
bottom: [
'auto'
],
clear: [
'both',
'left',
'none',
'right'
],
color: [
'transparent'
],
cursor: [
'all-scroll',
'auto',
'col-resize',
'crosshair',
'default',
'e-resize',
'help',
'move',
'n-resize',
'ne-resize',
'no-drop',
'not-allowed',
'nw-resize',
'pointer',
'progress',
'row-resize',
's-resize',
'se-resize',
'sw-resize',
'text',
'vertical-text',
'w-resize',
'wait'
],
display: [
'block',
'inline',
'inline-block',
'inline-table',
'list-item',
'none',
'table',
'table-caption',
'table-cell',
'table-column',
'table-column-group',
'table-footer-group',
'table-header-group',
'table-row',
'table-row-group'
],
float: [
'left',
'none',
'right'
],
left: [
'auto'
],
font: [
'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',
'300',
'400',
'500',
'600',
'700',
'800',
'900',
'bold',
'bolder',
'lighter',
'normal'
],
'line-height': [
'normal'
],
'list-style-position': [
'inside',
'outside'
],
'list-style-type': [
'armenian',
'circle',
'decimal',
'decimal-leading-zero',
'disc',
'decimal|disc', // this is the default value of list-style-type, see comment in configuration.js
'georgian',
'lower-alpha',
'lower-greek',
'lower-latin',
'lower-roman',
'none',
'square',
'upper-alpha',
'upper-latin',
'upper-roman'
],
overflow: [
'auto',
'hidden',
'scroll',
'visible'
],
position: [
'absolute',
'fixed',
'relative',
'static'
],
right: [
'auto'
],
'text-align': [
'center',
'justify',
'left',
'left|right', // this is the default value of list-style-type, see comment in configuration.js
'right'
],
'text-decoration': [
'line-through',
'none',
'overline',
'underline'
],
'text-overflow': [
'clip',
'ellipsis'
],
top: [
'auto'
],
'vertical-align': [
'baseline',
'bottom',
'middle',
'sub',
'super',
'text-bottom',
'text-top',
'top'
],
visibility: [
'collapse',
'hidden',
'visible'
],
'white-space': [
'normal',
'nowrap',
'pre'
],
width: [
'inherit',
'initial',
'medium',
'thick',
'thin'
]
};
var Units = [
'%',
'ch',
'cm',
'em',
'ex',
'in',
'mm',
'pc',
'pt',
'px',
'rem',
'vh',
'vm',
'vmax',
'vmin',
'vw'
];
function isColor(value) {
return value != 'auto'
&& (
isKeyword('color')(value)
|| isHexColor(value)
|| isColorFunction(value)
|| isNamedEntity(value)
);
}
function isColorFunction(value) {
return isRgbColor(value) || isHslColor(value);
}
function isDynamicUnit(value) {
return calcRegex.test(value);
}
function isFunction(value) {
return functionAnyRegex.test(value);
}
function isHexColor(value) {
return threeValueColorRegex.test(value)
|| fourValueColorRegex.test(value)
|| sixValueColorRegex.test(value)
|| eightValueColorRegex.test(value);
}
function isHslColor(value) {
return hslColorRegex.test(value) || hslColorWithSpacesRegex.test(value);
}
function isHexAlphaColor(value) {
return hexAlphaColorRegex.test(value);
}
function isIdentifier(value) {
return identifierRegex.test(value);
}
function isQuotedText(value) {
return quotedTextRegex.test(value);
}
function isImage(value) {
return value == 'none' || value == 'inherit' || isUrl(value);
}
function isKeyword(propertyName) {
return function(value) {
return Keywords[propertyName].indexOf(value) > -1;
};
}
function isNamedEntity(value) {
return namedEntityRegex.test(value);
}
function isNumber(value) {
return scanForNumber(value) == value.length;
}
function isRgbColor(value) {
return rgbColorRegex.test(value) || rgbColorWithSpacesRegex.test(value);
}
function isPrefixed(value) {
return prefixRegex.test(value);
}
function isPositiveNumber(value) {
return isNumber(value)
&& parseFloat(value) >= 0;
}
function isVariable(value) {
return variableRegex.test(value);
}
function isTime(value) {
var numberUpTo = scanForNumber(value);
return numberUpTo == value.length && parseInt(value) === 0
|| numberUpTo > -1 && validTimeUnits.indexOf(value.slice(numberUpTo + 1)) > -1
|| isCalculatedTime(value);
}
function isCalculatedTime(value) {
return isFunction(value) && timeUnitPattern.test(value);
}
function isTimingFunction() {
var isTimingFunctionKeyword = isKeyword('*-timing-function');
return function(value) {
return isTimingFunctionKeyword(value) || timingFunctionRegex.test(value);
};
}
function isUnit(validUnits, value) {
var numberUpTo = scanForNumber(value);
return numberUpTo == value.length && parseInt(value) === 0
|| numberUpTo > -1 && validUnits.indexOf(value.slice(numberUpTo + 1).toLowerCase()) > -1
|| value == 'auto'
|| value == 'inherit';
}
function isUrl(value) {
return urlRegex.test(value);
}
function isZIndex(value) {
return value == 'auto'
|| isNumber(value)
|| isKeyword('^')(value);
}
function scanForNumber(value) {
var hasDot = false;
var hasSign = false;
var character;
var i, l;
for (i = 0, l = value.length; i < l; i++) {
character = value[i];
if (i === 0 && (character == PLUS_SIGN || character == MINUS_SIGN)) {
hasSign = true;
} else if (i > 0 && hasSign && (character == PLUS_SIGN || character == MINUS_SIGN)) {
return i - 1;
} else if (character == DECIMAL_DOT && !hasDot) {
hasDot = true;
} else if (character == DECIMAL_DOT && hasDot) {
return i - 1;
} else if (decimalRegex.test(character)) {
continue;
} else {
return i - 1;
}
}
return i;
}
function validator(compatibility) {
var validUnits = Units.slice(0).filter(function(value) {
return !(value in compatibility.units) || compatibility.units[value] === true;
});
if (compatibility.customUnits.rpx) {
validUnits.push('rpx');
}
return {
colorOpacity: compatibility.colors.opacity,
colorHexAlpha: compatibility.colors.hexAlpha,
isAnimationDirectionKeyword: isKeyword('animation-direction'),
isAnimationFillModeKeyword: isKeyword('animation-fill-mode'),
isAnimationIterationCountKeyword: isKeyword('animation-iteration-count'),
isAnimationNameKeyword: isKeyword('animation-name'),
isAnimationPlayStateKeyword: isKeyword('animation-play-state'),
isTimingFunction: isTimingFunction(),
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('^'),
isHexAlphaColor: isHexAlphaColor,
isHslColor: isHslColor,
isIdentifier: isIdentifier,
isImage: isImage,
isKeyword: isKeyword,
isLineHeightKeyword: isKeyword('line-height'),
isListStylePositionKeyword: isKeyword('list-style-position'),
isListStyleTypeKeyword: isKeyword('list-style-type'),
isNumber: isNumber,
isPrefixed: isPrefixed,
isPositiveNumber: isPositiveNumber,
isQuotedText: isQuotedText,
isRgbColor: isRgbColor,
isStyleKeyword: isKeyword('*-style'),
isTime: isTime,
isUnit: isUnit.bind(null, validUnits),
isUrl: isUrl,
isVariable: isVariable,
isWidth: isKeyword('width'),
isZIndex: isZIndex
};
}
module.exports = validator;

View File

@ -0,0 +1,24 @@
var VENDOR_PREFIX_PATTERN = /(?:^|\W)(-\w+-)/g;
function unique(value) {
var prefixes = [];
var match;
// eslint-disable-next-line no-cond-assign
while ((match = VENDOR_PREFIX_PATTERN.exec(value)) !== null) {
if (prefixes.indexOf(match[0]) == -1) {
prefixes.push(match[0]);
}
}
return prefixes;
}
function same(value1, value2) {
return unique(value1).sort().join(',') == unique(value2).sort().join(',');
}
module.exports = {
unique: unique,
same: same
};

View File

@ -0,0 +1,192 @@
var Hack = require('./hack');
var Marker = require('../tokenizer/marker');
var Token = require('../tokenizer/token');
var Match = {
ASTERISK: '*',
BACKSLASH: '\\',
BANG: '!',
BANG_SUFFIX_PATTERN: /!\w+$/,
IMPORTANT_TOKEN: '!important',
IMPORTANT_TOKEN_PATTERN: new RegExp('!important$', 'i'),
IMPORTANT_WORD: 'important',
IMPORTANT_WORD_PATTERN: new RegExp('important$', 'i'),
SUFFIX_BANG_PATTERN: /!$/,
UNDERSCORE: '_',
VARIABLE_REFERENCE_PATTERN: /var\(--.+\)$/
};
function wrapAll(properties, skipProperties) {
var wrapped = [];
var single;
var property;
var i;
for (i = properties.length - 1; i >= 0; i--) {
property = properties[i];
if (property[0] != Token.PROPERTY) {
continue;
}
if (skipProperties && skipProperties.indexOf(property[1][1]) > -1) {
continue;
}
single = wrapSingle(property);
single.all = properties;
single.position = i;
wrapped.unshift(single);
}
return wrapped;
}
function someVariableReferences(property) {
var i, l;
var value;
// skipping `property` and property name tokens
for (i = 2, l = property.length; i < l; i++) {
value = property[i];
if (value[0] != Token.PROPERTY_VALUE) {
continue;
}
if (isVariableReference(value[1])) {
return true;
}
}
return false;
}
function isVariableReference(value) {
return Match.VARIABLE_REFERENCE_PATTERN.test(value);
}
function isMultiplex(property) {
var value;
var i, l;
for (i = 3, l = property.length; i < l; i++) {
value = property[i];
if (value[0] == Token.PROPERTY_VALUE && (value[1] == Marker.COMMA || value[1] == Marker.FORWARD_SLASH)) {
return true;
}
}
return false;
}
function hackFrom(property) {
var match = false;
var name = property[1][1];
var lastValue = property[property.length - 1];
if (name[0] == Match.UNDERSCORE) {
match = [Hack.UNDERSCORE];
} else if (name[0] == Match.ASTERISK) {
match = [Hack.ASTERISK];
} else if (lastValue[1][0] == Match.BANG && !lastValue[1].match(Match.IMPORTANT_WORD_PATTERN)) {
match = [Hack.BANG];
} else if (lastValue[1].indexOf(Match.BANG) > 0
&& !lastValue[1].match(Match.IMPORTANT_WORD_PATTERN)
&& Match.BANG_SUFFIX_PATTERN.test(lastValue[1])) {
match = [Hack.BANG];
} else if (lastValue[1].indexOf(Match.BACKSLASH) > 0
&& lastValue[1].indexOf(Match.BACKSLASH) == lastValue[1].length - Match.BACKSLASH.length - 1) {
match = [Hack.BACKSLASH, lastValue[1].substring(lastValue[1].indexOf(Match.BACKSLASH) + 1)];
} else if (lastValue[1].indexOf(Match.BACKSLASH) === 0 && lastValue[1].length == 2) {
match = [Hack.BACKSLASH, lastValue[1].substring(1)];
}
return match;
}
function isImportant(property) {
if (property.length < 3) { return false; }
var lastValue = property[property.length - 1];
if (Match.IMPORTANT_TOKEN_PATTERN.test(lastValue[1])) {
return true;
} if (Match.IMPORTANT_WORD_PATTERN.test(lastValue[1])
&& Match.SUFFIX_BANG_PATTERN.test(property[property.length - 2][1])) {
return true;
}
return false;
}
function stripImportant(property) {
var lastValue = property[property.length - 1];
var oneButLastValue = property[property.length - 2];
if (Match.IMPORTANT_TOKEN_PATTERN.test(lastValue[1])) {
lastValue[1] = lastValue[1].replace(Match.IMPORTANT_TOKEN_PATTERN, '');
} else {
lastValue[1] = lastValue[1].replace(Match.IMPORTANT_WORD_PATTERN, '');
oneButLastValue[1] = oneButLastValue[1].replace(Match.SUFFIX_BANG_PATTERN, '');
}
if (lastValue[1].length === 0) {
property.pop();
}
if (oneButLastValue[1].length === 0) {
property.pop();
}
}
function stripPrefixHack(property) {
property[1][1] = property[1][1].substring(1);
}
function stripSuffixHack(property, hackFrom) {
var lastValue = property[property.length - 1];
lastValue[1] = lastValue[1]
.substring(0, lastValue[1].indexOf(hackFrom[0] == Hack.BACKSLASH ? Match.BACKSLASH : Match.BANG))
.trim();
if (lastValue[1].length === 0) {
property.pop();
}
}
function wrapSingle(property) {
var importantProperty = isImportant(property);
if (importantProperty) {
stripImportant(property);
}
var whichHack = hackFrom(property);
if (whichHack[0] == Hack.ASTERISK || whichHack[0] == Hack.UNDERSCORE) {
stripPrefixHack(property);
} else if (whichHack[0] == Hack.BACKSLASH || whichHack[0] == Hack.BANG) {
stripSuffixHack(property, whichHack);
}
return {
block: property[2] && property[2][0] == Token.PROPERTY_BLOCK,
components: [],
dirty: false,
dynamic: someVariableReferences(property),
hack: whichHack,
important: importantProperty,
name: property[1][1],
multiplex: property.length > 3 ? isMultiplex(property) : false,
optimizable: true,
position: 0,
shorthand: false,
unused: false,
value: property.slice(2)
};
}
module.exports = {
all: wrapAll,
single: wrapSingle
};

181
node_modules/clean-css/lib/options/compatibility.js generated vendored Normal file
View File

@ -0,0 +1,181 @@
var DEFAULTS = {
'*': {
colors: {
hexAlpha: false, // 4- and 8-character hex notation
opacity: true // rgba / hsla
},
customUnits: { rpx: false },
properties: {
backgroundClipMerging: true, // background-clip to shorthand
backgroundOriginMerging: true, // background-origin to shorthand
backgroundSizeMerging: true, // background-size to shorthand
colors: true, // any kind of color transformations, like `#ff00ff` to `#f0f` or `#fff` into `red`
ieBangHack: false, // !ie suffix hacks on IE<8
ieFilters: false, // whether to preserve `filter` and `-ms-filter` properties
iePrefixHack: false, // underscore / asterisk prefix hacks on IE
ieSuffixHack: false, // \9 suffix hacks on IE6-9, \0 suffix hack on IE6-11
merging: true, // merging properties into one
shorterLengthUnits: false, // optimize pixel units into `pt`, `pc` or `in` units
spaceAfterClosingBrace: true, // 'url() no-repeat' to 'url()no-repeat'
urlQuotes: true, // whether to wrap content of `url()` into quotes or not
zeroUnits: true // 0[unit] -> 0
},
selectors: {
adjacentSpace: false, // div+ nav Android stock browser hack
ie7Hack: false, // *+html hack
mergeablePseudoClasses: [
':active',
':after',
':before',
':empty',
':checked',
':disabled',
':empty',
':enabled',
':first-child',
':first-letter',
':first-line',
':first-of-type',
':focus',
':hover',
':lang',
':last-child',
':last-of-type',
':link',
':not',
':nth-child',
':nth-last-child',
':nth-last-of-type',
':nth-of-type',
':only-child',
':only-of-type',
':root',
':target',
':visited'
], // selectors with these pseudo-classes can be merged as these are universally supported
mergeablePseudoElements: [
'::after',
'::before',
'::first-letter',
'::first-line'
], // selectors with these pseudo-elements can be merged as these are universally supported
mergeLimit: 8191, // number of rules that can be safely merged together
multiplePseudoMerging: true
},
units: {
ch: true,
in: true,
pc: true,
pt: true,
rem: true,
vh: true,
vm: true, // vm is vmin on IE9+ see https://developer.mozilla.org/en-US/docs/Web/CSS/length
vmax: true,
vmin: true,
vw: true
}
}
};
DEFAULTS.ie11 = merge(DEFAULTS['*'], { properties: { ieSuffixHack: true } });
DEFAULTS.ie10 = merge(DEFAULTS['*'], { properties: { ieSuffixHack: true } });
DEFAULTS.ie9 = merge(DEFAULTS['*'], {
properties: {
ieFilters: true,
ieSuffixHack: true
}
});
DEFAULTS.ie8 = merge(DEFAULTS.ie9, {
colors: { opacity: false },
properties: {
backgroundClipMerging: false,
backgroundOriginMerging: false,
backgroundSizeMerging: false,
iePrefixHack: true,
merging: false
},
selectors: {
mergeablePseudoClasses: [
':after',
':before',
':first-child',
':first-letter',
':focus',
':hover',
':visited'
],
mergeablePseudoElements: []
},
units: {
ch: false,
rem: false,
vh: false,
vm: false,
vmax: false,
vmin: false,
vw: false
}
});
DEFAULTS.ie7 = merge(DEFAULTS.ie8, {
properties: { ieBangHack: true },
selectors: {
ie7Hack: true,
mergeablePseudoClasses: [
':first-child',
':first-letter',
':hover',
':visited'
]
}
});
function compatibilityFrom(source) {
return merge(DEFAULTS['*'], calculateSource(source));
}
function merge(source, target) {
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
var value = source[key];
if (Object.prototype.hasOwnProperty.call(target, key) && typeof value === 'object' && !Array.isArray(value)) {
target[key] = merge(value, target[key] || {});
} else {
target[key] = key in target ? target[key] : value;
}
}
}
return target;
}
function calculateSource(source) {
if (typeof source == 'object') { return source; }
if (!/[,+-]/.test(source)) { return DEFAULTS[source] || DEFAULTS['*']; }
var parts = source.split(',');
var template = parts[0] in DEFAULTS
? DEFAULTS[parts.shift()]
: DEFAULTS['*'];
source = {};
parts.forEach(function(part) {
var isAdd = part[0] == '+';
var key = part.substring(1).split('.');
var group = key[0];
var option = key[1];
source[group] = source[group] || {};
source[group][option] = isAdd;
});
return merge(template, source);
}
module.exports = compatibilityFrom;

7
node_modules/clean-css/lib/options/fetch.js generated vendored Normal file
View File

@ -0,0 +1,7 @@
var loadRemoteResource = require('../reader/load-remote-resource');
function fetchFrom(callback) {
return callback || loadRemoteResource;
}
module.exports = fetchFrom;

Some files were not shown because too many files have changed in this diff Show More