It appears you have a well-structured Git repository with various files, including SVG icons and HTML documents. Here's a brief overview:
This commit is contained in:
20
backend/node_modules/csso/LICENSE
generated
vendored
Normal file
20
backend/node_modules/csso/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (C) 2015-2021 by Roman Dvornov
|
||||
Copyright (C) 2011-2015 by Sergey Kryzhanovsky
|
||||
|
||||
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.
|
422
backend/node_modules/csso/README.md
generated
vendored
Normal file
422
backend/node_modules/csso/README.md
generated
vendored
Normal file
@@ -0,0 +1,422 @@
|
||||
[](https://www.npmjs.com/package/csso)
|
||||
[](https://github.com/css/csso/actions/workflows/build.yml)
|
||||
[](https://coveralls.io/github/css/csso?branch=master)
|
||||
[](https://www.npmjs.com/package/csso)
|
||||
[](https://twitter.com/cssoptimizer)
|
||||
|
||||
CSSO (CSS Optimizer) is a CSS minifier. It performs three sort of transformations: cleaning (removing redundants), compression (replacement for the shorter forms) and restructuring (merge of declarations, rules and so on). As a result an output CSS becomes much smaller in size.
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
npm install csso
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { minify } from 'csso';
|
||||
// CommonJS is also supported
|
||||
// const { minify } = require('csso');
|
||||
|
||||
const minifiedCss = minify('.test { color: #ff0000; }').css;
|
||||
|
||||
console.log(minifiedCss);
|
||||
// .test{color:red}
|
||||
```
|
||||
|
||||
Bundles are also available for use in a browser:
|
||||
|
||||
- `dist/csso.js` – minified IIFE with `csso` as global
|
||||
```html
|
||||
<script src="node_modules/csso/dist/csso.js"></script>
|
||||
<script>
|
||||
csso.minify('.example { color: green }');
|
||||
</script>
|
||||
```
|
||||
|
||||
- `dist/csso.esm.js` – minified ES module
|
||||
```html
|
||||
<script type="module">
|
||||
import { minify } from 'node_modules/csso/dist/csso.esm.js'
|
||||
|
||||
minify('.example { color: green }');
|
||||
</script>
|
||||
```
|
||||
|
||||
One of CDN services like `unpkg` or `jsDelivr` can be used. By default (for short path) a ESM version is exposing. For IIFE version a full path to a bundle should be specified:
|
||||
|
||||
```html
|
||||
<!-- ESM -->
|
||||
<script type="module">
|
||||
import * as csstree from 'https://cdn.jsdelivr.net/npm/csso';
|
||||
import * as csstree from 'https://unpkg.com/csso';
|
||||
</script>
|
||||
|
||||
<!-- IIFE with an export to global -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/csso/dist/csso.js"></script>
|
||||
<script src="https://unpkg.com/csso/dist/csso.js"></script>
|
||||
```
|
||||
|
||||
CSSO is based on [CSSTree](https://github.com/csstree/csstree) to parse CSS into AST, AST traversal and to generate AST back to CSS. All `CSSTree` API is available behind `syntax` field extended with `compress()` method. You may minify CSS step by step:
|
||||
|
||||
```js
|
||||
import { syntax } from 'csso';
|
||||
|
||||
const ast = syntax.parse('.test { color: #ff0000; }');
|
||||
const compressedAst = syntax.compress(ast).ast;
|
||||
const minifiedCss = syntax.generate(compressedAst);
|
||||
|
||||
console.log(minifiedCss);
|
||||
// .test{color:red}
|
||||
```
|
||||
|
||||
Also syntax can be imported using `csso/syntax` entry point:
|
||||
|
||||
```js
|
||||
import { parse, compress, generate } from 'csso/syntax';
|
||||
|
||||
const ast = parse('.test { color: #ff0000; }');
|
||||
const compressedAst = compress(ast).ast;
|
||||
const minifiedCss = generate(compressedAst);
|
||||
|
||||
console.log(minifiedCss);
|
||||
// .test{color:red}
|
||||
```
|
||||
|
||||
> Warning: CSSO doesn't guarantee API behind a `syntax` field as well as AST format. Both might be changed with changes in CSSTree. If you rely heavily on `syntax` API, a better option might be to use CSSTree directly.
|
||||
|
||||
## Related projects
|
||||
|
||||
- [Web interface](http://css.github.io/csso/csso.html)
|
||||
- [csso-cli](https://github.com/css/csso-cli) – command line interface
|
||||
- [gulp-csso](https://github.com/ben-eb/gulp-csso) – `Gulp` plugin
|
||||
- [grunt-csso](https://github.com/t32k/grunt-csso) – `Grunt` plugin
|
||||
- [broccoli-csso](https://github.com/sindresorhus/broccoli-csso) – `Broccoli` plugin
|
||||
- [postcss-csso](https://github.com/lahmatiy/postcss-csso) – `PostCSS` plugin
|
||||
- [csso-loader](https://github.com/sandark7/csso-loader) – `webpack` loader
|
||||
- [csso-webpack-plugin](https://github.com/zoobestik/csso-webpack-plugin) – `webpack` plugin
|
||||
- [CSSO Visual Studio Code plugin](https://marketplace.visualstudio.com/items?itemName=Aneryu.csso)
|
||||
- [vscode-csso](https://github.com/1000ch/vscode-csso) - Visual Studio Code plugin
|
||||
- [atom-csso](https://github.com/1000ch/atom-csso) - Atom plugin
|
||||
- [Sublime-csso](https://github.com/1000ch/Sublime-csso) - Sublime plugin
|
||||
|
||||
## API
|
||||
|
||||
<!-- TOC depthfrom:3 -->
|
||||
|
||||
- [minify(source[, options])](#minifysource-options)
|
||||
- [minifyBlock(source[, options])](#minifyblocksource-options)
|
||||
- [syntax.compress(ast[, options])](#syntaxcompressast-options)
|
||||
- [Source maps](#source-maps)
|
||||
- [Usage data](#usage-data)
|
||||
- [White list filtering](#white-list-filtering)
|
||||
- [Black list filtering](#black-list-filtering)
|
||||
- [Scopes](#scopes)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
### minify(source[, options])
|
||||
|
||||
Minify `source` CSS passed as `String`.
|
||||
|
||||
```js
|
||||
const result = csso.minify('.test { color: #ff0000; }', {
|
||||
restructure: false, // don't change CSS structure, i.e. don't merge declarations, rulesets etc
|
||||
debug: true // show additional debug information:
|
||||
// true or number from 1 to 3 (greater number - more details)
|
||||
});
|
||||
|
||||
console.log(result.css);
|
||||
// > .test{color:red}
|
||||
```
|
||||
|
||||
Returns an object with properties:
|
||||
|
||||
- css `String` – resulting CSS
|
||||
- map `Object` – instance of [`SourceMapGenerator`](https://github.com/mozilla/source-map#sourcemapgenerator) or `null`
|
||||
|
||||
Options:
|
||||
|
||||
- sourceMap
|
||||
|
||||
Type: `Boolean`
|
||||
Default: `false`
|
||||
|
||||
Generate a source map when `true`.
|
||||
|
||||
- filename
|
||||
|
||||
Type: `String`
|
||||
Default: `'<unknown>'`
|
||||
|
||||
Filename of input CSS, uses for source map generation.
|
||||
|
||||
- debug
|
||||
|
||||
Type: `Boolean`
|
||||
Default: `false`
|
||||
|
||||
Output debug information to `stderr`.
|
||||
|
||||
- beforeCompress
|
||||
|
||||
Type: `function(ast, options)` or `Array<function(ast, options)>` or `null`
|
||||
Default: `null`
|
||||
|
||||
Called right after parse is run.
|
||||
|
||||
- afterCompress
|
||||
|
||||
Type: `function(compressResult, options)` or `Array<function(compressResult, options)>` or `null`
|
||||
Default: `null`
|
||||
|
||||
Called right after [`syntax.compress()`](#syntaxcompressast-options) is run.
|
||||
|
||||
- Other options are the same as for [`syntax.compress()`](#syntaxcompressast-options) function.
|
||||
|
||||
### minifyBlock(source[, options])
|
||||
|
||||
The same as `minify()` but for list of declarations. Usually it's a `style` attribute value.
|
||||
|
||||
```js
|
||||
const result = csso.minifyBlock('color: rgba(255, 0, 0, 1); color: #ff0000');
|
||||
|
||||
console.log(result.css);
|
||||
// > color:red
|
||||
```
|
||||
|
||||
### syntax.compress(ast[, options])
|
||||
|
||||
Does the main task – compress an AST. This is CSSO's extension in CSSTree syntax API.
|
||||
|
||||
> NOTE: `syntax.compress()` performs AST compression by transforming input AST by default (since AST cloning is expensive and needed in rare cases). Use `clone` option with truthy value in case you want to keep input AST untouched.
|
||||
|
||||
Returns an object with properties:
|
||||
|
||||
- ast `Object` – resulting AST
|
||||
|
||||
Options:
|
||||
|
||||
- restructure
|
||||
|
||||
Type: `Boolean`
|
||||
Default: `true`
|
||||
|
||||
Disable or enable a structure optimisations.
|
||||
|
||||
- forceMediaMerge
|
||||
|
||||
Type: `Boolean`
|
||||
Default: `false`
|
||||
|
||||
Enables merging of `@media` rules with the same media query by splitted by other rules. The optimisation is unsafe in general, but should work fine in most cases. Use it on your own risk.
|
||||
|
||||
- clone
|
||||
|
||||
Type: `Boolean`
|
||||
Default: `false`
|
||||
|
||||
Transform a copy of input AST if `true`. Useful in case of AST reuse.
|
||||
|
||||
- comments
|
||||
|
||||
Type: `String` or `Boolean`
|
||||
Default: `true`
|
||||
|
||||
Specify what comments to leave:
|
||||
|
||||
- `'exclamation'` or `true` – leave all exclamation comments (i.e. `/*! .. */`)
|
||||
- `'first-exclamation'` – remove every comment except first one
|
||||
- `false` – remove all comments
|
||||
|
||||
- usage
|
||||
|
||||
Type: `Object` or `null`
|
||||
Default: `null`
|
||||
|
||||
Usage data for advanced optimisations (see [Usage data](#usage-data) for details)
|
||||
|
||||
- logger
|
||||
|
||||
Type: `Function` or `null`
|
||||
Default: `null`
|
||||
|
||||
Function to track every step of transformation.
|
||||
|
||||
### Source maps
|
||||
|
||||
To get a source map set `true` for `sourceMap` option. Additianaly `filename` option can be passed to specify source file. When `sourceMap` option is `true`, `map` field of result object will contain a [`SourceMapGenerator`](https://github.com/mozilla/source-map#sourcemapgenerator) instance. This object can be mixed with another source map or translated to string.
|
||||
|
||||
```js
|
||||
const csso = require('csso');
|
||||
const css = fs.readFileSync('path/to/my.css', 'utf8');
|
||||
const result = csso.minify(css, {
|
||||
filename: 'path/to/my.css', // will be added to source map as reference to source file
|
||||
sourceMap: true // generate source map
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
// { css: '...minified...', map: SourceMapGenerator {} }
|
||||
|
||||
console.log(result.map.toString());
|
||||
// '{ .. source map content .. }'
|
||||
```
|
||||
|
||||
Example of generating source map with respect of source map from input CSS:
|
||||
|
||||
```js
|
||||
import { SourceMapConsumer } from 'source-map';
|
||||
import * as csso from 'csso';
|
||||
|
||||
const inputFile = 'path/to/my.css';
|
||||
const input = fs.readFileSync(inputFile, 'utf8');
|
||||
const inputMap = input.match(/\/\*# sourceMappingURL=(\S+)\s*\*\/\s*$/);
|
||||
const output = csso.minify(input, {
|
||||
filename: inputFile,
|
||||
sourceMap: true
|
||||
});
|
||||
|
||||
// apply input source map to output
|
||||
if (inputMap) {
|
||||
output.map.applySourceMap(
|
||||
new SourceMapConsumer(inputMap[1]),
|
||||
inputFile
|
||||
)
|
||||
}
|
||||
|
||||
// result CSS with source map
|
||||
console.log(
|
||||
output.css +
|
||||
'/*# sourceMappingURL=data:application/json;base64,' +
|
||||
Buffer.from(output.map.toString()).toString('base64') +
|
||||
' */'
|
||||
);
|
||||
```
|
||||
|
||||
### Usage data
|
||||
|
||||
`CSSO` can use data about how `CSS` is used in a markup for better compression. File with this data (`JSON`) can be set using `usage` option. Usage data may contain following sections:
|
||||
|
||||
- `blacklist` – a set of black lists (see [Black list filtering](#black-list-filtering))
|
||||
- `tags` – white list of tags
|
||||
- `ids` – white list of ids
|
||||
- `classes` – white list of classes
|
||||
- `scopes` – groups of classes which never used with classes from other groups on the same element
|
||||
|
||||
All sections are optional. Value of `tags`, `ids` and `classes` should be an array of a string, value of `scopes` should be an array of arrays of strings. Other values are ignoring.
|
||||
|
||||
#### White list filtering
|
||||
|
||||
`tags`, `ids` and `classes` are using on clean stage to filter selectors that contain something not in the lists. Selectors are filtering only by those kind of simple selector which white list is specified. For example, if only `tags` list is specified then type selectors are checking, and if all type selectors in selector present in list or selector has no any type selector it isn't filter.
|
||||
|
||||
> `ids` and `classes` are case sensitive, `tags` – is not.
|
||||
|
||||
Input CSS:
|
||||
|
||||
```css
|
||||
* { color: green; }
|
||||
ul, ol, li { color: blue; }
|
||||
UL.foo, span.bar { color: red; }
|
||||
```
|
||||
|
||||
Usage data:
|
||||
|
||||
```json
|
||||
{
|
||||
"tags": ["ul", "LI"]
|
||||
}
|
||||
```
|
||||
|
||||
Resulting CSS:
|
||||
|
||||
```css
|
||||
*{color:green}ul,li{color:blue}ul.foo{color:red}
|
||||
```
|
||||
|
||||
Filtering performs for nested selectors too. `:not()` pseudos content is ignoring since the result of matching is unpredictable. Example for the same usage data as above:
|
||||
|
||||
```css
|
||||
:nth-child(2n of ul, ol) { color: red }
|
||||
:nth-child(3n + 1 of img) { color: yellow }
|
||||
:not(div, ol, ul) { color: green }
|
||||
:has(:matches(ul, ol), ul, ol) { color: blue }
|
||||
```
|
||||
|
||||
Turns into:
|
||||
|
||||
```css
|
||||
:nth-child(2n of ul){color:red}:not(div,ol,ul){color:green}:has(:matches(ul),ul){color:blue}
|
||||
```
|
||||
|
||||
#### Black list filtering
|
||||
|
||||
Black list filtering performs the same as white list filtering, but filters things that mentioned in the lists. `blacklist` can contain the lists `tags`, `ids` and `classes`.
|
||||
|
||||
Black list has a higher priority, so when something mentioned in the white list and in the black list then white list occurrence is ignoring. The `:not()` pseudos content ignoring as well.
|
||||
|
||||
```css
|
||||
* { color: green; }
|
||||
ul, ol, li { color: blue; }
|
||||
UL.foo, li.bar { color: red; }
|
||||
```
|
||||
|
||||
Usage data:
|
||||
|
||||
```json
|
||||
{
|
||||
"blacklist": {
|
||||
"tags": ["ul"]
|
||||
},
|
||||
"tags": ["ul", "LI"]
|
||||
}
|
||||
```
|
||||
|
||||
Resulting CSS:
|
||||
|
||||
```css
|
||||
*{color:green}li{color:blue}li.bar{color:red}
|
||||
```
|
||||
|
||||
#### Scopes
|
||||
|
||||
Scopes is designed for CSS scope isolation solutions such as [css-modules](https://github.com/css-modules/css-modules). Scopes are similar to namespaces and define lists of class names that exclusively used on some markup. This information allows the optimizer to move rules more agressive. Since it assumes selectors from different scopes don't match for the same element. This can improve rule merging.
|
||||
|
||||
Suppose we have a file:
|
||||
|
||||
```css
|
||||
.module1-foo { color: red; }
|
||||
.module1-bar { font-size: 1.5em; background: yellow; }
|
||||
|
||||
.module2-baz { color: red; }
|
||||
.module2-qux { font-size: 1.5em; background: yellow; width: 50px; }
|
||||
```
|
||||
|
||||
It can be assumed that first two rules are never used with the second two on the same markup. But we can't say that for sure without a markup review. The optimizer doesn't know it either and will perform safe transformations only. The result will be the same as input but with no spaces and some semicolons:
|
||||
|
||||
```css
|
||||
.module1-foo{color:red}.module1-bar{font-size:1.5em;background:#ff0}.module2-baz{color:red}.module2-qux{font-size:1.5em;background:#ff0;width:50px}
|
||||
```
|
||||
|
||||
With usage data `CSSO` can produce better output. If follow usage data is provided:
|
||||
|
||||
```json
|
||||
{
|
||||
"scopes": [
|
||||
["module1-foo", "module1-bar"],
|
||||
["module2-baz", "module2-qux"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The result will be (29 bytes extra saving):
|
||||
|
||||
```css
|
||||
.module1-foo,.module2-baz{color:red}.module1-bar,.module2-qux{font-size:1.5em;background:#ff0}.module2-qux{width:50px}
|
||||
```
|
||||
|
||||
If class name isn't mentioned in the `scopes` it belongs to default scope. `scopes` data doesn't affect `classes` whitelist. If class name mentioned in `scopes` but missed in `classes` (both sections are specified) it will be filtered.
|
||||
|
||||
Note that class name can't be set for several scopes. Also a selector can't have class names from different scopes. In both cases an exception will thrown.
|
||||
|
||||
Currently the optimizer doesn't care about changing order safety for out-of-bounds selectors (i.e. selectors that match to elements without class name, e.g. `.scope div` or `.scope ~ :last-child`). It assumes that scoped CSS modules doesn't relay on it's order. It may be fix in future if to be an issue.
|
73
backend/node_modules/csso/cjs/clean/Atrule.cjs
generated
vendored
Normal file
73
backend/node_modules/csso/cjs/clean/Atrule.cjs
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const utils = require('./utils.cjs');
|
||||
|
||||
function cleanAtrule(node, item, list) {
|
||||
if (node.block) {
|
||||
// otherwise removed at-rule don't prevent @import for removal
|
||||
if (this.stylesheet !== null) {
|
||||
this.stylesheet.firstAtrulesAllowed = false;
|
||||
}
|
||||
|
||||
if (utils.hasNoChildren(node.block)) {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (node.name) {
|
||||
case 'charset':
|
||||
if (utils.hasNoChildren(node.prelude)) {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
|
||||
// if there is any rule before @charset -> remove it
|
||||
if (item.prev) {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'import':
|
||||
if (this.stylesheet === null || !this.stylesheet.firstAtrulesAllowed) {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
|
||||
// if there are some rules that not an @import or @charset before @import
|
||||
// remove it
|
||||
list.prevUntil(item.prev, function(rule) {
|
||||
if (rule.type === 'Atrule') {
|
||||
if (rule.name === 'import' || rule.name === 'charset') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.root.firstAtrulesAllowed = false;
|
||||
list.remove(item);
|
||||
|
||||
return true;
|
||||
}, this);
|
||||
|
||||
break;
|
||||
|
||||
default: {
|
||||
const name = cssTree.keyword(node.name).basename;
|
||||
|
||||
if (name === 'keyframes' ||
|
||||
name === 'media' ||
|
||||
name === 'supports') {
|
||||
|
||||
// drop at-rule with no prelude
|
||||
if (utils.hasNoChildren(node.prelude) || utils.hasNoChildren(node.block)) {
|
||||
list.remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = cleanAtrule;
|
7
backend/node_modules/csso/cjs/clean/Comment.cjs
generated
vendored
Normal file
7
backend/node_modules/csso/cjs/clean/Comment.cjs
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
function cleanComment(data, item, list) {
|
||||
list.remove(item);
|
||||
}
|
||||
|
||||
module.exports = cleanComment;
|
18
backend/node_modules/csso/cjs/clean/Declaration.cjs
generated
vendored
Normal file
18
backend/node_modules/csso/cjs/clean/Declaration.cjs
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
|
||||
function cleanDeclartion(node, item, list) {
|
||||
if (node.value.children && node.value.children.isEmpty) {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cssTree.property(node.property).custom) {
|
||||
if (/\S/.test(node.value.value)) {
|
||||
node.value.value = node.value.value.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = cleanDeclartion;
|
13
backend/node_modules/csso/cjs/clean/Raw.cjs
generated
vendored
Normal file
13
backend/node_modules/csso/cjs/clean/Raw.cjs
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const utils = require('./utils.cjs');
|
||||
|
||||
function cleanRaw(node, item, list) {
|
||||
// raw in stylesheet or block children
|
||||
if (utils.isNodeChildrenList(this.stylesheet, list) ||
|
||||
utils.isNodeChildrenList(this.block, list)) {
|
||||
list.remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = cleanRaw;
|
104
backend/node_modules/csso/cjs/clean/Rule.cjs
generated
vendored
Normal file
104
backend/node_modules/csso/cjs/clean/Rule.cjs
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const utils = require('./utils.cjs');
|
||||
|
||||
const { hasOwnProperty } = Object.prototype;
|
||||
const skipUsageFilteringAtrule = new Set(['keyframes']);
|
||||
|
||||
function cleanUnused(selectorList, usageData) {
|
||||
selectorList.children.forEach((selector, item, list) => {
|
||||
let shouldRemove = false;
|
||||
|
||||
cssTree.walk(selector, function(node) {
|
||||
// ignore nodes in nested selectors
|
||||
if (this.selector === null || this.selector === selectorList) {
|
||||
switch (node.type) {
|
||||
case 'SelectorList':
|
||||
// TODO: remove toLowerCase when pseudo selectors will be normalized
|
||||
// ignore selectors inside :not()
|
||||
if (this.function === null || this.function.name.toLowerCase() !== 'not') {
|
||||
if (cleanUnused(node, usageData)) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ClassSelector':
|
||||
if (usageData.whitelist !== null &&
|
||||
usageData.whitelist.classes !== null &&
|
||||
!hasOwnProperty.call(usageData.whitelist.classes, node.name)) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
if (usageData.blacklist !== null &&
|
||||
usageData.blacklist.classes !== null &&
|
||||
hasOwnProperty.call(usageData.blacklist.classes, node.name)) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'IdSelector':
|
||||
if (usageData.whitelist !== null &&
|
||||
usageData.whitelist.ids !== null &&
|
||||
!hasOwnProperty.call(usageData.whitelist.ids, node.name)) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
if (usageData.blacklist !== null &&
|
||||
usageData.blacklist.ids !== null &&
|
||||
hasOwnProperty.call(usageData.blacklist.ids, node.name)) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'TypeSelector':
|
||||
// TODO: remove toLowerCase when type selectors will be normalized
|
||||
// ignore universal selectors
|
||||
if (node.name.charAt(node.name.length - 1) !== '*') {
|
||||
if (usageData.whitelist !== null &&
|
||||
usageData.whitelist.tags !== null &&
|
||||
!hasOwnProperty.call(usageData.whitelist.tags, node.name.toLowerCase())) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
if (usageData.blacklist !== null &&
|
||||
usageData.blacklist.tags !== null &&
|
||||
hasOwnProperty.call(usageData.blacklist.tags, node.name.toLowerCase())) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (shouldRemove) {
|
||||
list.remove(item);
|
||||
}
|
||||
});
|
||||
|
||||
return selectorList.children.isEmpty;
|
||||
}
|
||||
|
||||
function cleanRule(node, item, list, options) {
|
||||
if (utils.hasNoChildren(node.prelude) || utils.hasNoChildren(node.block)) {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
|
||||
// avoid usage filtering for some at-rules
|
||||
if (this.atrule && skipUsageFilteringAtrule.has(cssTree.keyword(this.atrule.name).basename)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { usage } = options;
|
||||
|
||||
if (usage && (usage.whitelist !== null || usage.blacklist !== null)) {
|
||||
cleanUnused(node.prelude, usage);
|
||||
|
||||
if (utils.hasNoChildren(node.prelude)) {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = cleanRule;
|
23
backend/node_modules/csso/cjs/clean/TypeSelector.cjs
generated
vendored
Normal file
23
backend/node_modules/csso/cjs/clean/TypeSelector.cjs
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
// remove useless universal selector
|
||||
function cleanTypeSelector(node, item, list) {
|
||||
const name = item.data.name;
|
||||
|
||||
// check it's a non-namespaced universal selector
|
||||
if (name !== '*') {
|
||||
return;
|
||||
}
|
||||
|
||||
// remove when universal selector before other selectors
|
||||
const nextType = item.next && item.next.data.type;
|
||||
if (nextType === 'IdSelector' ||
|
||||
nextType === 'ClassSelector' ||
|
||||
nextType === 'AttributeSelector' ||
|
||||
nextType === 'PseudoClassSelector' ||
|
||||
nextType === 'PseudoElementSelector') {
|
||||
list.remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = cleanTypeSelector;
|
7
backend/node_modules/csso/cjs/clean/WhiteSpace.cjs
generated
vendored
Normal file
7
backend/node_modules/csso/cjs/clean/WhiteSpace.cjs
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
function cleanWhitespace(node, item, list) {
|
||||
list.remove(item);
|
||||
}
|
||||
|
||||
module.exports = cleanWhitespace;
|
32
backend/node_modules/csso/cjs/clean/index.cjs
generated
vendored
Normal file
32
backend/node_modules/csso/cjs/clean/index.cjs
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const Atrule = require('./Atrule.cjs');
|
||||
const Comment = require('./Comment.cjs');
|
||||
const Declaration = require('./Declaration.cjs');
|
||||
const Raw = require('./Raw.cjs');
|
||||
const Rule = require('./Rule.cjs');
|
||||
const TypeSelector = require('./TypeSelector.cjs');
|
||||
const WhiteSpace = require('./WhiteSpace.cjs');
|
||||
|
||||
const handlers = {
|
||||
Atrule,
|
||||
Comment,
|
||||
Declaration,
|
||||
Raw,
|
||||
Rule,
|
||||
TypeSelector,
|
||||
WhiteSpace
|
||||
};
|
||||
|
||||
function clean(ast, options) {
|
||||
cssTree.walk(ast, {
|
||||
leave(node, item, list) {
|
||||
if (handlers.hasOwnProperty(node.type)) {
|
||||
handlers[node.type].call(this, node, item, list, options);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = clean;
|
12
backend/node_modules/csso/cjs/clean/utils.cjs
generated
vendored
Normal file
12
backend/node_modules/csso/cjs/clean/utils.cjs
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
function hasNoChildren(node) {
|
||||
return !node || !node.children || node.children.isEmpty;
|
||||
}
|
||||
|
||||
function isNodeChildrenList(node, list) {
|
||||
return node !== null && node.children === list;
|
||||
}
|
||||
|
||||
exports.hasNoChildren = hasNoChildren;
|
||||
exports.isNodeChildrenList = isNodeChildrenList;
|
200
backend/node_modules/csso/cjs/compress.cjs
generated
vendored
Normal file
200
backend/node_modules/csso/cjs/compress.cjs
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const usage = require('./usage.cjs');
|
||||
const index = require('./clean/index.cjs');
|
||||
const index$1 = require('./replace/index.cjs');
|
||||
const index$2 = require('./restructure/index.cjs');
|
||||
|
||||
function readChunk(input, specialComments) {
|
||||
const children = new cssTree.List();
|
||||
let nonSpaceTokenInBuffer = false;
|
||||
let protectedComment;
|
||||
|
||||
input.nextUntil(input.head, (node, item, list) => {
|
||||
if (node.type === 'Comment') {
|
||||
if (!specialComments || node.value.charAt(0) !== '!') {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nonSpaceTokenInBuffer || protectedComment) {
|
||||
return true;
|
||||
}
|
||||
|
||||
list.remove(item);
|
||||
protectedComment = node;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.type !== 'WhiteSpace') {
|
||||
nonSpaceTokenInBuffer = true;
|
||||
}
|
||||
|
||||
children.insert(list.remove(item));
|
||||
});
|
||||
|
||||
return {
|
||||
comment: protectedComment,
|
||||
stylesheet: {
|
||||
type: 'StyleSheet',
|
||||
loc: null,
|
||||
children
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function compressChunk(ast, firstAtrulesAllowed, num, options) {
|
||||
options.logger(`Compress block #${num}`, null, true);
|
||||
|
||||
let seed = 1;
|
||||
|
||||
if (ast.type === 'StyleSheet') {
|
||||
ast.firstAtrulesAllowed = firstAtrulesAllowed;
|
||||
ast.id = seed++;
|
||||
}
|
||||
|
||||
cssTree.walk(ast, {
|
||||
visit: 'Atrule',
|
||||
enter(node) {
|
||||
if (node.block !== null) {
|
||||
node.block.id = seed++;
|
||||
}
|
||||
}
|
||||
});
|
||||
options.logger('init', ast);
|
||||
|
||||
// remove redundant
|
||||
index(ast, options);
|
||||
options.logger('clean', ast);
|
||||
|
||||
// replace nodes for shortened forms
|
||||
index$1(ast);
|
||||
options.logger('replace', ast);
|
||||
|
||||
// structure optimisations
|
||||
if (options.restructuring) {
|
||||
index$2(ast, options);
|
||||
}
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
function getCommentsOption(options) {
|
||||
let comments = 'comments' in options ? options.comments : 'exclamation';
|
||||
|
||||
if (typeof comments === 'boolean') {
|
||||
comments = comments ? 'exclamation' : false;
|
||||
} else if (comments !== 'exclamation' && comments !== 'first-exclamation') {
|
||||
comments = false;
|
||||
}
|
||||
|
||||
return comments;
|
||||
}
|
||||
|
||||
function getRestructureOption(options) {
|
||||
if ('restructure' in options) {
|
||||
return options.restructure;
|
||||
}
|
||||
|
||||
return 'restructuring' in options ? options.restructuring : true;
|
||||
}
|
||||
|
||||
function wrapBlock(block) {
|
||||
return new cssTree.List().appendData({
|
||||
type: 'Rule',
|
||||
loc: null,
|
||||
prelude: {
|
||||
type: 'SelectorList',
|
||||
loc: null,
|
||||
children: new cssTree.List().appendData({
|
||||
type: 'Selector',
|
||||
loc: null,
|
||||
children: new cssTree.List().appendData({
|
||||
type: 'TypeSelector',
|
||||
loc: null,
|
||||
name: 'x'
|
||||
})
|
||||
})
|
||||
},
|
||||
block
|
||||
});
|
||||
}
|
||||
|
||||
function compress(ast, options) {
|
||||
ast = ast || { type: 'StyleSheet', loc: null, children: new cssTree.List() };
|
||||
options = options || {};
|
||||
|
||||
const compressOptions = {
|
||||
logger: typeof options.logger === 'function' ? options.logger : function() {},
|
||||
restructuring: getRestructureOption(options),
|
||||
forceMediaMerge: Boolean(options.forceMediaMerge),
|
||||
usage: options.usage ? usage.buildIndex(options.usage) : false
|
||||
};
|
||||
const output = new cssTree.List();
|
||||
let specialComments = getCommentsOption(options);
|
||||
let firstAtrulesAllowed = true;
|
||||
let input;
|
||||
let chunk;
|
||||
let chunkNum = 1;
|
||||
let chunkChildren;
|
||||
|
||||
if (options.clone) {
|
||||
ast = cssTree.clone(ast);
|
||||
}
|
||||
|
||||
if (ast.type === 'StyleSheet') {
|
||||
input = ast.children;
|
||||
ast.children = output;
|
||||
} else {
|
||||
input = wrapBlock(ast);
|
||||
}
|
||||
|
||||
do {
|
||||
chunk = readChunk(input, Boolean(specialComments));
|
||||
compressChunk(chunk.stylesheet, firstAtrulesAllowed, chunkNum++, compressOptions);
|
||||
chunkChildren = chunk.stylesheet.children;
|
||||
|
||||
if (chunk.comment) {
|
||||
// add \n before comment if there is another content in output
|
||||
if (!output.isEmpty) {
|
||||
output.insert(cssTree.List.createItem({
|
||||
type: 'Raw',
|
||||
value: '\n'
|
||||
}));
|
||||
}
|
||||
|
||||
output.insert(cssTree.List.createItem(chunk.comment));
|
||||
|
||||
// add \n after comment if chunk is not empty
|
||||
if (!chunkChildren.isEmpty) {
|
||||
output.insert(cssTree.List.createItem({
|
||||
type: 'Raw',
|
||||
value: '\n'
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if (firstAtrulesAllowed && !chunkChildren.isEmpty) {
|
||||
const lastRule = chunkChildren.last;
|
||||
|
||||
if (lastRule.type !== 'Atrule' ||
|
||||
(lastRule.name !== 'import' && lastRule.name !== 'charset')) {
|
||||
firstAtrulesAllowed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (specialComments !== 'exclamation') {
|
||||
specialComments = false;
|
||||
}
|
||||
|
||||
output.appendList(chunkChildren);
|
||||
} while (!input.isEmpty);
|
||||
|
||||
return {
|
||||
ast
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = compress;
|
127
backend/node_modules/csso/cjs/index.cjs
generated
vendored
Normal file
127
backend/node_modules/csso/cjs/index.cjs
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
'use strict';
|
||||
|
||||
const version = require('./version.cjs');
|
||||
const syntax = require('./syntax.cjs');
|
||||
const utils = require('./utils.cjs');
|
||||
|
||||
const { parse, generate, compress } = syntax;
|
||||
|
||||
function debugOutput(name, options, startTime, data) {
|
||||
if (options.debug) {
|
||||
console.error(`## ${name} done in %d ms\n`, Date.now() - startTime);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function createDefaultLogger(level) {
|
||||
let lastDebug;
|
||||
|
||||
return function logger(title, ast) {
|
||||
let line = title;
|
||||
|
||||
if (ast) {
|
||||
line = `[${((Date.now() - lastDebug) / 1000).toFixed(3)}s] ${line}`;
|
||||
}
|
||||
|
||||
if (level > 1 && ast) {
|
||||
let css = generate(ast);
|
||||
|
||||
// when level 2, limit css to 256 symbols
|
||||
if (level === 2 && css.length > 256) {
|
||||
css = css.substr(0, 256) + '...';
|
||||
}
|
||||
|
||||
line += `\n ${css}\n`;
|
||||
}
|
||||
|
||||
console.error(line);
|
||||
lastDebug = Date.now();
|
||||
};
|
||||
}
|
||||
|
||||
function buildCompressOptions(options) {
|
||||
options = { ...options };
|
||||
|
||||
if (typeof options.logger !== 'function' && options.debug) {
|
||||
options.logger = createDefaultLogger(options.debug);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function runHandler(ast, options, handlers) {
|
||||
if (!Array.isArray(handlers)) {
|
||||
handlers = [handlers];
|
||||
}
|
||||
|
||||
handlers.forEach(fn => fn(ast, options));
|
||||
}
|
||||
|
||||
function minify(context, source, options) {
|
||||
options = options || {};
|
||||
|
||||
const filename = options.filename || '<unknown>';
|
||||
let result;
|
||||
|
||||
// parse
|
||||
const ast = debugOutput('parsing', options, Date.now(),
|
||||
parse(source, {
|
||||
context,
|
||||
filename,
|
||||
positions: Boolean(options.sourceMap)
|
||||
})
|
||||
);
|
||||
|
||||
// before compress handlers
|
||||
if (options.beforeCompress) {
|
||||
debugOutput('beforeCompress', options, Date.now(),
|
||||
runHandler(ast, options, options.beforeCompress)
|
||||
);
|
||||
}
|
||||
|
||||
// compress
|
||||
const compressResult = debugOutput('compress', options, Date.now(),
|
||||
compress(ast, buildCompressOptions(options))
|
||||
);
|
||||
|
||||
// after compress handlers
|
||||
if (options.afterCompress) {
|
||||
debugOutput('afterCompress', options, Date.now(),
|
||||
runHandler(compressResult, options, options.afterCompress)
|
||||
);
|
||||
}
|
||||
|
||||
// generate
|
||||
if (options.sourceMap) {
|
||||
result = debugOutput('generate(sourceMap: true)', options, Date.now(), (() => {
|
||||
const tmp = generate(compressResult.ast, { sourceMap: true });
|
||||
|
||||
tmp.map._file = filename; // since other tools can relay on file in source map transform chain
|
||||
tmp.map.setSourceContent(filename, source);
|
||||
|
||||
return tmp;
|
||||
})());
|
||||
} else {
|
||||
result = debugOutput('generate', options, Date.now(), {
|
||||
css: generate(compressResult.ast),
|
||||
map: null
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function minifyStylesheet(source, options) {
|
||||
return minify('stylesheet', source, options);
|
||||
}
|
||||
|
||||
function minifyBlock(source, options) {
|
||||
return minify('declarationList', source, options);
|
||||
}
|
||||
|
||||
exports.version = version.version;
|
||||
exports.syntax = syntax;
|
||||
exports.utils = utils;
|
||||
exports.minify = minifyStylesheet;
|
||||
exports.minifyBlock = minifyBlock;
|
13
backend/node_modules/csso/cjs/replace/Atrule.cjs
generated
vendored
Normal file
13
backend/node_modules/csso/cjs/replace/Atrule.cjs
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const keyframes = require('./atrule/keyframes.cjs');
|
||||
|
||||
function Atrule(node) {
|
||||
// compress @keyframe selectors
|
||||
if (cssTree.keyword(node.name).basename === 'keyframes') {
|
||||
keyframes(node);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Atrule;
|
32
backend/node_modules/csso/cjs/replace/AttributeSelector.cjs
generated
vendored
Normal file
32
backend/node_modules/csso/cjs/replace/AttributeSelector.cjs
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
// Can unquote attribute detection
|
||||
// Adopted implementation of Mathias Bynens
|
||||
// https://github.com/mathiasbynens/mothereff.in/blob/master/unquoted-attributes/eff.js
|
||||
const blockUnquoteRx = /^(-?\d|--)|[\u0000-\u002c\u002e\u002f\u003A-\u0040\u005B-\u005E\u0060\u007B-\u009f]/;
|
||||
|
||||
function canUnquote(value) {
|
||||
if (value === '' || value === '-') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !blockUnquoteRx.test(value);
|
||||
}
|
||||
|
||||
function AttributeSelector(node) {
|
||||
const attrValue = node.value;
|
||||
|
||||
if (!attrValue || attrValue.type !== 'String') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (canUnquote(attrValue.value)) {
|
||||
node.value = {
|
||||
type: 'Identifier',
|
||||
loc: attrValue.loc,
|
||||
name: attrValue.value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AttributeSelector;
|
67
backend/node_modules/csso/cjs/replace/Dimension.cjs
generated
vendored
Normal file
67
backend/node_modules/csso/cjs/replace/Dimension.cjs
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
'use strict';
|
||||
|
||||
const _Number = require('./Number.cjs');
|
||||
|
||||
const MATH_FUNCTIONS = new Set([
|
||||
'calc',
|
||||
'min',
|
||||
'max',
|
||||
'clamp'
|
||||
]);
|
||||
const LENGTH_UNIT = new Set([
|
||||
// absolute length units
|
||||
'px',
|
||||
'mm',
|
||||
'cm',
|
||||
'in',
|
||||
'pt',
|
||||
'pc',
|
||||
|
||||
// relative length units
|
||||
'em',
|
||||
'ex',
|
||||
'ch',
|
||||
'rem',
|
||||
|
||||
// viewport-percentage lengths
|
||||
'vh',
|
||||
'vw',
|
||||
'vmin',
|
||||
'vmax',
|
||||
'vm'
|
||||
]);
|
||||
|
||||
function compressDimension(node, item) {
|
||||
const value = _Number.packNumber(node.value);
|
||||
|
||||
node.value = value;
|
||||
|
||||
if (value === '0' && this.declaration !== null && this.atrulePrelude === null) {
|
||||
const unit = node.unit.toLowerCase();
|
||||
|
||||
// only length values can be compressed
|
||||
if (!LENGTH_UNIT.has(unit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// issue #362: shouldn't remove unit in -ms-flex since it breaks flex in IE10/11
|
||||
// issue #200: shouldn't remove unit in flex since it breaks flex in IE10/11
|
||||
if (this.declaration.property === '-ms-flex' ||
|
||||
this.declaration.property === 'flex') {
|
||||
return;
|
||||
}
|
||||
|
||||
// issue #222: don't remove units inside calc
|
||||
if (this.function && MATH_FUNCTIONS.has(this.function.name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
item.data = {
|
||||
type: 'Number',
|
||||
loc: node.loc,
|
||||
value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = compressDimension;
|
45
backend/node_modules/csso/cjs/replace/Number.cjs
generated
vendored
Normal file
45
backend/node_modules/csso/cjs/replace/Number.cjs
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
'use strict';
|
||||
|
||||
const OMIT_PLUSSIGN = /^(?:\+|(-))?0*(\d*)(?:\.0*|(\.\d*?)0*)?$/;
|
||||
const KEEP_PLUSSIGN = /^([\+\-])?0*(\d*)(?:\.0*|(\.\d*?)0*)?$/;
|
||||
const unsafeToRemovePlusSignAfter = new Set([
|
||||
'Dimension',
|
||||
'Hash',
|
||||
'Identifier',
|
||||
'Number',
|
||||
'Raw',
|
||||
'UnicodeRange'
|
||||
]);
|
||||
|
||||
function packNumber(value, item) {
|
||||
// omit plus sign only if no prev or prev is safe type
|
||||
const regexp = item && item.prev !== null && unsafeToRemovePlusSignAfter.has(item.prev.data.type)
|
||||
? KEEP_PLUSSIGN
|
||||
: OMIT_PLUSSIGN;
|
||||
|
||||
// 100 -> '100'
|
||||
// 00100 -> '100'
|
||||
// +100 -> '100'
|
||||
// -100 -> '-100'
|
||||
// 0.123 -> '.123'
|
||||
// 0.12300 -> '.123'
|
||||
// 0.0 -> ''
|
||||
// 0 -> ''
|
||||
// -0 -> '-'
|
||||
value = String(value).replace(regexp, '$1$2$3');
|
||||
|
||||
if (value === '' || value === '-') {
|
||||
value = '0';
|
||||
}
|
||||
// FIXME: is it solution simplier?
|
||||
// value = String(Number(value)).replace(/^(-?)0+\./, '$1.');
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function Number(node) {
|
||||
node.value = packNumber(node.value);
|
||||
}
|
||||
|
||||
exports.Number = Number;
|
||||
exports.packNumber = packNumber;
|
41
backend/node_modules/csso/cjs/replace/Percentage.cjs
generated
vendored
Normal file
41
backend/node_modules/csso/cjs/replace/Percentage.cjs
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const _Number = require('./Number.cjs');
|
||||
|
||||
const blacklist = new Set([
|
||||
// see https://github.com/jakubpawlowicz/clean-css/issues/957
|
||||
'width',
|
||||
'min-width',
|
||||
'max-width',
|
||||
'height',
|
||||
'min-height',
|
||||
'max-height',
|
||||
|
||||
// issue #410: Don’t remove units in flex-basis value for (-ms-)flex shorthand
|
||||
// issue #362: shouldn't remove unit in -ms-flex since it breaks flex in IE10/11
|
||||
// issue #200: shouldn't remove unit in flex since it breaks flex in IE10/11
|
||||
'flex',
|
||||
'-ms-flex'
|
||||
]);
|
||||
|
||||
function compressPercentage(node, item) {
|
||||
node.value = _Number.packNumber(node.value);
|
||||
|
||||
if (node.value === '0' && this.declaration && !blacklist.has(this.declaration.property)) {
|
||||
// try to convert a number
|
||||
item.data = {
|
||||
type: 'Number',
|
||||
loc: node.loc,
|
||||
value: node.value
|
||||
};
|
||||
|
||||
// that's ok only when new value matches on length
|
||||
if (!cssTree.lexer.matchDeclaration(this.declaration).isType(item.data, 'length')) {
|
||||
// otherwise rollback changes
|
||||
item.data = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = compressPercentage;
|
8
backend/node_modules/csso/cjs/replace/Url.cjs
generated
vendored
Normal file
8
backend/node_modules/csso/cjs/replace/Url.cjs
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
function Url(node) {
|
||||
// convert `\\` to `/`
|
||||
node.value = node.value.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
module.exports = Url;
|
29
backend/node_modules/csso/cjs/replace/Value.cjs
generated
vendored
Normal file
29
backend/node_modules/csso/cjs/replace/Value.cjs
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const font = require('./property/font.cjs');
|
||||
const fontWeight = require('./property/font-weight.cjs');
|
||||
const background = require('./property/background.cjs');
|
||||
const border = require('./property/border.cjs');
|
||||
|
||||
const handlers = {
|
||||
'font': font,
|
||||
'font-weight': fontWeight,
|
||||
'background': background,
|
||||
'border': border,
|
||||
'outline': border
|
||||
};
|
||||
|
||||
function compressValue(node) {
|
||||
if (!this.declaration) {
|
||||
return;
|
||||
}
|
||||
|
||||
const property = cssTree.property(this.declaration.property);
|
||||
|
||||
if (handlers.hasOwnProperty(property.basename)) {
|
||||
handlers[property.basename](node);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = compressValue;
|
25
backend/node_modules/csso/cjs/replace/atrule/keyframes.cjs
generated
vendored
Normal file
25
backend/node_modules/csso/cjs/replace/atrule/keyframes.cjs
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
function compressKeyframes(node) {
|
||||
node.block.children.forEach((rule) => {
|
||||
rule.prelude.children.forEach((simpleselector) => {
|
||||
simpleselector.children.forEach((data, item) => {
|
||||
if (data.type === 'Percentage' && data.value === '100') {
|
||||
item.data = {
|
||||
type: 'TypeSelector',
|
||||
loc: data.loc,
|
||||
name: 'to'
|
||||
};
|
||||
} else if (data.type === 'TypeSelector' && data.name === 'from') {
|
||||
item.data = {
|
||||
type: 'Percentage',
|
||||
loc: data.loc,
|
||||
value: '0'
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = compressKeyframes;
|
504
backend/node_modules/csso/cjs/replace/color.cjs
generated
vendored
Normal file
504
backend/node_modules/csso/cjs/replace/color.cjs
generated
vendored
Normal file
@@ -0,0 +1,504 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const _Number = require('./Number.cjs');
|
||||
|
||||
// http://www.w3.org/TR/css3-color/#svg-color
|
||||
const NAME_TO_HEX = {
|
||||
'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',
|
||||
'darkgrey': 'a9a9a9',
|
||||
'darkgreen': '006400',
|
||||
'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',
|
||||
'grey': '808080',
|
||||
'green': '008000',
|
||||
'greenyellow': 'adff2f',
|
||||
'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',
|
||||
'lightgrey': 'd3d3d3',
|
||||
'lightgreen': '90ee90',
|
||||
'lightpink': 'ffb6c1',
|
||||
'lightsalmon': 'ffa07a',
|
||||
'lightseagreen': '20b2aa',
|
||||
'lightskyblue': '87cefa',
|
||||
'lightslategray': '789',
|
||||
'lightslategrey': '789',
|
||||
'lightsteelblue': 'b0c4de',
|
||||
'lightyellow': 'ffffe0',
|
||||
'lime': '0f0',
|
||||
'limegreen': '32cd32',
|
||||
'linen': 'faf0e6',
|
||||
'magenta': 'f0f',
|
||||
'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': '639',
|
||||
'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'
|
||||
};
|
||||
|
||||
const HEX_TO_NAME = {
|
||||
'800000': 'maroon',
|
||||
'800080': 'purple',
|
||||
'808000': 'olive',
|
||||
'808080': 'gray',
|
||||
'00ffff': 'cyan',
|
||||
'f0ffff': 'azure',
|
||||
'f5f5dc': 'beige',
|
||||
'ffe4c4': 'bisque',
|
||||
'000000': 'black',
|
||||
'0000ff': 'blue',
|
||||
'a52a2a': 'brown',
|
||||
'ff7f50': 'coral',
|
||||
'ffd700': 'gold',
|
||||
'008000': 'green',
|
||||
'4b0082': 'indigo',
|
||||
'fffff0': 'ivory',
|
||||
'f0e68c': 'khaki',
|
||||
'00ff00': 'lime',
|
||||
'faf0e6': 'linen',
|
||||
'000080': 'navy',
|
||||
'ffa500': 'orange',
|
||||
'da70d6': 'orchid',
|
||||
'cd853f': 'peru',
|
||||
'ffc0cb': 'pink',
|
||||
'dda0dd': 'plum',
|
||||
'f00': 'red',
|
||||
'ff0000': 'red',
|
||||
'fa8072': 'salmon',
|
||||
'a0522d': 'sienna',
|
||||
'c0c0c0': 'silver',
|
||||
'fffafa': 'snow',
|
||||
'd2b48c': 'tan',
|
||||
'008080': 'teal',
|
||||
'ff6347': 'tomato',
|
||||
'ee82ee': 'violet',
|
||||
'f5deb3': 'wheat',
|
||||
'ffffff': 'white',
|
||||
'ffff00': 'yellow'
|
||||
};
|
||||
|
||||
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 hslToRgb(h, s, l, a) {
|
||||
let r;
|
||||
let g;
|
||||
let b;
|
||||
|
||||
if (s === 0) {
|
||||
r = g = b = l; // achromatic
|
||||
} else {
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
const p = 2 * l - q;
|
||||
|
||||
r = hueToRgb(p, q, h + 1 / 3);
|
||||
g = hueToRgb(p, q, h);
|
||||
b = hueToRgb(p, q, h - 1 / 3);
|
||||
}
|
||||
|
||||
return [
|
||||
Math.round(r * 255),
|
||||
Math.round(g * 255),
|
||||
Math.round(b * 255),
|
||||
a
|
||||
];
|
||||
}
|
||||
|
||||
function toHex(value) {
|
||||
value = value.toString(16);
|
||||
|
||||
return value.length === 1 ? '0' + value : value;
|
||||
}
|
||||
|
||||
function parseFunctionArgs(functionArgs, count, rgb) {
|
||||
let cursor = functionArgs.head;
|
||||
let args = [];
|
||||
let wasValue = false;
|
||||
|
||||
while (cursor !== null) {
|
||||
const { type, value } = cursor.data;
|
||||
|
||||
switch (type) {
|
||||
case 'Number':
|
||||
case 'Percentage':
|
||||
if (wasValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
wasValue = true;
|
||||
args.push({
|
||||
type,
|
||||
value: Number(value)
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case 'Operator':
|
||||
if (value === ',') {
|
||||
if (!wasValue) {
|
||||
return;
|
||||
}
|
||||
wasValue = false;
|
||||
} else if (wasValue || value !== '+') {
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// something we couldn't understand
|
||||
return;
|
||||
}
|
||||
|
||||
cursor = cursor.next;
|
||||
}
|
||||
|
||||
if (args.length !== count) {
|
||||
// invalid arguments count
|
||||
// TODO: remove those tokens
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.length === 4) {
|
||||
if (args[3].type !== 'Number') {
|
||||
// 4th argument should be a number
|
||||
// TODO: remove those tokens
|
||||
return;
|
||||
}
|
||||
|
||||
args[3].type = 'Alpha';
|
||||
}
|
||||
|
||||
if (rgb) {
|
||||
if (args[0].type !== args[1].type || args[0].type !== args[2].type) {
|
||||
// invalid color, numbers and percentage shouldn't be mixed
|
||||
// TODO: remove those tokens
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (args[0].type !== 'Number' ||
|
||||
args[1].type !== 'Percentage' ||
|
||||
args[2].type !== 'Percentage') {
|
||||
// invalid color, for hsl values should be: number, percentage, percentage
|
||||
// TODO: remove those tokens
|
||||
return;
|
||||
}
|
||||
|
||||
args[0].type = 'Angle';
|
||||
}
|
||||
|
||||
return args.map(function(arg) {
|
||||
let value = Math.max(0, arg.value);
|
||||
|
||||
switch (arg.type) {
|
||||
case 'Number':
|
||||
// fit value to [0..255] range
|
||||
value = Math.min(value, 255);
|
||||
break;
|
||||
|
||||
case 'Percentage':
|
||||
// convert 0..100% to value in [0..255] range
|
||||
value = Math.min(value, 100) / 100;
|
||||
|
||||
if (!rgb) {
|
||||
return value;
|
||||
}
|
||||
|
||||
value = 255 * value;
|
||||
break;
|
||||
|
||||
case 'Angle':
|
||||
// fit value to (-360..360) range
|
||||
return (((value % 360) + 360) % 360) / 360;
|
||||
|
||||
case 'Alpha':
|
||||
// fit value to [0..1] range
|
||||
return Math.min(value, 1);
|
||||
}
|
||||
|
||||
return Math.round(value);
|
||||
});
|
||||
}
|
||||
|
||||
function compressFunction(node, item) {
|
||||
let functionName = node.name;
|
||||
let args;
|
||||
|
||||
if (functionName === 'rgba' || functionName === 'hsla') {
|
||||
args = parseFunctionArgs(node.children, 4, functionName === 'rgba');
|
||||
|
||||
if (!args) {
|
||||
// something went wrong
|
||||
return;
|
||||
}
|
||||
|
||||
if (functionName === 'hsla') {
|
||||
args = hslToRgb(...args);
|
||||
node.name = 'rgba';
|
||||
}
|
||||
|
||||
if (args[3] === 0) {
|
||||
// try to replace `rgba(x, x, x, 0)` to `transparent`
|
||||
// always replace `rgba(0, 0, 0, 0)` to `transparent`
|
||||
// otherwise avoid replacement in gradients since it may break color transition
|
||||
// http://stackoverflow.com/questions/11829410/css3-gradient-rendering-issues-from-transparent-to-white
|
||||
const scopeFunctionName = this.function && this.function.name;
|
||||
|
||||
if ((args[0] === 0 && args[1] === 0 && args[2] === 0) ||
|
||||
!/^(?:to|from|color-stop)$|gradient$/i.test(scopeFunctionName)) {
|
||||
|
||||
item.data = {
|
||||
type: 'Identifier',
|
||||
loc: node.loc,
|
||||
name: 'transparent'
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (args[3] !== 1) {
|
||||
// replace argument values for normalized/interpolated
|
||||
node.children.forEach((node, item, list) => {
|
||||
if (node.type === 'Operator') {
|
||||
if (node.value !== ',') {
|
||||
list.remove(item);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
item.data = {
|
||||
type: 'Number',
|
||||
loc: node.loc,
|
||||
value: _Number.packNumber(args.shift())
|
||||
};
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise convert to rgb, i.e. rgba(255, 0, 0, 1) -> rgb(255, 0, 0)
|
||||
functionName = 'rgb';
|
||||
}
|
||||
|
||||
if (functionName === 'hsl') {
|
||||
args = args || parseFunctionArgs(node.children, 3, false);
|
||||
|
||||
if (!args) {
|
||||
// something went wrong
|
||||
return;
|
||||
}
|
||||
|
||||
// convert to rgb
|
||||
args = hslToRgb(...args);
|
||||
functionName = 'rgb';
|
||||
}
|
||||
|
||||
if (functionName === 'rgb') {
|
||||
args = args || parseFunctionArgs(node.children, 3, true);
|
||||
|
||||
if (!args) {
|
||||
// something went wrong
|
||||
return;
|
||||
}
|
||||
|
||||
item.data = {
|
||||
type: 'Hash',
|
||||
loc: node.loc,
|
||||
value: toHex(args[0]) + toHex(args[1]) + toHex(args[2])
|
||||
};
|
||||
|
||||
compressHex(item.data, item);
|
||||
}
|
||||
}
|
||||
|
||||
function compressIdent(node, item) {
|
||||
if (this.declaration === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let color = node.name.toLowerCase();
|
||||
|
||||
if (NAME_TO_HEX.hasOwnProperty(color) &&
|
||||
cssTree.lexer.matchDeclaration(this.declaration).isType(node, 'color')) {
|
||||
const hex = NAME_TO_HEX[color];
|
||||
|
||||
if (hex.length + 1 <= color.length) {
|
||||
// replace for shorter hex value
|
||||
item.data = {
|
||||
type: 'Hash',
|
||||
loc: node.loc,
|
||||
value: hex
|
||||
};
|
||||
} else {
|
||||
// special case for consistent colors
|
||||
if (color === 'grey') {
|
||||
color = 'gray';
|
||||
}
|
||||
|
||||
// just replace value for lower cased name
|
||||
node.name = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function compressHex(node, item) {
|
||||
let color = node.value.toLowerCase();
|
||||
|
||||
// #112233 -> #123
|
||||
if (color.length === 6 &&
|
||||
color[0] === color[1] &&
|
||||
color[2] === color[3] &&
|
||||
color[4] === color[5]) {
|
||||
color = color[0] + color[2] + color[4];
|
||||
}
|
||||
|
||||
if (HEX_TO_NAME[color]) {
|
||||
item.data = {
|
||||
type: 'Identifier',
|
||||
loc: node.loc,
|
||||
name: HEX_TO_NAME[color]
|
||||
};
|
||||
} else {
|
||||
node.value = color;
|
||||
}
|
||||
}
|
||||
|
||||
exports.compressFunction = compressFunction;
|
||||
exports.compressHex = compressHex;
|
||||
exports.compressIdent = compressIdent;
|
36
backend/node_modules/csso/cjs/replace/index.cjs
generated
vendored
Normal file
36
backend/node_modules/csso/cjs/replace/index.cjs
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const Atrule = require('./Atrule.cjs');
|
||||
const AttributeSelector = require('./AttributeSelector.cjs');
|
||||
const Value = require('./Value.cjs');
|
||||
const Dimension = require('./Dimension.cjs');
|
||||
const Percentage = require('./Percentage.cjs');
|
||||
const _Number = require('./Number.cjs');
|
||||
const Url = require('./Url.cjs');
|
||||
const color = require('./color.cjs');
|
||||
|
||||
const handlers = {
|
||||
Atrule,
|
||||
AttributeSelector,
|
||||
Value,
|
||||
Dimension,
|
||||
Percentage,
|
||||
Number: _Number.Number,
|
||||
Url,
|
||||
Hash: color.compressHex,
|
||||
Identifier: color.compressIdent,
|
||||
Function: color.compressFunction
|
||||
};
|
||||
|
||||
function replace(ast) {
|
||||
cssTree.walk(ast, {
|
||||
leave(node, item, list) {
|
||||
if (handlers.hasOwnProperty(node.type)) {
|
||||
handlers[node.type].call(this, node, item, list);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = replace;
|
54
backend/node_modules/csso/cjs/replace/property/background.cjs
generated
vendored
Normal file
54
backend/node_modules/csso/cjs/replace/property/background.cjs
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
|
||||
function compressBackground(node) {
|
||||
function flush() {
|
||||
if (!buffer.length) {
|
||||
buffer.unshift(
|
||||
{
|
||||
type: 'Number',
|
||||
loc: null,
|
||||
value: '0'
|
||||
},
|
||||
{
|
||||
type: 'Number',
|
||||
loc: null,
|
||||
value: '0'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
newValue.push.apply(newValue, buffer);
|
||||
|
||||
buffer = [];
|
||||
}
|
||||
|
||||
let newValue = [];
|
||||
let buffer = [];
|
||||
|
||||
node.children.forEach((node) => {
|
||||
if (node.type === 'Operator' && node.value === ',') {
|
||||
flush();
|
||||
newValue.push(node);
|
||||
return;
|
||||
}
|
||||
|
||||
// remove defaults
|
||||
if (node.type === 'Identifier') {
|
||||
if (node.name === 'transparent' ||
|
||||
node.name === 'none' ||
|
||||
node.name === 'repeat' ||
|
||||
node.name === 'scroll') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
buffer.push(node);
|
||||
});
|
||||
|
||||
flush();
|
||||
node.children = new cssTree.List().fromArray(newValue);
|
||||
}
|
||||
|
||||
module.exports = compressBackground;
|
20
backend/node_modules/csso/cjs/replace/property/border.cjs
generated
vendored
Normal file
20
backend/node_modules/csso/cjs/replace/property/border.cjs
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
function compressBorder(node) {
|
||||
node.children.forEach((node, item, list) => {
|
||||
if (node.type === 'Identifier' && node.name.toLowerCase() === 'none') {
|
||||
if (list.head === list.tail) {
|
||||
// replace `none` for zero when `none` is a single term
|
||||
item.data = {
|
||||
type: 'Number',
|
||||
loc: node.loc,
|
||||
value: '0'
|
||||
};
|
||||
} else {
|
||||
list.remove(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = compressBorder;
|
26
backend/node_modules/csso/cjs/replace/property/font-weight.cjs
generated
vendored
Normal file
26
backend/node_modules/csso/cjs/replace/property/font-weight.cjs
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
function compressFontWeight(node) {
|
||||
const value = node.children.head.data;
|
||||
|
||||
if (value.type === 'Identifier') {
|
||||
switch (value.name) {
|
||||
case 'normal':
|
||||
node.children.head.data = {
|
||||
type: 'Number',
|
||||
loc: value.loc,
|
||||
value: '400'
|
||||
};
|
||||
break;
|
||||
case 'bold':
|
||||
node.children.head.data = {
|
||||
type: 'Number',
|
||||
loc: value.loc,
|
||||
value: '700'
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = compressFontWeight;
|
34
backend/node_modules/csso/cjs/replace/property/font.cjs
generated
vendored
Normal file
34
backend/node_modules/csso/cjs/replace/property/font.cjs
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
function compressFont(node) {
|
||||
const list = node.children;
|
||||
|
||||
list.forEachRight(function(node, item) {
|
||||
if (node.type === 'Identifier') {
|
||||
if (node.name === 'bold') {
|
||||
item.data = {
|
||||
type: 'Number',
|
||||
loc: node.loc,
|
||||
value: '700'
|
||||
};
|
||||
} else if (node.name === 'normal') {
|
||||
const prev = item.prev;
|
||||
|
||||
if (prev && prev.data.type === 'Operator' && prev.data.value === '/') {
|
||||
this.remove(prev);
|
||||
}
|
||||
|
||||
this.remove(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (list.isEmpty) {
|
||||
list.insert(list.createItem({
|
||||
type: 'Identifier',
|
||||
name: 'normal'
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = compressFont;
|
109
backend/node_modules/csso/cjs/restructure/1-mergeAtrule.cjs
generated
vendored
Normal file
109
backend/node_modules/csso/cjs/restructure/1-mergeAtrule.cjs
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
|
||||
const { hasOwnProperty } = Object.prototype;
|
||||
|
||||
function addRuleToMap(map, item, list, single) {
|
||||
const node = item.data;
|
||||
const name = cssTree.keyword(node.name).basename;
|
||||
const id = node.name.toLowerCase() + '/' + (node.prelude ? node.prelude.id : null);
|
||||
|
||||
if (!hasOwnProperty.call(map, name)) {
|
||||
map[name] = Object.create(null);
|
||||
}
|
||||
|
||||
if (single) {
|
||||
delete map[name][id];
|
||||
}
|
||||
|
||||
if (!hasOwnProperty.call(map[name], id)) {
|
||||
map[name][id] = new cssTree.List();
|
||||
}
|
||||
|
||||
map[name][id].append(list.remove(item));
|
||||
}
|
||||
|
||||
function relocateAtrules(ast, options) {
|
||||
const collected = Object.create(null);
|
||||
let topInjectPoint = null;
|
||||
|
||||
ast.children.forEach(function(node, item, list) {
|
||||
if (node.type === 'Atrule') {
|
||||
const name = cssTree.keyword(node.name).basename;
|
||||
|
||||
switch (name) {
|
||||
case 'keyframes':
|
||||
addRuleToMap(collected, item, list, true);
|
||||
return;
|
||||
|
||||
case 'media':
|
||||
if (options.forceMediaMerge) {
|
||||
addRuleToMap(collected, item, list, false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (topInjectPoint === null &&
|
||||
name !== 'charset' &&
|
||||
name !== 'import') {
|
||||
topInjectPoint = item;
|
||||
}
|
||||
} else {
|
||||
if (topInjectPoint === null) {
|
||||
topInjectPoint = item;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (const atrule in collected) {
|
||||
for (const id in collected[atrule]) {
|
||||
ast.children.insertList(
|
||||
collected[atrule][id],
|
||||
atrule === 'media' ? null : topInjectPoint
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
function isMediaRule(node) {
|
||||
return node.type === 'Atrule' && node.name === 'media';
|
||||
}
|
||||
|
||||
function processAtrule(node, item, list) {
|
||||
if (!isMediaRule(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const prev = item.prev && item.prev.data;
|
||||
|
||||
if (!prev || !isMediaRule(prev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// merge @media with same query
|
||||
if (node.prelude &&
|
||||
prev.prelude &&
|
||||
node.prelude.id === prev.prelude.id) {
|
||||
prev.block.children.appendList(node.block.children);
|
||||
list.remove(item);
|
||||
|
||||
// TODO: use it when we can refer to several points in source
|
||||
// prev.loc = {
|
||||
// primary: prev.loc,
|
||||
// merged: node.loc
|
||||
// };
|
||||
}
|
||||
}
|
||||
|
||||
function rejoinAtrule(ast, options) {
|
||||
relocateAtrules(ast, options);
|
||||
|
||||
cssTree.walk(ast, {
|
||||
visit: 'Atrule',
|
||||
reverse: true,
|
||||
enter: processAtrule
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = rejoinAtrule;
|
51
backend/node_modules/csso/cjs/restructure/2-initialMergeRuleset.cjs
generated
vendored
Normal file
51
backend/node_modules/csso/cjs/restructure/2-initialMergeRuleset.cjs
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const utils = require('./utils.cjs');
|
||||
|
||||
function processRule(node, item, list) {
|
||||
const selectors = node.prelude.children;
|
||||
const declarations = node.block.children;
|
||||
|
||||
list.prevUntil(item.prev, function(prev) {
|
||||
// skip non-ruleset node if safe
|
||||
if (prev.type !== 'Rule') {
|
||||
return utils.unsafeToSkipNode.call(selectors, prev);
|
||||
}
|
||||
|
||||
const prevSelectors = prev.prelude.children;
|
||||
const prevDeclarations = prev.block.children;
|
||||
|
||||
// try to join rulesets with equal pseudo signature
|
||||
if (node.pseudoSignature === prev.pseudoSignature) {
|
||||
// try to join by selectors
|
||||
if (utils.isEqualSelectors(prevSelectors, selectors)) {
|
||||
prevDeclarations.appendList(declarations);
|
||||
list.remove(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
// try to join by declarations
|
||||
if (utils.isEqualDeclarations(declarations, prevDeclarations)) {
|
||||
utils.addSelectors(prevSelectors, selectors);
|
||||
list.remove(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// go to prev ruleset if has no selector similarities
|
||||
return utils.hasSimilarSelectors(selectors, prevSelectors);
|
||||
});
|
||||
}
|
||||
|
||||
// NOTE: direction should be left to right, since rulesets merge to left
|
||||
// ruleset. When direction right to left unmerged rulesets may prevent lookup
|
||||
// TODO: remove initial merge
|
||||
function initialMergeRule(ast) {
|
||||
cssTree.walk(ast, {
|
||||
visit: 'Rule',
|
||||
enter: processRule
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = initialMergeRule;
|
46
backend/node_modules/csso/cjs/restructure/3-disjoinRuleset.cjs
generated
vendored
Normal file
46
backend/node_modules/csso/cjs/restructure/3-disjoinRuleset.cjs
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
|
||||
function processRule(node, item, list) {
|
||||
const selectors = node.prelude.children;
|
||||
|
||||
// generate new rule sets:
|
||||
// .a, .b { color: red; }
|
||||
// ->
|
||||
// .a { color: red; }
|
||||
// .b { color: red; }
|
||||
|
||||
// while there are more than 1 simple selector split for rulesets
|
||||
while (selectors.head !== selectors.tail) {
|
||||
const newSelectors = new cssTree.List();
|
||||
|
||||
newSelectors.insert(selectors.remove(selectors.head));
|
||||
|
||||
list.insert(list.createItem({
|
||||
type: 'Rule',
|
||||
loc: node.loc,
|
||||
prelude: {
|
||||
type: 'SelectorList',
|
||||
loc: node.prelude.loc,
|
||||
children: newSelectors
|
||||
},
|
||||
block: {
|
||||
type: 'Block',
|
||||
loc: node.block.loc,
|
||||
children: node.block.children.copy()
|
||||
},
|
||||
pseudoSignature: node.pseudoSignature
|
||||
}), item);
|
||||
}
|
||||
}
|
||||
|
||||
function disjoinRule(ast) {
|
||||
cssTree.walk(ast, {
|
||||
visit: 'Rule',
|
||||
reverse: true,
|
||||
enter: processRule
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = disjoinRule;
|
429
backend/node_modules/csso/cjs/restructure/4-restructShorthand.cjs
generated
vendored
Normal file
429
backend/node_modules/csso/cjs/restructure/4-restructShorthand.cjs
generated
vendored
Normal file
@@ -0,0 +1,429 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
|
||||
const REPLACE = 1;
|
||||
const REMOVE = 2;
|
||||
const TOP = 0;
|
||||
const RIGHT = 1;
|
||||
const BOTTOM = 2;
|
||||
const LEFT = 3;
|
||||
const SIDES = ['top', 'right', 'bottom', 'left'];
|
||||
const SIDE = {
|
||||
'margin-top': 'top',
|
||||
'margin-right': 'right',
|
||||
'margin-bottom': 'bottom',
|
||||
'margin-left': 'left',
|
||||
|
||||
'padding-top': 'top',
|
||||
'padding-right': 'right',
|
||||
'padding-bottom': 'bottom',
|
||||
'padding-left': 'left',
|
||||
|
||||
'border-top-color': 'top',
|
||||
'border-right-color': 'right',
|
||||
'border-bottom-color': 'bottom',
|
||||
'border-left-color': 'left',
|
||||
'border-top-width': 'top',
|
||||
'border-right-width': 'right',
|
||||
'border-bottom-width': 'bottom',
|
||||
'border-left-width': 'left',
|
||||
'border-top-style': 'top',
|
||||
'border-right-style': 'right',
|
||||
'border-bottom-style': 'bottom',
|
||||
'border-left-style': 'left'
|
||||
};
|
||||
const MAIN_PROPERTY = {
|
||||
'margin': 'margin',
|
||||
'margin-top': 'margin',
|
||||
'margin-right': 'margin',
|
||||
'margin-bottom': 'margin',
|
||||
'margin-left': 'margin',
|
||||
|
||||
'padding': 'padding',
|
||||
'padding-top': 'padding',
|
||||
'padding-right': 'padding',
|
||||
'padding-bottom': 'padding',
|
||||
'padding-left': 'padding',
|
||||
|
||||
'border-color': 'border-color',
|
||||
'border-top-color': 'border-color',
|
||||
'border-right-color': 'border-color',
|
||||
'border-bottom-color': 'border-color',
|
||||
'border-left-color': 'border-color',
|
||||
'border-width': 'border-width',
|
||||
'border-top-width': 'border-width',
|
||||
'border-right-width': 'border-width',
|
||||
'border-bottom-width': 'border-width',
|
||||
'border-left-width': 'border-width',
|
||||
'border-style': 'border-style',
|
||||
'border-top-style': 'border-style',
|
||||
'border-right-style': 'border-style',
|
||||
'border-bottom-style': 'border-style',
|
||||
'border-left-style': 'border-style'
|
||||
};
|
||||
|
||||
class TRBL {
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
this.loc = null;
|
||||
this.iehack = undefined;
|
||||
this.sides = {
|
||||
'top': null,
|
||||
'right': null,
|
||||
'bottom': null,
|
||||
'left': null
|
||||
};
|
||||
}
|
||||
|
||||
getValueSequence(declaration, count) {
|
||||
const values = [];
|
||||
let iehack = '';
|
||||
const hasBadValues = declaration.value.type !== 'Value' || declaration.value.children.some(function(child) {
|
||||
let special = false;
|
||||
|
||||
switch (child.type) {
|
||||
case 'Identifier':
|
||||
switch (child.name) {
|
||||
case '\\0':
|
||||
case '\\9':
|
||||
iehack = child.name;
|
||||
return;
|
||||
|
||||
case 'inherit':
|
||||
case 'initial':
|
||||
case 'unset':
|
||||
case 'revert':
|
||||
special = child.name;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Dimension':
|
||||
switch (child.unit) {
|
||||
// is not supported until IE11
|
||||
case 'rem':
|
||||
|
||||
// v* units is too buggy across browsers and better
|
||||
// don't merge values with those units
|
||||
case 'vw':
|
||||
case 'vh':
|
||||
case 'vmin':
|
||||
case 'vmax':
|
||||
case 'vm': // IE9 supporting "vm" instead of "vmin".
|
||||
special = child.unit;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Hash': // color
|
||||
case 'Number':
|
||||
case 'Percentage':
|
||||
break;
|
||||
|
||||
case 'Function':
|
||||
if (child.name === 'var') {
|
||||
return true;
|
||||
}
|
||||
|
||||
special = child.name;
|
||||
break;
|
||||
|
||||
default:
|
||||
return true; // bad value
|
||||
}
|
||||
|
||||
values.push({
|
||||
node: child,
|
||||
special,
|
||||
important: declaration.important
|
||||
});
|
||||
});
|
||||
|
||||
if (hasBadValues || values.length > count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof this.iehack === 'string' && this.iehack !== iehack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.iehack = iehack; // move outside
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
canOverride(side, value) {
|
||||
const currentValue = this.sides[side];
|
||||
|
||||
return !currentValue || (value.important && !currentValue.important);
|
||||
}
|
||||
|
||||
add(name, declaration) {
|
||||
function attemptToAdd() {
|
||||
const sides = this.sides;
|
||||
const side = SIDE[name];
|
||||
|
||||
if (side) {
|
||||
if (side in sides === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const values = this.getValueSequence(declaration, 1);
|
||||
|
||||
if (!values || !values.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// can mix only if specials are equal
|
||||
for (const key in sides) {
|
||||
if (sides[key] !== null && sides[key].special !== values[0].special) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.canOverride(side, values[0])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
sides[side] = values[0];
|
||||
|
||||
return true;
|
||||
} else if (name === this.name) {
|
||||
const values = this.getValueSequence(declaration, 4);
|
||||
|
||||
if (!values || !values.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (values.length) {
|
||||
case 1:
|
||||
values[RIGHT] = values[TOP];
|
||||
values[BOTTOM] = values[TOP];
|
||||
values[LEFT] = values[TOP];
|
||||
break;
|
||||
|
||||
case 2:
|
||||
values[BOTTOM] = values[TOP];
|
||||
values[LEFT] = values[RIGHT];
|
||||
break;
|
||||
|
||||
case 3:
|
||||
values[LEFT] = values[RIGHT];
|
||||
break;
|
||||
}
|
||||
|
||||
// can mix only if specials are equal
|
||||
for (let i = 0; i < 4; i++) {
|
||||
for (const key in sides) {
|
||||
if (sides[key] !== null && sides[key].special !== values[i].special) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (this.canOverride(SIDES[i], values[i])) {
|
||||
sides[SIDES[i]] = values[i];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!attemptToAdd.call(this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: use it when we can refer to several points in source
|
||||
// if (this.loc) {
|
||||
// this.loc = {
|
||||
// primary: this.loc,
|
||||
// merged: declaration.loc
|
||||
// };
|
||||
// } else {
|
||||
// this.loc = declaration.loc;
|
||||
// }
|
||||
if (!this.loc) {
|
||||
this.loc = declaration.loc;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
isOkToMinimize() {
|
||||
const top = this.sides.top;
|
||||
const right = this.sides.right;
|
||||
const bottom = this.sides.bottom;
|
||||
const left = this.sides.left;
|
||||
|
||||
if (top && right && bottom && left) {
|
||||
const important =
|
||||
top.important +
|
||||
right.important +
|
||||
bottom.important +
|
||||
left.important;
|
||||
|
||||
return important === 0 || important === 4;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
getValue() {
|
||||
const result = new cssTree.List();
|
||||
const sides = this.sides;
|
||||
const values = [
|
||||
sides.top,
|
||||
sides.right,
|
||||
sides.bottom,
|
||||
sides.left
|
||||
];
|
||||
const stringValues = [
|
||||
cssTree.generate(sides.top.node),
|
||||
cssTree.generate(sides.right.node),
|
||||
cssTree.generate(sides.bottom.node),
|
||||
cssTree.generate(sides.left.node)
|
||||
];
|
||||
|
||||
if (stringValues[LEFT] === stringValues[RIGHT]) {
|
||||
values.pop();
|
||||
if (stringValues[BOTTOM] === stringValues[TOP]) {
|
||||
values.pop();
|
||||
if (stringValues[RIGHT] === stringValues[TOP]) {
|
||||
values.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
result.appendData(values[i].node);
|
||||
}
|
||||
|
||||
if (this.iehack) {
|
||||
result.appendData({
|
||||
type: 'Identifier',
|
||||
loc: null,
|
||||
name: this.iehack
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'Value',
|
||||
loc: null,
|
||||
children: result
|
||||
};
|
||||
}
|
||||
|
||||
getDeclaration() {
|
||||
return {
|
||||
type: 'Declaration',
|
||||
loc: this.loc,
|
||||
important: this.sides.top.important,
|
||||
property: this.name,
|
||||
value: this.getValue()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function processRule(rule, shorts, shortDeclarations, lastShortSelector) {
|
||||
const declarations = rule.block.children;
|
||||
const selector = rule.prelude.children.first.id;
|
||||
|
||||
rule.block.children.forEachRight(function(declaration, item) {
|
||||
const property = declaration.property;
|
||||
|
||||
if (!MAIN_PROPERTY.hasOwnProperty(property)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = MAIN_PROPERTY[property];
|
||||
let shorthand;
|
||||
let operation;
|
||||
|
||||
if (!lastShortSelector || selector === lastShortSelector) {
|
||||
if (key in shorts) {
|
||||
operation = REMOVE;
|
||||
shorthand = shorts[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (!shorthand || !shorthand.add(property, declaration)) {
|
||||
operation = REPLACE;
|
||||
shorthand = new TRBL(key);
|
||||
|
||||
// if can't parse value ignore it and break shorthand children
|
||||
if (!shorthand.add(property, declaration)) {
|
||||
lastShortSelector = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
shorts[key] = shorthand;
|
||||
shortDeclarations.push({
|
||||
operation,
|
||||
block: declarations,
|
||||
item,
|
||||
shorthand
|
||||
});
|
||||
|
||||
lastShortSelector = selector;
|
||||
});
|
||||
|
||||
return lastShortSelector;
|
||||
}
|
||||
|
||||
function processShorthands(shortDeclarations, markDeclaration) {
|
||||
shortDeclarations.forEach(function(item) {
|
||||
const shorthand = item.shorthand;
|
||||
|
||||
if (!shorthand.isOkToMinimize()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.operation === REPLACE) {
|
||||
item.item.data = markDeclaration(shorthand.getDeclaration());
|
||||
} else {
|
||||
item.block.remove(item.item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function restructBlock(ast, indexer) {
|
||||
const stylesheetMap = {};
|
||||
const shortDeclarations = [];
|
||||
|
||||
cssTree.walk(ast, {
|
||||
visit: 'Rule',
|
||||
reverse: true,
|
||||
enter(node) {
|
||||
const stylesheet = this.block || this.stylesheet;
|
||||
const ruleId = (node.pseudoSignature || '') + '|' + node.prelude.children.first.id;
|
||||
let ruleMap;
|
||||
let shorts;
|
||||
|
||||
if (!stylesheetMap.hasOwnProperty(stylesheet.id)) {
|
||||
ruleMap = {
|
||||
lastShortSelector: null
|
||||
};
|
||||
stylesheetMap[stylesheet.id] = ruleMap;
|
||||
} else {
|
||||
ruleMap = stylesheetMap[stylesheet.id];
|
||||
}
|
||||
|
||||
if (ruleMap.hasOwnProperty(ruleId)) {
|
||||
shorts = ruleMap[ruleId];
|
||||
} else {
|
||||
shorts = {};
|
||||
ruleMap[ruleId] = shorts;
|
||||
}
|
||||
|
||||
ruleMap.lastShortSelector = processRule.call(this, node, shorts, shortDeclarations, ruleMap.lastShortSelector);
|
||||
}
|
||||
});
|
||||
|
||||
processShorthands(shortDeclarations, indexer.declaration);
|
||||
}
|
||||
|
||||
module.exports = restructBlock;
|
307
backend/node_modules/csso/cjs/restructure/6-restructBlock.cjs
generated
vendored
Normal file
307
backend/node_modules/csso/cjs/restructure/6-restructBlock.cjs
generated
vendored
Normal file
@@ -0,0 +1,307 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
|
||||
let fingerprintId = 1;
|
||||
const dontRestructure = new Set([
|
||||
'src' // https://github.com/afelix/csso/issues/50
|
||||
]);
|
||||
|
||||
const DONT_MIX_VALUE = {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/display#Browser_compatibility
|
||||
'display': /table|ruby|flex|-(flex)?box$|grid|contents|run-in/i,
|
||||
// https://developer.mozilla.org/en/docs/Web/CSS/text-align
|
||||
'text-align': /^(start|end|match-parent|justify-all)$/i
|
||||
};
|
||||
|
||||
const SAFE_VALUES = {
|
||||
cursor: [
|
||||
'auto', 'crosshair', 'default', 'move', 'text', 'wait', 'help',
|
||||
'n-resize', 'e-resize', 's-resize', 'w-resize',
|
||||
'ne-resize', 'nw-resize', 'se-resize', 'sw-resize',
|
||||
'pointer', 'progress', 'not-allowed', 'no-drop', 'vertical-text', 'all-scroll',
|
||||
'col-resize', 'row-resize'
|
||||
],
|
||||
overflow: [
|
||||
'hidden', 'visible', 'scroll', 'auto'
|
||||
],
|
||||
position: [
|
||||
'static', 'relative', 'absolute', 'fixed'
|
||||
]
|
||||
};
|
||||
|
||||
const NEEDLESS_TABLE = {
|
||||
'border-width': ['border'],
|
||||
'border-style': ['border'],
|
||||
'border-color': ['border'],
|
||||
'border-top': ['border'],
|
||||
'border-right': ['border'],
|
||||
'border-bottom': ['border'],
|
||||
'border-left': ['border'],
|
||||
'border-top-width': ['border-top', 'border-width', 'border'],
|
||||
'border-right-width': ['border-right', 'border-width', 'border'],
|
||||
'border-bottom-width': ['border-bottom', 'border-width', 'border'],
|
||||
'border-left-width': ['border-left', 'border-width', 'border'],
|
||||
'border-top-style': ['border-top', 'border-style', 'border'],
|
||||
'border-right-style': ['border-right', 'border-style', 'border'],
|
||||
'border-bottom-style': ['border-bottom', 'border-style', 'border'],
|
||||
'border-left-style': ['border-left', 'border-style', 'border'],
|
||||
'border-top-color': ['border-top', 'border-color', 'border'],
|
||||
'border-right-color': ['border-right', 'border-color', 'border'],
|
||||
'border-bottom-color': ['border-bottom', 'border-color', 'border'],
|
||||
'border-left-color': ['border-left', 'border-color', 'border'],
|
||||
'margin-top': ['margin'],
|
||||
'margin-right': ['margin'],
|
||||
'margin-bottom': ['margin'],
|
||||
'margin-left': ['margin'],
|
||||
'padding-top': ['padding'],
|
||||
'padding-right': ['padding'],
|
||||
'padding-bottom': ['padding'],
|
||||
'padding-left': ['padding'],
|
||||
'font-style': ['font'],
|
||||
'font-variant': ['font'],
|
||||
'font-weight': ['font'],
|
||||
'font-size': ['font'],
|
||||
'font-family': ['font'],
|
||||
'list-style-type': ['list-style'],
|
||||
'list-style-position': ['list-style'],
|
||||
'list-style-image': ['list-style']
|
||||
};
|
||||
|
||||
function getPropertyFingerprint(propertyName, declaration, fingerprints) {
|
||||
const realName = cssTree.property(propertyName).basename;
|
||||
|
||||
if (realName === 'background') {
|
||||
return propertyName + ':' + cssTree.generate(declaration.value);
|
||||
}
|
||||
|
||||
const declarationId = declaration.id;
|
||||
let fingerprint = fingerprints[declarationId];
|
||||
|
||||
if (!fingerprint) {
|
||||
switch (declaration.value.type) {
|
||||
case 'Value':
|
||||
const special = {};
|
||||
let vendorId = '';
|
||||
let iehack = '';
|
||||
let raw = false;
|
||||
|
||||
declaration.value.children.forEach(function walk(node) {
|
||||
switch (node.type) {
|
||||
case 'Value':
|
||||
case 'Brackets':
|
||||
case 'Parentheses':
|
||||
node.children.forEach(walk);
|
||||
break;
|
||||
|
||||
case 'Raw':
|
||||
raw = true;
|
||||
break;
|
||||
|
||||
case 'Identifier': {
|
||||
const { name } = node;
|
||||
|
||||
if (!vendorId) {
|
||||
vendorId = cssTree.keyword(name).vendor;
|
||||
}
|
||||
|
||||
if (/\\[09]/.test(name)) {
|
||||
iehack = RegExp.lastMatch;
|
||||
}
|
||||
|
||||
if (SAFE_VALUES.hasOwnProperty(realName)) {
|
||||
if (SAFE_VALUES[realName].indexOf(name) === -1) {
|
||||
special[name] = true;
|
||||
}
|
||||
} else if (DONT_MIX_VALUE.hasOwnProperty(realName)) {
|
||||
if (DONT_MIX_VALUE[realName].test(name)) {
|
||||
special[name] = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Function': {
|
||||
let { name } = node;
|
||||
|
||||
if (!vendorId) {
|
||||
vendorId = cssTree.keyword(name).vendor;
|
||||
}
|
||||
|
||||
if (name === 'rect') {
|
||||
// there are 2 forms of rect:
|
||||
// rect(<top>, <right>, <bottom>, <left>) - standart
|
||||
// rect(<top> <right> <bottom> <left>) – backwards compatible syntax
|
||||
// only the same form values can be merged
|
||||
const hasComma = node.children.some((node) =>
|
||||
node.type === 'Operator' && node.value === ','
|
||||
);
|
||||
|
||||
if (!hasComma) {
|
||||
name = 'rect-backward';
|
||||
}
|
||||
}
|
||||
|
||||
special[name + '()'] = true;
|
||||
|
||||
// check nested tokens too
|
||||
node.children.forEach(walk);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Dimension': {
|
||||
const { unit } = node;
|
||||
|
||||
if (/\\[09]/.test(unit)) {
|
||||
iehack = RegExp.lastMatch;
|
||||
}
|
||||
|
||||
switch (unit) {
|
||||
// is not supported until IE11
|
||||
case 'rem':
|
||||
|
||||
// v* units is too buggy across browsers and better
|
||||
// don't merge values with those units
|
||||
case 'vw':
|
||||
case 'vh':
|
||||
case 'vmin':
|
||||
case 'vmax':
|
||||
case 'vm': // IE9 supporting "vm" instead of "vmin".
|
||||
special[unit] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fingerprint = raw
|
||||
? '!' + fingerprintId++
|
||||
: '!' + Object.keys(special).sort() + '|' + iehack + vendorId;
|
||||
break;
|
||||
|
||||
case 'Raw':
|
||||
fingerprint = '!' + declaration.value.value;
|
||||
break;
|
||||
|
||||
default:
|
||||
fingerprint = cssTree.generate(declaration.value);
|
||||
}
|
||||
|
||||
fingerprints[declarationId] = fingerprint;
|
||||
}
|
||||
|
||||
return propertyName + fingerprint;
|
||||
}
|
||||
|
||||
function needless(props, declaration, fingerprints) {
|
||||
const property = cssTree.property(declaration.property);
|
||||
|
||||
if (NEEDLESS_TABLE.hasOwnProperty(property.basename)) {
|
||||
const table = NEEDLESS_TABLE[property.basename];
|
||||
|
||||
for (const entry of table) {
|
||||
const ppre = getPropertyFingerprint(property.prefix + entry, declaration, fingerprints);
|
||||
const prev = props.hasOwnProperty(ppre) ? props[ppre] : null;
|
||||
|
||||
if (prev && (!declaration.important || prev.item.data.important)) {
|
||||
return prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processRule(rule, item, list, props, fingerprints) {
|
||||
const declarations = rule.block.children;
|
||||
|
||||
declarations.forEachRight(function(declaration, declarationItem) {
|
||||
const { property } = declaration;
|
||||
const fingerprint = getPropertyFingerprint(property, declaration, fingerprints);
|
||||
const prev = props[fingerprint];
|
||||
|
||||
if (prev && !dontRestructure.has(property)) {
|
||||
if (declaration.important && !prev.item.data.important) {
|
||||
props[fingerprint] = {
|
||||
block: declarations,
|
||||
item: declarationItem
|
||||
};
|
||||
|
||||
prev.block.remove(prev.item);
|
||||
|
||||
// TODO: use it when we can refer to several points in source
|
||||
// declaration.loc = {
|
||||
// primary: declaration.loc,
|
||||
// merged: prev.item.data.loc
|
||||
// };
|
||||
} else {
|
||||
declarations.remove(declarationItem);
|
||||
|
||||
// TODO: use it when we can refer to several points in source
|
||||
// prev.item.data.loc = {
|
||||
// primary: prev.item.data.loc,
|
||||
// merged: declaration.loc
|
||||
// };
|
||||
}
|
||||
} else {
|
||||
const prev = needless(props, declaration, fingerprints);
|
||||
|
||||
if (prev) {
|
||||
declarations.remove(declarationItem);
|
||||
|
||||
// TODO: use it when we can refer to several points in source
|
||||
// prev.item.data.loc = {
|
||||
// primary: prev.item.data.loc,
|
||||
// merged: declaration.loc
|
||||
// };
|
||||
} else {
|
||||
declaration.fingerprint = fingerprint;
|
||||
|
||||
props[fingerprint] = {
|
||||
block: declarations,
|
||||
item: declarationItem
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (declarations.isEmpty) {
|
||||
list.remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
function restructBlock(ast) {
|
||||
const stylesheetMap = {};
|
||||
const fingerprints = Object.create(null);
|
||||
|
||||
cssTree.walk(ast, {
|
||||
visit: 'Rule',
|
||||
reverse: true,
|
||||
enter(node, item, list) {
|
||||
const stylesheet = this.block || this.stylesheet;
|
||||
const ruleId = (node.pseudoSignature || '') + '|' + node.prelude.children.first.id;
|
||||
let ruleMap;
|
||||
let props;
|
||||
|
||||
if (!stylesheetMap.hasOwnProperty(stylesheet.id)) {
|
||||
ruleMap = {};
|
||||
stylesheetMap[stylesheet.id] = ruleMap;
|
||||
} else {
|
||||
ruleMap = stylesheetMap[stylesheet.id];
|
||||
}
|
||||
|
||||
if (ruleMap.hasOwnProperty(ruleId)) {
|
||||
props = ruleMap[ruleId];
|
||||
} else {
|
||||
props = {};
|
||||
ruleMap[ruleId] = props;
|
||||
}
|
||||
|
||||
processRule.call(this, node, item, list, props, fingerprints);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = restructBlock;
|
90
backend/node_modules/csso/cjs/restructure/7-mergeRuleset.cjs
generated
vendored
Normal file
90
backend/node_modules/csso/cjs/restructure/7-mergeRuleset.cjs
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const utils = require('./utils.cjs');
|
||||
|
||||
/*
|
||||
At this step all rules has single simple selector. We try to join by equal
|
||||
declaration blocks to first rule, e.g.
|
||||
|
||||
.a { color: red }
|
||||
b { ... }
|
||||
.b { color: red }
|
||||
->
|
||||
.a, .b { color: red }
|
||||
b { ... }
|
||||
*/
|
||||
|
||||
function processRule(node, item, list) {
|
||||
const selectors = node.prelude.children;
|
||||
const declarations = node.block.children;
|
||||
const nodeCompareMarker = selectors.first.compareMarker;
|
||||
const skippedCompareMarkers = {};
|
||||
|
||||
list.nextUntil(item.next, function(next, nextItem) {
|
||||
// skip non-ruleset node if safe
|
||||
if (next.type !== 'Rule') {
|
||||
return utils.unsafeToSkipNode.call(selectors, next);
|
||||
}
|
||||
|
||||
if (node.pseudoSignature !== next.pseudoSignature) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const nextFirstSelector = next.prelude.children.head;
|
||||
const nextDeclarations = next.block.children;
|
||||
const nextCompareMarker = nextFirstSelector.data.compareMarker;
|
||||
|
||||
// if next ruleset has same marked as one of skipped then stop joining
|
||||
if (nextCompareMarker in skippedCompareMarkers) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// try to join by selectors
|
||||
if (selectors.head === selectors.tail) {
|
||||
if (selectors.first.id === nextFirstSelector.data.id) {
|
||||
declarations.appendList(nextDeclarations);
|
||||
list.remove(nextItem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// try to join by properties
|
||||
if (utils.isEqualDeclarations(declarations, nextDeclarations)) {
|
||||
const nextStr = nextFirstSelector.data.id;
|
||||
|
||||
selectors.some((data, item) => {
|
||||
const curStr = data.id;
|
||||
|
||||
if (nextStr < curStr) {
|
||||
selectors.insert(nextFirstSelector, item);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!item.next) {
|
||||
selectors.insert(nextFirstSelector);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
list.remove(nextItem);
|
||||
return;
|
||||
}
|
||||
|
||||
// go to next ruleset if current one can be skipped (has no equal specificity nor element selector)
|
||||
if (nextCompareMarker === nodeCompareMarker) {
|
||||
return true;
|
||||
}
|
||||
|
||||
skippedCompareMarkers[nextCompareMarker] = true;
|
||||
});
|
||||
}
|
||||
|
||||
function mergeRule(ast) {
|
||||
cssTree.walk(ast, {
|
||||
visit: 'Rule',
|
||||
enter: processRule
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = mergeRule;
|
175
backend/node_modules/csso/cjs/restructure/8-restructRuleset.cjs
generated
vendored
Normal file
175
backend/node_modules/csso/cjs/restructure/8-restructRuleset.cjs
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const utils = require('./utils.cjs');
|
||||
|
||||
function calcSelectorLength(list) {
|
||||
return list.reduce((res, data) => res + data.id.length + 1, 0) - 1;
|
||||
}
|
||||
|
||||
function calcDeclarationsLength(tokens) {
|
||||
let length = 0;
|
||||
|
||||
for (const token of tokens) {
|
||||
length += token.length;
|
||||
}
|
||||
|
||||
return (
|
||||
length + // declarations
|
||||
tokens.length - 1 // delimeters
|
||||
);
|
||||
}
|
||||
|
||||
function processRule(node, item, list) {
|
||||
const avoidRulesMerge = this.block !== null ? this.block.avoidRulesMerge : false;
|
||||
const selectors = node.prelude.children;
|
||||
const block = node.block;
|
||||
const disallowDownMarkers = Object.create(null);
|
||||
let allowMergeUp = true;
|
||||
let allowMergeDown = true;
|
||||
|
||||
list.prevUntil(item.prev, function(prev, prevItem) {
|
||||
const prevBlock = prev.block;
|
||||
const prevType = prev.type;
|
||||
|
||||
if (prevType !== 'Rule') {
|
||||
const unsafe = utils.unsafeToSkipNode.call(selectors, prev);
|
||||
|
||||
if (!unsafe && prevType === 'Atrule' && prevBlock) {
|
||||
cssTree.walk(prevBlock, {
|
||||
visit: 'Rule',
|
||||
enter(node) {
|
||||
node.prelude.children.forEach((data) => {
|
||||
disallowDownMarkers[data.compareMarker] = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return unsafe;
|
||||
}
|
||||
|
||||
if (node.pseudoSignature !== prev.pseudoSignature) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const prevSelectors = prev.prelude.children;
|
||||
|
||||
allowMergeDown = !prevSelectors.some((selector) =>
|
||||
selector.compareMarker in disallowDownMarkers
|
||||
);
|
||||
|
||||
// try prev ruleset if simpleselectors has no equal specifity and element selector
|
||||
if (!allowMergeDown && !allowMergeUp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// try to join by selectors
|
||||
if (allowMergeUp && utils.isEqualSelectors(prevSelectors, selectors)) {
|
||||
prevBlock.children.appendList(block.children);
|
||||
list.remove(item);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// try to join by properties
|
||||
const diff = utils.compareDeclarations(block.children, prevBlock.children);
|
||||
|
||||
// console.log(diff.eq, diff.ne1, diff.ne2);
|
||||
|
||||
if (diff.eq.length) {
|
||||
if (!diff.ne1.length && !diff.ne2.length) {
|
||||
// equal blocks
|
||||
if (allowMergeDown) {
|
||||
utils.addSelectors(selectors, prevSelectors);
|
||||
list.remove(prevItem);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (!avoidRulesMerge) { /* probably we don't need to prevent those merges for @keyframes
|
||||
TODO: need to be checked */
|
||||
|
||||
if (diff.ne1.length && !diff.ne2.length) {
|
||||
// prevBlock is subset block
|
||||
const selectorLength = calcSelectorLength(selectors);
|
||||
const blockLength = calcDeclarationsLength(diff.eq); // declarations length
|
||||
|
||||
if (allowMergeUp && selectorLength < blockLength) {
|
||||
utils.addSelectors(prevSelectors, selectors);
|
||||
block.children.fromArray(diff.ne1);
|
||||
}
|
||||
} else if (!diff.ne1.length && diff.ne2.length) {
|
||||
// node is subset of prevBlock
|
||||
const selectorLength = calcSelectorLength(prevSelectors);
|
||||
const blockLength = calcDeclarationsLength(diff.eq); // declarations length
|
||||
|
||||
if (allowMergeDown && selectorLength < blockLength) {
|
||||
utils.addSelectors(selectors, prevSelectors);
|
||||
prevBlock.children.fromArray(diff.ne2);
|
||||
}
|
||||
} else {
|
||||
// diff.ne1.length && diff.ne2.length
|
||||
// extract equal block
|
||||
const newSelector = {
|
||||
type: 'SelectorList',
|
||||
loc: null,
|
||||
children: utils.addSelectors(prevSelectors.copy(), selectors)
|
||||
};
|
||||
const newBlockLength = calcSelectorLength(newSelector.children) + 2; // selectors length + curly braces length
|
||||
const blockLength = calcDeclarationsLength(diff.eq); // declarations length
|
||||
|
||||
// create new ruleset if declarations length greater than
|
||||
// ruleset description overhead
|
||||
if (blockLength >= newBlockLength) {
|
||||
const newItem = list.createItem({
|
||||
type: 'Rule',
|
||||
loc: null,
|
||||
prelude: newSelector,
|
||||
block: {
|
||||
type: 'Block',
|
||||
loc: null,
|
||||
children: new cssTree.List().fromArray(diff.eq)
|
||||
},
|
||||
pseudoSignature: node.pseudoSignature
|
||||
});
|
||||
|
||||
block.children.fromArray(diff.ne1);
|
||||
prevBlock.children.fromArray(diff.ne2overrided);
|
||||
|
||||
if (allowMergeUp) {
|
||||
list.insert(newItem, prevItem);
|
||||
} else {
|
||||
list.insert(newItem, item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allowMergeUp) {
|
||||
// TODO: disallow up merge only if any property interception only (i.e. diff.ne2overrided.length > 0);
|
||||
// await property families to find property interception correctly
|
||||
allowMergeUp = !prevSelectors.some((prevSelector) =>
|
||||
selectors.some((selector) =>
|
||||
selector.compareMarker === prevSelector.compareMarker
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
prevSelectors.forEach((data) => {
|
||||
disallowDownMarkers[data.compareMarker] = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function restructRule(ast) {
|
||||
cssTree.walk(ast, {
|
||||
visit: 'Rule',
|
||||
reverse: true,
|
||||
enter: processRule
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = restructRule;
|
39
backend/node_modules/csso/cjs/restructure/index.cjs
generated
vendored
Normal file
39
backend/node_modules/csso/cjs/restructure/index.cjs
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
const index = require('./prepare/index.cjs');
|
||||
const _1MergeAtrule = require('./1-mergeAtrule.cjs');
|
||||
const _2InitialMergeRuleset = require('./2-initialMergeRuleset.cjs');
|
||||
const _3DisjoinRuleset = require('./3-disjoinRuleset.cjs');
|
||||
const _4RestructShorthand = require('./4-restructShorthand.cjs');
|
||||
const _6RestructBlock = require('./6-restructBlock.cjs');
|
||||
const _7MergeRuleset = require('./7-mergeRuleset.cjs');
|
||||
const _8RestructRuleset = require('./8-restructRuleset.cjs');
|
||||
|
||||
function restructure(ast, options) {
|
||||
// prepare ast for restructing
|
||||
const indexer = index(ast, options);
|
||||
options.logger('prepare', ast);
|
||||
|
||||
_1MergeAtrule(ast, options);
|
||||
options.logger('mergeAtrule', ast);
|
||||
|
||||
_2InitialMergeRuleset(ast);
|
||||
options.logger('initialMergeRuleset', ast);
|
||||
|
||||
_3DisjoinRuleset(ast);
|
||||
options.logger('disjoinRuleset', ast);
|
||||
|
||||
_4RestructShorthand(ast, indexer);
|
||||
options.logger('restructShorthand', ast);
|
||||
|
||||
_6RestructBlock(ast);
|
||||
options.logger('restructBlock', ast);
|
||||
|
||||
_7MergeRuleset(ast);
|
||||
options.logger('mergeRuleset', ast);
|
||||
|
||||
_8RestructRuleset(ast);
|
||||
options.logger('restructRuleset', ast);
|
||||
}
|
||||
|
||||
module.exports = restructure;
|
34
backend/node_modules/csso/cjs/restructure/prepare/createDeclarationIndexer.cjs
generated
vendored
Normal file
34
backend/node_modules/csso/cjs/restructure/prepare/createDeclarationIndexer.cjs
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
|
||||
class Index {
|
||||
constructor() {
|
||||
this.map = new Map();
|
||||
}
|
||||
resolve(str) {
|
||||
let index = this.map.get(str);
|
||||
|
||||
if (index === undefined) {
|
||||
index = this.map.size + 1;
|
||||
this.map.set(str, index);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
||||
function createDeclarationIndexer() {
|
||||
const ids = new Index();
|
||||
|
||||
return function markDeclaration(node) {
|
||||
const id = cssTree.generate(node);
|
||||
|
||||
node.id = ids.resolve(id);
|
||||
node.length = id.length;
|
||||
node.fingerprint = null;
|
||||
|
||||
return node;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = createDeclarationIndexer;
|
45
backend/node_modules/csso/cjs/restructure/prepare/index.cjs
generated
vendored
Normal file
45
backend/node_modules/csso/cjs/restructure/prepare/index.cjs
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const createDeclarationIndexer = require('./createDeclarationIndexer.cjs');
|
||||
const processSelector = require('./processSelector.cjs');
|
||||
|
||||
function prepare(ast, options) {
|
||||
const markDeclaration = createDeclarationIndexer();
|
||||
|
||||
cssTree.walk(ast, {
|
||||
visit: 'Rule',
|
||||
enter(node) {
|
||||
node.block.children.forEach(markDeclaration);
|
||||
processSelector(node, options.usage);
|
||||
}
|
||||
});
|
||||
|
||||
cssTree.walk(ast, {
|
||||
visit: 'Atrule',
|
||||
enter(node) {
|
||||
if (node.prelude) {
|
||||
node.prelude.id = null; // pre-init property to avoid multiple hidden class for generate
|
||||
node.prelude.id = cssTree.generate(node.prelude);
|
||||
}
|
||||
|
||||
// compare keyframe selectors by its values
|
||||
// NOTE: still no clarification about problems with keyframes selector grouping (issue #197)
|
||||
if (cssTree.keyword(node.name).basename === 'keyframes') {
|
||||
node.block.avoidRulesMerge = true; /* probably we don't need to prevent those merges for @keyframes
|
||||
TODO: need to be checked */
|
||||
node.block.children.forEach(function(rule) {
|
||||
rule.prelude.children.forEach(function(simpleselector) {
|
||||
simpleselector.compareMarker = simpleselector.id;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
declaration: markDeclaration
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = prepare;
|
101
backend/node_modules/csso/cjs/restructure/prepare/processSelector.cjs
generated
vendored
Normal file
101
backend/node_modules/csso/cjs/restructure/prepare/processSelector.cjs
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const specificity = require('./specificity.cjs');
|
||||
|
||||
const nonFreezePseudoElements = new Set([
|
||||
'first-letter',
|
||||
'first-line',
|
||||
'after',
|
||||
'before'
|
||||
]);
|
||||
const nonFreezePseudoClasses = new Set([
|
||||
'link',
|
||||
'visited',
|
||||
'hover',
|
||||
'active',
|
||||
'first-letter',
|
||||
'first-line',
|
||||
'after',
|
||||
'before'
|
||||
]);
|
||||
|
||||
function processSelector(node, usageData) {
|
||||
const pseudos = new Set();
|
||||
|
||||
node.prelude.children.forEach(function(simpleSelector) {
|
||||
let tagName = '*';
|
||||
let scope = 0;
|
||||
|
||||
simpleSelector.children.forEach(function(node) {
|
||||
switch (node.type) {
|
||||
case 'ClassSelector':
|
||||
if (usageData && usageData.scopes) {
|
||||
const classScope = usageData.scopes[node.name] || 0;
|
||||
|
||||
if (scope !== 0 && classScope !== scope) {
|
||||
throw new Error('Selector can\'t has classes from different scopes: ' + cssTree.generate(simpleSelector));
|
||||
}
|
||||
|
||||
scope = classScope;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'PseudoClassSelector': {
|
||||
const name = node.name.toLowerCase();
|
||||
|
||||
if (!nonFreezePseudoClasses.has(name)) {
|
||||
pseudos.add(`:${name}`);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'PseudoElementSelector': {
|
||||
const name = node.name.toLowerCase();
|
||||
|
||||
if (!nonFreezePseudoElements.has(name)) {
|
||||
pseudos.add(`::${name}`);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'TypeSelector':
|
||||
tagName = node.name.toLowerCase();
|
||||
break;
|
||||
|
||||
case 'AttributeSelector':
|
||||
if (node.flags) {
|
||||
pseudos.add(`[${node.flags.toLowerCase()}]`);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'Combinator':
|
||||
tagName = '*';
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
simpleSelector.compareMarker = specificity(simpleSelector).toString();
|
||||
simpleSelector.id = null; // pre-init property to avoid multiple hidden class
|
||||
simpleSelector.id = cssTree.generate(simpleSelector);
|
||||
|
||||
if (scope) {
|
||||
simpleSelector.compareMarker += ':' + scope;
|
||||
}
|
||||
|
||||
if (tagName !== '*') {
|
||||
simpleSelector.compareMarker += ',' + tagName;
|
||||
}
|
||||
});
|
||||
|
||||
// add property to all rule nodes to avoid multiple hidden class
|
||||
node.pseudoSignature = pseudos.size > 0
|
||||
? [...pseudos].sort().join(',')
|
||||
: false;
|
||||
}
|
||||
|
||||
module.exports = processSelector;
|
133
backend/node_modules/csso/cjs/restructure/prepare/specificity.cjs
generated
vendored
Normal file
133
backend/node_modules/csso/cjs/restructure/prepare/specificity.cjs
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
|
||||
function ensureSelectorList(node) {
|
||||
if (node.type === 'Raw') {
|
||||
return cssTree.parse(node.value, { context: 'selectorList' });
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
function maxSpecificity(a, b) {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (a[i] !== b[i]) {
|
||||
return a[i] > b[i] ? a : b;
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
function maxSelectorListSpecificity(selectorList) {
|
||||
return ensureSelectorList(selectorList).children.reduce(
|
||||
(result, node) => maxSpecificity(specificity(node), result),
|
||||
[0, 0, 0]
|
||||
);
|
||||
}
|
||||
|
||||
// §16. Calculating a selector’s specificity
|
||||
// https://www.w3.org/TR/selectors-4/#specificity-rules
|
||||
function specificity(simpleSelector) {
|
||||
let A = 0;
|
||||
let B = 0;
|
||||
let C = 0;
|
||||
|
||||
// A selector’s specificity is calculated for a given element as follows:
|
||||
simpleSelector.children.forEach((node) => {
|
||||
switch (node.type) {
|
||||
// count the number of ID selectors in the selector (= A)
|
||||
case 'IdSelector':
|
||||
A++;
|
||||
break;
|
||||
|
||||
// count the number of class selectors, attributes selectors, ...
|
||||
case 'ClassSelector':
|
||||
case 'AttributeSelector':
|
||||
B++;
|
||||
break;
|
||||
|
||||
// ... and pseudo-classes in the selector (= B)
|
||||
case 'PseudoClassSelector':
|
||||
switch (node.name.toLowerCase()) {
|
||||
// The specificity of an :is(), :not(), or :has() pseudo-class is replaced
|
||||
// by the specificity of the most specific complex selector in its selector list argument.
|
||||
case 'not':
|
||||
case 'has':
|
||||
case 'is':
|
||||
// :matches() is used before it was renamed to :is()
|
||||
// https://github.com/w3c/csswg-drafts/issues/3258
|
||||
case 'matches':
|
||||
// Older browsers support :is() functionality as prefixed pseudo-class :any()
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/:is
|
||||
case '-webkit-any':
|
||||
case '-moz-any': {
|
||||
const [a, b, c] = maxSelectorListSpecificity(node.children.first);
|
||||
|
||||
A += a;
|
||||
B += b;
|
||||
C += c;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Analogously, the specificity of an :nth-child() or :nth-last-child() selector
|
||||
// is the specificity of the pseudo class itself (counting as one pseudo-class selector)
|
||||
// plus the specificity of the most specific complex selector in its selector list argument (if any).
|
||||
case 'nth-child':
|
||||
case 'nth-last-child': {
|
||||
const arg = node.children.first;
|
||||
|
||||
if (arg.type === 'Nth' && arg.selector) {
|
||||
const [a, b, c] = maxSelectorListSpecificity(arg.selector);
|
||||
|
||||
A += a;
|
||||
B += b + 1;
|
||||
C += c;
|
||||
} else {
|
||||
B++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// The specificity of a :where() pseudo-class is replaced by zero.
|
||||
case 'where':
|
||||
break;
|
||||
|
||||
// The four Level 2 pseudo-elements (::before, ::after, ::first-line, and ::first-letter) may,
|
||||
// for legacy reasons, be represented using the <pseudo-class-selector> grammar,
|
||||
// with only a single ":" character at their start.
|
||||
// https://www.w3.org/TR/selectors-4/#single-colon-pseudos
|
||||
case 'before':
|
||||
case 'after':
|
||||
case 'first-line':
|
||||
case 'first-letter':
|
||||
C++;
|
||||
break;
|
||||
|
||||
default:
|
||||
B++;
|
||||
}
|
||||
break;
|
||||
|
||||
// count the number of type selectors ...
|
||||
case 'TypeSelector':
|
||||
// ignore the universal selector
|
||||
if (!node.name.endsWith('*')) {
|
||||
C++;
|
||||
}
|
||||
break;
|
||||
|
||||
// ... and pseudo-elements in the selector (= C)
|
||||
case 'PseudoElementSelector':
|
||||
C++;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return [A, B, C];
|
||||
}
|
||||
|
||||
module.exports = specificity;
|
151
backend/node_modules/csso/cjs/restructure/utils.cjs
generated
vendored
Normal file
151
backend/node_modules/csso/cjs/restructure/utils.cjs
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
'use strict';
|
||||
|
||||
const { hasOwnProperty } = Object.prototype;
|
||||
|
||||
function isEqualSelectors(a, b) {
|
||||
let cursor1 = a.head;
|
||||
let cursor2 = b.head;
|
||||
|
||||
while (cursor1 !== null && cursor2 !== null && cursor1.data.id === cursor2.data.id) {
|
||||
cursor1 = cursor1.next;
|
||||
cursor2 = cursor2.next;
|
||||
}
|
||||
|
||||
return cursor1 === null && cursor2 === null;
|
||||
}
|
||||
|
||||
function isEqualDeclarations(a, b) {
|
||||
let cursor1 = a.head;
|
||||
let cursor2 = b.head;
|
||||
|
||||
while (cursor1 !== null && cursor2 !== null && cursor1.data.id === cursor2.data.id) {
|
||||
cursor1 = cursor1.next;
|
||||
cursor2 = cursor2.next;
|
||||
}
|
||||
|
||||
return cursor1 === null && cursor2 === null;
|
||||
}
|
||||
|
||||
function compareDeclarations(declarations1, declarations2) {
|
||||
const result = {
|
||||
eq: [],
|
||||
ne1: [],
|
||||
ne2: [],
|
||||
ne2overrided: []
|
||||
};
|
||||
|
||||
const fingerprints = Object.create(null);
|
||||
const declarations2hash = Object.create(null);
|
||||
|
||||
for (let cursor = declarations2.head; cursor; cursor = cursor.next) {
|
||||
declarations2hash[cursor.data.id] = true;
|
||||
}
|
||||
|
||||
for (let cursor = declarations1.head; cursor; cursor = cursor.next) {
|
||||
const data = cursor.data;
|
||||
|
||||
if (data.fingerprint) {
|
||||
fingerprints[data.fingerprint] = data.important;
|
||||
}
|
||||
|
||||
if (declarations2hash[data.id]) {
|
||||
declarations2hash[data.id] = false;
|
||||
result.eq.push(data);
|
||||
} else {
|
||||
result.ne1.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
for (let cursor = declarations2.head; cursor; cursor = cursor.next) {
|
||||
const data = cursor.data;
|
||||
|
||||
if (declarations2hash[data.id]) {
|
||||
// when declarations1 has an overriding declaration, this is not a difference
|
||||
// unless no !important is used on prev and !important is used on the following
|
||||
if (!hasOwnProperty.call(fingerprints, data.fingerprint) ||
|
||||
(!fingerprints[data.fingerprint] && data.important)) {
|
||||
result.ne2.push(data);
|
||||
}
|
||||
|
||||
result.ne2overrided.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function addSelectors(dest, source) {
|
||||
source.forEach((sourceData) => {
|
||||
const newStr = sourceData.id;
|
||||
let cursor = dest.head;
|
||||
|
||||
while (cursor) {
|
||||
const nextStr = cursor.data.id;
|
||||
|
||||
if (nextStr === newStr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextStr > newStr) {
|
||||
break;
|
||||
}
|
||||
|
||||
cursor = cursor.next;
|
||||
}
|
||||
|
||||
dest.insert(dest.createItem(sourceData), cursor);
|
||||
});
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
// check if simpleselectors has no equal specificity and element selector
|
||||
function hasSimilarSelectors(selectors1, selectors2) {
|
||||
let cursor1 = selectors1.head;
|
||||
|
||||
while (cursor1 !== null) {
|
||||
let cursor2 = selectors2.head;
|
||||
|
||||
while (cursor2 !== null) {
|
||||
if (cursor1.data.compareMarker === cursor2.data.compareMarker) {
|
||||
return true;
|
||||
}
|
||||
|
||||
cursor2 = cursor2.next;
|
||||
}
|
||||
|
||||
cursor1 = cursor1.next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// test node can't to be skipped
|
||||
function unsafeToSkipNode(node) {
|
||||
switch (node.type) {
|
||||
case 'Rule':
|
||||
// unsafe skip ruleset with selector similarities
|
||||
return hasSimilarSelectors(node.prelude.children, this);
|
||||
|
||||
case 'Atrule':
|
||||
// can skip at-rules with blocks
|
||||
if (node.block) {
|
||||
// unsafe skip at-rule if block contains something unsafe to skip
|
||||
return node.block.children.some(unsafeToSkipNode, this);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Declaration':
|
||||
return false;
|
||||
}
|
||||
|
||||
// unsafe by default
|
||||
return true;
|
||||
}
|
||||
|
||||
exports.addSelectors = addSelectors;
|
||||
exports.compareDeclarations = compareDeclarations;
|
||||
exports.hasSimilarSelectors = hasSimilarSelectors;
|
||||
exports.isEqualDeclarations = isEqualDeclarations;
|
||||
exports.isEqualSelectors = isEqualSelectors;
|
||||
exports.unsafeToSkipNode = unsafeToSkipNode;
|
60
backend/node_modules/csso/cjs/syntax.cjs
generated
vendored
Normal file
60
backend/node_modules/csso/cjs/syntax.cjs
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
|
||||
const cssTree = require('css-tree');
|
||||
const compress = require('./compress.cjs');
|
||||
const specificity = require('./restructure/prepare/specificity.cjs');
|
||||
|
||||
function encodeString(value) {
|
||||
const stringApostrophe = cssTree.string.encode(value, true);
|
||||
const stringQuote = cssTree.string.encode(value);
|
||||
|
||||
return stringApostrophe.length < stringQuote.length
|
||||
? stringApostrophe
|
||||
: stringQuote;
|
||||
}
|
||||
|
||||
const {
|
||||
lexer,
|
||||
tokenize,
|
||||
parse,
|
||||
generate,
|
||||
walk,
|
||||
find,
|
||||
findLast,
|
||||
findAll,
|
||||
fromPlainObject,
|
||||
toPlainObject
|
||||
} = cssTree.fork({
|
||||
node: {
|
||||
String: {
|
||||
generate(node) {
|
||||
this.token(cssTree.tokenTypes.String, encodeString(node.value));
|
||||
}
|
||||
},
|
||||
Url: {
|
||||
generate(node) {
|
||||
const encodedUrl = cssTree.url.encode(node.value);
|
||||
const string = encodeString(node.value);
|
||||
|
||||
this.token(cssTree.tokenTypes.Url,
|
||||
encodedUrl.length <= string.length + 5 /* "url()".length */
|
||||
? encodedUrl
|
||||
: 'url(' + string + ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
exports.compress = compress;
|
||||
exports.specificity = specificity;
|
||||
exports.find = find;
|
||||
exports.findAll = findAll;
|
||||
exports.findLast = findLast;
|
||||
exports.fromPlainObject = fromPlainObject;
|
||||
exports.generate = generate;
|
||||
exports.lexer = lexer;
|
||||
exports.parse = parse;
|
||||
exports.toPlainObject = toPlainObject;
|
||||
exports.tokenize = tokenize;
|
||||
exports.walk = walk;
|
75
backend/node_modules/csso/cjs/usage.cjs
generated
vendored
Normal file
75
backend/node_modules/csso/cjs/usage.cjs
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
'use strict';
|
||||
|
||||
const { hasOwnProperty } = Object.prototype;
|
||||
|
||||
function buildMap(list, caseInsensitive) {
|
||||
const map = Object.create(null);
|
||||
|
||||
if (!Array.isArray(list)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (let name of list) {
|
||||
if (caseInsensitive) {
|
||||
name = name.toLowerCase();
|
||||
}
|
||||
|
||||
map[name] = true;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
function buildList(data) {
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tags = buildMap(data.tags, true);
|
||||
const ids = buildMap(data.ids);
|
||||
const classes = buildMap(data.classes);
|
||||
|
||||
if (tags === null &&
|
||||
ids === null &&
|
||||
classes === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
tags,
|
||||
ids,
|
||||
classes
|
||||
};
|
||||
}
|
||||
|
||||
function buildIndex(data) {
|
||||
let scopes = false;
|
||||
|
||||
if (data.scopes && Array.isArray(data.scopes)) {
|
||||
scopes = Object.create(null);
|
||||
|
||||
for (let i = 0; i < data.scopes.length; i++) {
|
||||
const list = data.scopes[i];
|
||||
|
||||
if (!list || !Array.isArray(list)) {
|
||||
throw new Error('Wrong usage format');
|
||||
}
|
||||
|
||||
for (const name of list) {
|
||||
if (hasOwnProperty.call(scopes, name)) {
|
||||
throw new Error(`Class can't be used for several scopes: ${name}`);
|
||||
}
|
||||
|
||||
scopes[name] = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
whitelist: buildList(data),
|
||||
blacklist: buildList(data.blacklist),
|
||||
scopes
|
||||
};
|
||||
}
|
||||
|
||||
exports.buildIndex = buildIndex;
|
14
backend/node_modules/csso/cjs/utils.cjs
generated
vendored
Normal file
14
backend/node_modules/csso/cjs/utils.cjs
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const processSelector = require('./restructure/prepare/processSelector.cjs');
|
||||
const utils$1 = require('./restructure/utils.cjs');
|
||||
|
||||
|
||||
|
||||
exports.processSelector = processSelector;
|
||||
exports.addSelectors = utils$1.addSelectors;
|
||||
exports.compareDeclarations = utils$1.compareDeclarations;
|
||||
exports.hasSimilarSelectors = utils$1.hasSimilarSelectors;
|
||||
exports.isEqualDeclarations = utils$1.isEqualDeclarations;
|
||||
exports.isEqualSelectors = utils$1.isEqualSelectors;
|
||||
exports.unsafeToSkipNode = utils$1.unsafeToSkipNode;
|
5
backend/node_modules/csso/cjs/version.cjs
generated
vendored
Normal file
5
backend/node_modules/csso/cjs/version.cjs
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const { version } = require('../package.json');
|
||||
|
||||
exports.version = version;
|
16
backend/node_modules/csso/dist/csso.esm.js
generated
vendored
Normal file
16
backend/node_modules/csso/dist/csso.esm.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
16
backend/node_modules/csso/dist/csso.js
generated
vendored
Normal file
16
backend/node_modules/csso/dist/csso.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
backend/node_modules/csso/dist/version.cjs
generated
vendored
Normal file
1
backend/node_modules/csso/dist/version.cjs
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = "5.0.5";
|
1
backend/node_modules/csso/dist/version.js
generated
vendored
Normal file
1
backend/node_modules/csso/dist/version.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export const version = "5.0.5";
|
69
backend/node_modules/csso/lib/clean/Atrule.js
generated
vendored
Normal file
69
backend/node_modules/csso/lib/clean/Atrule.js
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
import { keyword as resolveKeyword } from 'css-tree';
|
||||
import { hasNoChildren } from './utils.js';
|
||||
|
||||
export default function cleanAtrule(node, item, list) {
|
||||
if (node.block) {
|
||||
// otherwise removed at-rule don't prevent @import for removal
|
||||
if (this.stylesheet !== null) {
|
||||
this.stylesheet.firstAtrulesAllowed = false;
|
||||
}
|
||||
|
||||
if (hasNoChildren(node.block)) {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (node.name) {
|
||||
case 'charset':
|
||||
if (hasNoChildren(node.prelude)) {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
|
||||
// if there is any rule before @charset -> remove it
|
||||
if (item.prev) {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'import':
|
||||
if (this.stylesheet === null || !this.stylesheet.firstAtrulesAllowed) {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
|
||||
// if there are some rules that not an @import or @charset before @import
|
||||
// remove it
|
||||
list.prevUntil(item.prev, function(rule) {
|
||||
if (rule.type === 'Atrule') {
|
||||
if (rule.name === 'import' || rule.name === 'charset') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.root.firstAtrulesAllowed = false;
|
||||
list.remove(item);
|
||||
|
||||
return true;
|
||||
}, this);
|
||||
|
||||
break;
|
||||
|
||||
default: {
|
||||
const name = resolveKeyword(node.name).basename;
|
||||
|
||||
if (name === 'keyframes' ||
|
||||
name === 'media' ||
|
||||
name === 'supports') {
|
||||
|
||||
// drop at-rule with no prelude
|
||||
if (hasNoChildren(node.prelude) || hasNoChildren(node.block)) {
|
||||
list.remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
3
backend/node_modules/csso/lib/clean/Comment.js
generated
vendored
Normal file
3
backend/node_modules/csso/lib/clean/Comment.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function cleanComment(data, item, list) {
|
||||
list.remove(item);
|
||||
};
|
14
backend/node_modules/csso/lib/clean/Declaration.js
generated
vendored
Normal file
14
backend/node_modules/csso/lib/clean/Declaration.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import { property } from 'css-tree';
|
||||
|
||||
export default function cleanDeclartion(node, item, list) {
|
||||
if (node.value.children && node.value.children.isEmpty) {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property(node.property).custom) {
|
||||
if (/\S/.test(node.value.value)) {
|
||||
node.value.value = node.value.value.trim();
|
||||
}
|
||||
}
|
||||
};
|
9
backend/node_modules/csso/lib/clean/Raw.js
generated
vendored
Normal file
9
backend/node_modules/csso/lib/clean/Raw.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { isNodeChildrenList } from './utils.js';
|
||||
|
||||
export default function cleanRaw(node, item, list) {
|
||||
// raw in stylesheet or block children
|
||||
if (isNodeChildrenList(this.stylesheet, list) ||
|
||||
isNodeChildrenList(this.block, list)) {
|
||||
list.remove(item);
|
||||
}
|
||||
};
|
100
backend/node_modules/csso/lib/clean/Rule.js
generated
vendored
Normal file
100
backend/node_modules/csso/lib/clean/Rule.js
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
import { walk, keyword } from 'css-tree';
|
||||
import { hasNoChildren } from './utils.js';
|
||||
|
||||
const { hasOwnProperty } = Object.prototype;
|
||||
const skipUsageFilteringAtrule = new Set(['keyframes']);
|
||||
|
||||
function cleanUnused(selectorList, usageData) {
|
||||
selectorList.children.forEach((selector, item, list) => {
|
||||
let shouldRemove = false;
|
||||
|
||||
walk(selector, function(node) {
|
||||
// ignore nodes in nested selectors
|
||||
if (this.selector === null || this.selector === selectorList) {
|
||||
switch (node.type) {
|
||||
case 'SelectorList':
|
||||
// TODO: remove toLowerCase when pseudo selectors will be normalized
|
||||
// ignore selectors inside :not()
|
||||
if (this.function === null || this.function.name.toLowerCase() !== 'not') {
|
||||
if (cleanUnused(node, usageData)) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ClassSelector':
|
||||
if (usageData.whitelist !== null &&
|
||||
usageData.whitelist.classes !== null &&
|
||||
!hasOwnProperty.call(usageData.whitelist.classes, node.name)) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
if (usageData.blacklist !== null &&
|
||||
usageData.blacklist.classes !== null &&
|
||||
hasOwnProperty.call(usageData.blacklist.classes, node.name)) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'IdSelector':
|
||||
if (usageData.whitelist !== null &&
|
||||
usageData.whitelist.ids !== null &&
|
||||
!hasOwnProperty.call(usageData.whitelist.ids, node.name)) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
if (usageData.blacklist !== null &&
|
||||
usageData.blacklist.ids !== null &&
|
||||
hasOwnProperty.call(usageData.blacklist.ids, node.name)) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'TypeSelector':
|
||||
// TODO: remove toLowerCase when type selectors will be normalized
|
||||
// ignore universal selectors
|
||||
if (node.name.charAt(node.name.length - 1) !== '*') {
|
||||
if (usageData.whitelist !== null &&
|
||||
usageData.whitelist.tags !== null &&
|
||||
!hasOwnProperty.call(usageData.whitelist.tags, node.name.toLowerCase())) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
if (usageData.blacklist !== null &&
|
||||
usageData.blacklist.tags !== null &&
|
||||
hasOwnProperty.call(usageData.blacklist.tags, node.name.toLowerCase())) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (shouldRemove) {
|
||||
list.remove(item);
|
||||
}
|
||||
});
|
||||
|
||||
return selectorList.children.isEmpty;
|
||||
}
|
||||
|
||||
export default function cleanRule(node, item, list, options) {
|
||||
if (hasNoChildren(node.prelude) || hasNoChildren(node.block)) {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
|
||||
// avoid usage filtering for some at-rules
|
||||
if (this.atrule && skipUsageFilteringAtrule.has(keyword(this.atrule.name).basename)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { usage } = options;
|
||||
|
||||
if (usage && (usage.whitelist !== null || usage.blacklist !== null)) {
|
||||
cleanUnused(node.prelude, usage);
|
||||
|
||||
if (hasNoChildren(node.prelude)) {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
19
backend/node_modules/csso/lib/clean/TypeSelector.js
generated
vendored
Normal file
19
backend/node_modules/csso/lib/clean/TypeSelector.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// remove useless universal selector
|
||||
export default function cleanTypeSelector(node, item, list) {
|
||||
const name = item.data.name;
|
||||
|
||||
// check it's a non-namespaced universal selector
|
||||
if (name !== '*') {
|
||||
return;
|
||||
}
|
||||
|
||||
// remove when universal selector before other selectors
|
||||
const nextType = item.next && item.next.data.type;
|
||||
if (nextType === 'IdSelector' ||
|
||||
nextType === 'ClassSelector' ||
|
||||
nextType === 'AttributeSelector' ||
|
||||
nextType === 'PseudoClassSelector' ||
|
||||
nextType === 'PseudoElementSelector') {
|
||||
list.remove(item);
|
||||
}
|
||||
};
|
3
backend/node_modules/csso/lib/clean/WhiteSpace.js
generated
vendored
Normal file
3
backend/node_modules/csso/lib/clean/WhiteSpace.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function cleanWhitespace(node, item, list) {
|
||||
list.remove(item);
|
||||
};
|
28
backend/node_modules/csso/lib/clean/index.js
generated
vendored
Normal file
28
backend/node_modules/csso/lib/clean/index.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import { walk } from 'css-tree';
|
||||
import Atrule from './Atrule.js';
|
||||
import Comment from './Comment.js';
|
||||
import Declaration from './Declaration.js';
|
||||
import Raw from './Raw.js';
|
||||
import Rule from './Rule.js';
|
||||
import TypeSelector from './TypeSelector.js';
|
||||
import WhiteSpace from './WhiteSpace.js';
|
||||
|
||||
const handlers = {
|
||||
Atrule,
|
||||
Comment,
|
||||
Declaration,
|
||||
Raw,
|
||||
Rule,
|
||||
TypeSelector,
|
||||
WhiteSpace
|
||||
};
|
||||
|
||||
export default function(ast, options) {
|
||||
walk(ast, {
|
||||
leave(node, item, list) {
|
||||
if (handlers.hasOwnProperty(node.type)) {
|
||||
handlers[node.type].call(this, node, item, list, options);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
7
backend/node_modules/csso/lib/clean/utils.js
generated
vendored
Normal file
7
backend/node_modules/csso/lib/clean/utils.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export function hasNoChildren(node) {
|
||||
return !node || !node.children || node.children.isEmpty;
|
||||
}
|
||||
|
||||
export function isNodeChildrenList(node, list) {
|
||||
return node !== null && node.children === list;
|
||||
}
|
196
backend/node_modules/csso/lib/compress.js
generated
vendored
Normal file
196
backend/node_modules/csso/lib/compress.js
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
import { List, clone, walk } from 'css-tree';
|
||||
import { buildIndex } from './usage.js';
|
||||
import clean from './clean/index.js';
|
||||
import replace from './replace/index.js';
|
||||
import restructure from './restructure/index.js';
|
||||
|
||||
function readChunk(input, specialComments) {
|
||||
const children = new List();
|
||||
let nonSpaceTokenInBuffer = false;
|
||||
let protectedComment;
|
||||
|
||||
input.nextUntil(input.head, (node, item, list) => {
|
||||
if (node.type === 'Comment') {
|
||||
if (!specialComments || node.value.charAt(0) !== '!') {
|
||||
list.remove(item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nonSpaceTokenInBuffer || protectedComment) {
|
||||
return true;
|
||||
}
|
||||
|
||||
list.remove(item);
|
||||
protectedComment = node;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.type !== 'WhiteSpace') {
|
||||
nonSpaceTokenInBuffer = true;
|
||||
}
|
||||
|
||||
children.insert(list.remove(item));
|
||||
});
|
||||
|
||||
return {
|
||||
comment: protectedComment,
|
||||
stylesheet: {
|
||||
type: 'StyleSheet',
|
||||
loc: null,
|
||||
children
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function compressChunk(ast, firstAtrulesAllowed, num, options) {
|
||||
options.logger(`Compress block #${num}`, null, true);
|
||||
|
||||
let seed = 1;
|
||||
|
||||
if (ast.type === 'StyleSheet') {
|
||||
ast.firstAtrulesAllowed = firstAtrulesAllowed;
|
||||
ast.id = seed++;
|
||||
}
|
||||
|
||||
walk(ast, {
|
||||
visit: 'Atrule',
|
||||
enter(node) {
|
||||
if (node.block !== null) {
|
||||
node.block.id = seed++;
|
||||
}
|
||||
}
|
||||
});
|
||||
options.logger('init', ast);
|
||||
|
||||
// remove redundant
|
||||
clean(ast, options);
|
||||
options.logger('clean', ast);
|
||||
|
||||
// replace nodes for shortened forms
|
||||
replace(ast, options);
|
||||
options.logger('replace', ast);
|
||||
|
||||
// structure optimisations
|
||||
if (options.restructuring) {
|
||||
restructure(ast, options);
|
||||
}
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
function getCommentsOption(options) {
|
||||
let comments = 'comments' in options ? options.comments : 'exclamation';
|
||||
|
||||
if (typeof comments === 'boolean') {
|
||||
comments = comments ? 'exclamation' : false;
|
||||
} else if (comments !== 'exclamation' && comments !== 'first-exclamation') {
|
||||
comments = false;
|
||||
}
|
||||
|
||||
return comments;
|
||||
}
|
||||
|
||||
function getRestructureOption(options) {
|
||||
if ('restructure' in options) {
|
||||
return options.restructure;
|
||||
}
|
||||
|
||||
return 'restructuring' in options ? options.restructuring : true;
|
||||
}
|
||||
|
||||
function wrapBlock(block) {
|
||||
return new List().appendData({
|
||||
type: 'Rule',
|
||||
loc: null,
|
||||
prelude: {
|
||||
type: 'SelectorList',
|
||||
loc: null,
|
||||
children: new List().appendData({
|
||||
type: 'Selector',
|
||||
loc: null,
|
||||
children: new List().appendData({
|
||||
type: 'TypeSelector',
|
||||
loc: null,
|
||||
name: 'x'
|
||||
})
|
||||
})
|
||||
},
|
||||
block
|
||||
});
|
||||
}
|
||||
|
||||
export default function compress(ast, options) {
|
||||
ast = ast || { type: 'StyleSheet', loc: null, children: new List() };
|
||||
options = options || {};
|
||||
|
||||
const compressOptions = {
|
||||
logger: typeof options.logger === 'function' ? options.logger : function() {},
|
||||
restructuring: getRestructureOption(options),
|
||||
forceMediaMerge: Boolean(options.forceMediaMerge),
|
||||
usage: options.usage ? buildIndex(options.usage) : false
|
||||
};
|
||||
const output = new List();
|
||||
let specialComments = getCommentsOption(options);
|
||||
let firstAtrulesAllowed = true;
|
||||
let input;
|
||||
let chunk;
|
||||
let chunkNum = 1;
|
||||
let chunkChildren;
|
||||
|
||||
if (options.clone) {
|
||||
ast = clone(ast);
|
||||
}
|
||||
|
||||
if (ast.type === 'StyleSheet') {
|
||||
input = ast.children;
|
||||
ast.children = output;
|
||||
} else {
|
||||
input = wrapBlock(ast);
|
||||
}
|
||||
|
||||
do {
|
||||
chunk = readChunk(input, Boolean(specialComments));
|
||||
compressChunk(chunk.stylesheet, firstAtrulesAllowed, chunkNum++, compressOptions);
|
||||
chunkChildren = chunk.stylesheet.children;
|
||||
|
||||
if (chunk.comment) {
|
||||
// add \n before comment if there is another content in output
|
||||
if (!output.isEmpty) {
|
||||
output.insert(List.createItem({
|
||||
type: 'Raw',
|
||||
value: '\n'
|
||||
}));
|
||||
}
|
||||
|
||||
output.insert(List.createItem(chunk.comment));
|
||||
|
||||
// add \n after comment if chunk is not empty
|
||||
if (!chunkChildren.isEmpty) {
|
||||
output.insert(List.createItem({
|
||||
type: 'Raw',
|
||||
value: '\n'
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if (firstAtrulesAllowed && !chunkChildren.isEmpty) {
|
||||
const lastRule = chunkChildren.last;
|
||||
|
||||
if (lastRule.type !== 'Atrule' ||
|
||||
(lastRule.name !== 'import' && lastRule.name !== 'charset')) {
|
||||
firstAtrulesAllowed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (specialComments !== 'exclamation') {
|
||||
specialComments = false;
|
||||
}
|
||||
|
||||
output.appendList(chunkChildren);
|
||||
} while (!input.isEmpty);
|
||||
|
||||
return {
|
||||
ast
|
||||
};
|
||||
};
|
131
backend/node_modules/csso/lib/index.js
generated
vendored
Normal file
131
backend/node_modules/csso/lib/index.js
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
import { version } from './version.js';
|
||||
import * as syntax from './syntax.js';
|
||||
import * as utils from './utils.js';
|
||||
|
||||
const { parse, generate, compress } = syntax;
|
||||
|
||||
function debugOutput(name, options, startTime, data) {
|
||||
if (options.debug) {
|
||||
console.error(`## ${name} done in %d ms\n`, Date.now() - startTime);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function createDefaultLogger(level) {
|
||||
let lastDebug;
|
||||
|
||||
return function logger(title, ast) {
|
||||
let line = title;
|
||||
|
||||
if (ast) {
|
||||
line = `[${((Date.now() - lastDebug) / 1000).toFixed(3)}s] ${line}`;
|
||||
}
|
||||
|
||||
if (level > 1 && ast) {
|
||||
let css = generate(ast);
|
||||
|
||||
// when level 2, limit css to 256 symbols
|
||||
if (level === 2 && css.length > 256) {
|
||||
css = css.substr(0, 256) + '...';
|
||||
}
|
||||
|
||||
line += `\n ${css}\n`;
|
||||
}
|
||||
|
||||
console.error(line);
|
||||
lastDebug = Date.now();
|
||||
};
|
||||
}
|
||||
|
||||
function buildCompressOptions(options) {
|
||||
options = { ...options };
|
||||
|
||||
if (typeof options.logger !== 'function' && options.debug) {
|
||||
options.logger = createDefaultLogger(options.debug);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function runHandler(ast, options, handlers) {
|
||||
if (!Array.isArray(handlers)) {
|
||||
handlers = [handlers];
|
||||
}
|
||||
|
||||
handlers.forEach(fn => fn(ast, options));
|
||||
}
|
||||
|
||||
function minify(context, source, options) {
|
||||
options = options || {};
|
||||
|
||||
const filename = options.filename || '<unknown>';
|
||||
let result;
|
||||
|
||||
// parse
|
||||
const ast = debugOutput('parsing', options, Date.now(),
|
||||
parse(source, {
|
||||
context,
|
||||
filename,
|
||||
positions: Boolean(options.sourceMap)
|
||||
})
|
||||
);
|
||||
|
||||
// before compress handlers
|
||||
if (options.beforeCompress) {
|
||||
debugOutput('beforeCompress', options, Date.now(),
|
||||
runHandler(ast, options, options.beforeCompress)
|
||||
);
|
||||
}
|
||||
|
||||
// compress
|
||||
const compressResult = debugOutput('compress', options, Date.now(),
|
||||
compress(ast, buildCompressOptions(options))
|
||||
);
|
||||
|
||||
// after compress handlers
|
||||
if (options.afterCompress) {
|
||||
debugOutput('afterCompress', options, Date.now(),
|
||||
runHandler(compressResult, options, options.afterCompress)
|
||||
);
|
||||
}
|
||||
|
||||
// generate
|
||||
if (options.sourceMap) {
|
||||
result = debugOutput('generate(sourceMap: true)', options, Date.now(), (() => {
|
||||
const tmp = generate(compressResult.ast, { sourceMap: true });
|
||||
|
||||
tmp.map._file = filename; // since other tools can relay on file in source map transform chain
|
||||
tmp.map.setSourceContent(filename, source);
|
||||
|
||||
return tmp;
|
||||
})());
|
||||
} else {
|
||||
result = debugOutput('generate', options, Date.now(), {
|
||||
css: generate(compressResult.ast),
|
||||
map: null
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function minifyStylesheet(source, options) {
|
||||
return minify('stylesheet', source, options);
|
||||
}
|
||||
|
||||
function minifyBlock(source, options) {
|
||||
return minify('declarationList', source, options);
|
||||
}
|
||||
|
||||
export {
|
||||
version,
|
||||
utils,
|
||||
|
||||
// main methods
|
||||
minifyStylesheet as minify,
|
||||
minifyBlock,
|
||||
|
||||
// css syntax parser/walkers/generator/etc
|
||||
syntax
|
||||
};
|
9
backend/node_modules/csso/lib/replace/Atrule.js
generated
vendored
Normal file
9
backend/node_modules/csso/lib/replace/Atrule.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { keyword as resolveKeyword } from 'css-tree';
|
||||
import compressKeyframes from './atrule/keyframes.js';
|
||||
|
||||
export default function(node) {
|
||||
// compress @keyframe selectors
|
||||
if (resolveKeyword(node.name).basename === 'keyframes') {
|
||||
compressKeyframes(node);
|
||||
}
|
||||
};
|
28
backend/node_modules/csso/lib/replace/AttributeSelector.js
generated
vendored
Normal file
28
backend/node_modules/csso/lib/replace/AttributeSelector.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// Can unquote attribute detection
|
||||
// Adopted implementation of Mathias Bynens
|
||||
// https://github.com/mathiasbynens/mothereff.in/blob/master/unquoted-attributes/eff.js
|
||||
const blockUnquoteRx = /^(-?\d|--)|[\u0000-\u002c\u002e\u002f\u003A-\u0040\u005B-\u005E\u0060\u007B-\u009f]/;
|
||||
|
||||
function canUnquote(value) {
|
||||
if (value === '' || value === '-') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !blockUnquoteRx.test(value);
|
||||
}
|
||||
|
||||
export default function(node) {
|
||||
const attrValue = node.value;
|
||||
|
||||
if (!attrValue || attrValue.type !== 'String') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (canUnquote(attrValue.value)) {
|
||||
node.value = {
|
||||
type: 'Identifier',
|
||||
loc: attrValue.loc,
|
||||
name: attrValue.value
|
||||
};
|
||||
}
|
||||
};
|
63
backend/node_modules/csso/lib/replace/Dimension.js
generated
vendored
Normal file
63
backend/node_modules/csso/lib/replace/Dimension.js
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
import { packNumber } from './Number.js';
|
||||
|
||||
const MATH_FUNCTIONS = new Set([
|
||||
'calc',
|
||||
'min',
|
||||
'max',
|
||||
'clamp'
|
||||
]);
|
||||
const LENGTH_UNIT = new Set([
|
||||
// absolute length units
|
||||
'px',
|
||||
'mm',
|
||||
'cm',
|
||||
'in',
|
||||
'pt',
|
||||
'pc',
|
||||
|
||||
// relative length units
|
||||
'em',
|
||||
'ex',
|
||||
'ch',
|
||||
'rem',
|
||||
|
||||
// viewport-percentage lengths
|
||||
'vh',
|
||||
'vw',
|
||||
'vmin',
|
||||
'vmax',
|
||||
'vm'
|
||||
]);
|
||||
|
||||
export default function compressDimension(node, item) {
|
||||
const value = packNumber(node.value);
|
||||
|
||||
node.value = value;
|
||||
|
||||
if (value === '0' && this.declaration !== null && this.atrulePrelude === null) {
|
||||
const unit = node.unit.toLowerCase();
|
||||
|
||||
// only length values can be compressed
|
||||
if (!LENGTH_UNIT.has(unit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// issue #362: shouldn't remove unit in -ms-flex since it breaks flex in IE10/11
|
||||
// issue #200: shouldn't remove unit in flex since it breaks flex in IE10/11
|
||||
if (this.declaration.property === '-ms-flex' ||
|
||||
this.declaration.property === 'flex') {
|
||||
return;
|
||||
}
|
||||
|
||||
// issue #222: don't remove units inside calc
|
||||
if (this.function && MATH_FUNCTIONS.has(this.function.name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
item.data = {
|
||||
type: 'Number',
|
||||
loc: node.loc,
|
||||
value
|
||||
};
|
||||
}
|
||||
};
|
40
backend/node_modules/csso/lib/replace/Number.js
generated
vendored
Normal file
40
backend/node_modules/csso/lib/replace/Number.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
const OMIT_PLUSSIGN = /^(?:\+|(-))?0*(\d*)(?:\.0*|(\.\d*?)0*)?$/;
|
||||
const KEEP_PLUSSIGN = /^([\+\-])?0*(\d*)(?:\.0*|(\.\d*?)0*)?$/;
|
||||
const unsafeToRemovePlusSignAfter = new Set([
|
||||
'Dimension',
|
||||
'Hash',
|
||||
'Identifier',
|
||||
'Number',
|
||||
'Raw',
|
||||
'UnicodeRange'
|
||||
]);
|
||||
|
||||
export function packNumber(value, item) {
|
||||
// omit plus sign only if no prev or prev is safe type
|
||||
const regexp = item && item.prev !== null && unsafeToRemovePlusSignAfter.has(item.prev.data.type)
|
||||
? KEEP_PLUSSIGN
|
||||
: OMIT_PLUSSIGN;
|
||||
|
||||
// 100 -> '100'
|
||||
// 00100 -> '100'
|
||||
// +100 -> '100'
|
||||
// -100 -> '-100'
|
||||
// 0.123 -> '.123'
|
||||
// 0.12300 -> '.123'
|
||||
// 0.0 -> ''
|
||||
// 0 -> ''
|
||||
// -0 -> '-'
|
||||
value = String(value).replace(regexp, '$1$2$3');
|
||||
|
||||
if (value === '' || value === '-') {
|
||||
value = '0';
|
||||
}
|
||||
// FIXME: is it solution simplier?
|
||||
// value = String(Number(value)).replace(/^(-?)0+\./, '$1.');
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export function Number(node) {
|
||||
node.value = packNumber(node.value);
|
||||
};
|
37
backend/node_modules/csso/lib/replace/Percentage.js
generated
vendored
Normal file
37
backend/node_modules/csso/lib/replace/Percentage.js
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
import { lexer } from 'css-tree';
|
||||
import { packNumber } from './Number.js';
|
||||
|
||||
const blacklist = new Set([
|
||||
// see https://github.com/jakubpawlowicz/clean-css/issues/957
|
||||
'width',
|
||||
'min-width',
|
||||
'max-width',
|
||||
'height',
|
||||
'min-height',
|
||||
'max-height',
|
||||
|
||||
// issue #410: Don’t remove units in flex-basis value for (-ms-)flex shorthand
|
||||
// issue #362: shouldn't remove unit in -ms-flex since it breaks flex in IE10/11
|
||||
// issue #200: shouldn't remove unit in flex since it breaks flex in IE10/11
|
||||
'flex',
|
||||
'-ms-flex'
|
||||
]);
|
||||
|
||||
export default function compressPercentage(node, item) {
|
||||
node.value = packNumber(node.value);
|
||||
|
||||
if (node.value === '0' && this.declaration && !blacklist.has(this.declaration.property)) {
|
||||
// try to convert a number
|
||||
item.data = {
|
||||
type: 'Number',
|
||||
loc: node.loc,
|
||||
value: node.value
|
||||
};
|
||||
|
||||
// that's ok only when new value matches on length
|
||||
if (!lexer.matchDeclaration(this.declaration).isType(item.data, 'length')) {
|
||||
// otherwise rollback changes
|
||||
item.data = node;
|
||||
}
|
||||
}
|
||||
};
|
4
backend/node_modules/csso/lib/replace/Url.js
generated
vendored
Normal file
4
backend/node_modules/csso/lib/replace/Url.js
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export default function(node) {
|
||||
// convert `\\` to `/`
|
||||
node.value = node.value.replace(/\\/g, '/');
|
||||
};
|
26
backend/node_modules/csso/lib/replace/Value.js
generated
vendored
Normal file
26
backend/node_modules/csso/lib/replace/Value.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import { property as resolveName } from 'css-tree';
|
||||
import font from './property/font.js';
|
||||
import fontWeight from './property/font-weight.js';
|
||||
import background from './property/background.js';
|
||||
import border from './property/border.js';
|
||||
import outline from './property/border.js';
|
||||
|
||||
const handlers = {
|
||||
'font': font,
|
||||
'font-weight': fontWeight,
|
||||
'background': background,
|
||||
'border': border,
|
||||
'outline': outline
|
||||
};
|
||||
|
||||
export default function compressValue(node) {
|
||||
if (!this.declaration) {
|
||||
return;
|
||||
}
|
||||
|
||||
const property = resolveName(this.declaration.property);
|
||||
|
||||
if (handlers.hasOwnProperty(property.basename)) {
|
||||
handlers[property.basename](node);
|
||||
}
|
||||
};
|
21
backend/node_modules/csso/lib/replace/atrule/keyframes.js
generated
vendored
Normal file
21
backend/node_modules/csso/lib/replace/atrule/keyframes.js
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
export default function(node) {
|
||||
node.block.children.forEach((rule) => {
|
||||
rule.prelude.children.forEach((simpleselector) => {
|
||||
simpleselector.children.forEach((data, item) => {
|
||||
if (data.type === 'Percentage' && data.value === '100') {
|
||||
item.data = {
|
||||
type: 'TypeSelector',
|
||||
loc: data.loc,
|
||||
name: 'to'
|
||||
};
|
||||
} else if (data.type === 'TypeSelector' && data.name === 'from') {
|
||||
item.data = {
|
||||
type: 'Percentage',
|
||||
loc: data.loc,
|
||||
value: '0'
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
498
backend/node_modules/csso/lib/replace/color.js
generated
vendored
Normal file
498
backend/node_modules/csso/lib/replace/color.js
generated
vendored
Normal file
@@ -0,0 +1,498 @@
|
||||
import { lexer } from 'css-tree';
|
||||
import { packNumber } from './Number.js';
|
||||
|
||||
// http://www.w3.org/TR/css3-color/#svg-color
|
||||
const NAME_TO_HEX = {
|
||||
'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',
|
||||
'darkgrey': 'a9a9a9',
|
||||
'darkgreen': '006400',
|
||||
'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',
|
||||
'grey': '808080',
|
||||
'green': '008000',
|
||||
'greenyellow': 'adff2f',
|
||||
'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',
|
||||
'lightgrey': 'd3d3d3',
|
||||
'lightgreen': '90ee90',
|
||||
'lightpink': 'ffb6c1',
|
||||
'lightsalmon': 'ffa07a',
|
||||
'lightseagreen': '20b2aa',
|
||||
'lightskyblue': '87cefa',
|
||||
'lightslategray': '789',
|
||||
'lightslategrey': '789',
|
||||
'lightsteelblue': 'b0c4de',
|
||||
'lightyellow': 'ffffe0',
|
||||
'lime': '0f0',
|
||||
'limegreen': '32cd32',
|
||||
'linen': 'faf0e6',
|
||||
'magenta': 'f0f',
|
||||
'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': '639',
|
||||
'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'
|
||||
};
|
||||
|
||||
const HEX_TO_NAME = {
|
||||
'800000': 'maroon',
|
||||
'800080': 'purple',
|
||||
'808000': 'olive',
|
||||
'808080': 'gray',
|
||||
'00ffff': 'cyan',
|
||||
'f0ffff': 'azure',
|
||||
'f5f5dc': 'beige',
|
||||
'ffe4c4': 'bisque',
|
||||
'000000': 'black',
|
||||
'0000ff': 'blue',
|
||||
'a52a2a': 'brown',
|
||||
'ff7f50': 'coral',
|
||||
'ffd700': 'gold',
|
||||
'008000': 'green',
|
||||
'4b0082': 'indigo',
|
||||
'fffff0': 'ivory',
|
||||
'f0e68c': 'khaki',
|
||||
'00ff00': 'lime',
|
||||
'faf0e6': 'linen',
|
||||
'000080': 'navy',
|
||||
'ffa500': 'orange',
|
||||
'da70d6': 'orchid',
|
||||
'cd853f': 'peru',
|
||||
'ffc0cb': 'pink',
|
||||
'dda0dd': 'plum',
|
||||
'f00': 'red',
|
||||
'ff0000': 'red',
|
||||
'fa8072': 'salmon',
|
||||
'a0522d': 'sienna',
|
||||
'c0c0c0': 'silver',
|
||||
'fffafa': 'snow',
|
||||
'd2b48c': 'tan',
|
||||
'008080': 'teal',
|
||||
'ff6347': 'tomato',
|
||||
'ee82ee': 'violet',
|
||||
'f5deb3': 'wheat',
|
||||
'ffffff': 'white',
|
||||
'ffff00': 'yellow'
|
||||
};
|
||||
|
||||
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 hslToRgb(h, s, l, a) {
|
||||
let r;
|
||||
let g;
|
||||
let b;
|
||||
|
||||
if (s === 0) {
|
||||
r = g = b = l; // achromatic
|
||||
} else {
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
const p = 2 * l - q;
|
||||
|
||||
r = hueToRgb(p, q, h + 1 / 3);
|
||||
g = hueToRgb(p, q, h);
|
||||
b = hueToRgb(p, q, h - 1 / 3);
|
||||
}
|
||||
|
||||
return [
|
||||
Math.round(r * 255),
|
||||
Math.round(g * 255),
|
||||
Math.round(b * 255),
|
||||
a
|
||||
];
|
||||
}
|
||||
|
||||
function toHex(value) {
|
||||
value = value.toString(16);
|
||||
|
||||
return value.length === 1 ? '0' + value : value;
|
||||
}
|
||||
|
||||
function parseFunctionArgs(functionArgs, count, rgb) {
|
||||
let cursor = functionArgs.head;
|
||||
let args = [];
|
||||
let wasValue = false;
|
||||
|
||||
while (cursor !== null) {
|
||||
const { type, value } = cursor.data;
|
||||
|
||||
switch (type) {
|
||||
case 'Number':
|
||||
case 'Percentage':
|
||||
if (wasValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
wasValue = true;
|
||||
args.push({
|
||||
type,
|
||||
value: Number(value)
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case 'Operator':
|
||||
if (value === ',') {
|
||||
if (!wasValue) {
|
||||
return;
|
||||
}
|
||||
wasValue = false;
|
||||
} else if (wasValue || value !== '+') {
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// something we couldn't understand
|
||||
return;
|
||||
}
|
||||
|
||||
cursor = cursor.next;
|
||||
}
|
||||
|
||||
if (args.length !== count) {
|
||||
// invalid arguments count
|
||||
// TODO: remove those tokens
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.length === 4) {
|
||||
if (args[3].type !== 'Number') {
|
||||
// 4th argument should be a number
|
||||
// TODO: remove those tokens
|
||||
return;
|
||||
}
|
||||
|
||||
args[3].type = 'Alpha';
|
||||
}
|
||||
|
||||
if (rgb) {
|
||||
if (args[0].type !== args[1].type || args[0].type !== args[2].type) {
|
||||
// invalid color, numbers and percentage shouldn't be mixed
|
||||
// TODO: remove those tokens
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (args[0].type !== 'Number' ||
|
||||
args[1].type !== 'Percentage' ||
|
||||
args[2].type !== 'Percentage') {
|
||||
// invalid color, for hsl values should be: number, percentage, percentage
|
||||
// TODO: remove those tokens
|
||||
return;
|
||||
}
|
||||
|
||||
args[0].type = 'Angle';
|
||||
}
|
||||
|
||||
return args.map(function(arg) {
|
||||
let value = Math.max(0, arg.value);
|
||||
|
||||
switch (arg.type) {
|
||||
case 'Number':
|
||||
// fit value to [0..255] range
|
||||
value = Math.min(value, 255);
|
||||
break;
|
||||
|
||||
case 'Percentage':
|
||||
// convert 0..100% to value in [0..255] range
|
||||
value = Math.min(value, 100) / 100;
|
||||
|
||||
if (!rgb) {
|
||||
return value;
|
||||
}
|
||||
|
||||
value = 255 * value;
|
||||
break;
|
||||
|
||||
case 'Angle':
|
||||
// fit value to (-360..360) range
|
||||
return (((value % 360) + 360) % 360) / 360;
|
||||
|
||||
case 'Alpha':
|
||||
// fit value to [0..1] range
|
||||
return Math.min(value, 1);
|
||||
}
|
||||
|
||||
return Math.round(value);
|
||||
});
|
||||
}
|
||||
|
||||
export function compressFunction(node, item) {
|
||||
let functionName = node.name;
|
||||
let args;
|
||||
|
||||
if (functionName === 'rgba' || functionName === 'hsla') {
|
||||
args = parseFunctionArgs(node.children, 4, functionName === 'rgba');
|
||||
|
||||
if (!args) {
|
||||
// something went wrong
|
||||
return;
|
||||
}
|
||||
|
||||
if (functionName === 'hsla') {
|
||||
args = hslToRgb(...args);
|
||||
node.name = 'rgba';
|
||||
}
|
||||
|
||||
if (args[3] === 0) {
|
||||
// try to replace `rgba(x, x, x, 0)` to `transparent`
|
||||
// always replace `rgba(0, 0, 0, 0)` to `transparent`
|
||||
// otherwise avoid replacement in gradients since it may break color transition
|
||||
// http://stackoverflow.com/questions/11829410/css3-gradient-rendering-issues-from-transparent-to-white
|
||||
const scopeFunctionName = this.function && this.function.name;
|
||||
|
||||
if ((args[0] === 0 && args[1] === 0 && args[2] === 0) ||
|
||||
!/^(?:to|from|color-stop)$|gradient$/i.test(scopeFunctionName)) {
|
||||
|
||||
item.data = {
|
||||
type: 'Identifier',
|
||||
loc: node.loc,
|
||||
name: 'transparent'
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (args[3] !== 1) {
|
||||
// replace argument values for normalized/interpolated
|
||||
node.children.forEach((node, item, list) => {
|
||||
if (node.type === 'Operator') {
|
||||
if (node.value !== ',') {
|
||||
list.remove(item);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
item.data = {
|
||||
type: 'Number',
|
||||
loc: node.loc,
|
||||
value: packNumber(args.shift())
|
||||
};
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise convert to rgb, i.e. rgba(255, 0, 0, 1) -> rgb(255, 0, 0)
|
||||
functionName = 'rgb';
|
||||
}
|
||||
|
||||
if (functionName === 'hsl') {
|
||||
args = args || parseFunctionArgs(node.children, 3, false);
|
||||
|
||||
if (!args) {
|
||||
// something went wrong
|
||||
return;
|
||||
}
|
||||
|
||||
// convert to rgb
|
||||
args = hslToRgb(...args);
|
||||
functionName = 'rgb';
|
||||
}
|
||||
|
||||
if (functionName === 'rgb') {
|
||||
args = args || parseFunctionArgs(node.children, 3, true);
|
||||
|
||||
if (!args) {
|
||||
// something went wrong
|
||||
return;
|
||||
}
|
||||
|
||||
item.data = {
|
||||
type: 'Hash',
|
||||
loc: node.loc,
|
||||
value: toHex(args[0]) + toHex(args[1]) + toHex(args[2])
|
||||
};
|
||||
|
||||
compressHex(item.data, item);
|
||||
}
|
||||
}
|
||||
|
||||
export function compressIdent(node, item) {
|
||||
if (this.declaration === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let color = node.name.toLowerCase();
|
||||
|
||||
if (NAME_TO_HEX.hasOwnProperty(color) &&
|
||||
lexer.matchDeclaration(this.declaration).isType(node, 'color')) {
|
||||
const hex = NAME_TO_HEX[color];
|
||||
|
||||
if (hex.length + 1 <= color.length) {
|
||||
// replace for shorter hex value
|
||||
item.data = {
|
||||
type: 'Hash',
|
||||
loc: node.loc,
|
||||
value: hex
|
||||
};
|
||||
} else {
|
||||
// special case for consistent colors
|
||||
if (color === 'grey') {
|
||||
color = 'gray';
|
||||
}
|
||||
|
||||
// just replace value for lower cased name
|
||||
node.name = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function compressHex(node, item) {
|
||||
let color = node.value.toLowerCase();
|
||||
|
||||
// #112233 -> #123
|
||||
if (color.length === 6 &&
|
||||
color[0] === color[1] &&
|
||||
color[2] === color[3] &&
|
||||
color[4] === color[5]) {
|
||||
color = color[0] + color[2] + color[4];
|
||||
}
|
||||
|
||||
if (HEX_TO_NAME[color]) {
|
||||
item.data = {
|
||||
type: 'Identifier',
|
||||
loc: node.loc,
|
||||
name: HEX_TO_NAME[color]
|
||||
};
|
||||
} else {
|
||||
node.value = color;
|
||||
}
|
||||
}
|
32
backend/node_modules/csso/lib/replace/index.js
generated
vendored
Normal file
32
backend/node_modules/csso/lib/replace/index.js
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
import { walk } from 'css-tree';
|
||||
import Atrule from './Atrule.js';
|
||||
import AttributeSelector from './AttributeSelector.js';
|
||||
import Value from './Value.js';
|
||||
import Dimension from './Dimension.js';
|
||||
import Percentage from './Percentage.js';
|
||||
import { Number } from './Number.js';
|
||||
import Url from './Url.js';
|
||||
import { compressHex, compressIdent, compressFunction } from './color.js';
|
||||
|
||||
const handlers = {
|
||||
Atrule,
|
||||
AttributeSelector,
|
||||
Value,
|
||||
Dimension,
|
||||
Percentage,
|
||||
Number,
|
||||
Url,
|
||||
Hash: compressHex,
|
||||
Identifier: compressIdent,
|
||||
Function: compressFunction
|
||||
};
|
||||
|
||||
export default function(ast) {
|
||||
walk(ast, {
|
||||
leave(node, item, list) {
|
||||
if (handlers.hasOwnProperty(node.type)) {
|
||||
handlers[node.type].call(this, node, item, list);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
50
backend/node_modules/csso/lib/replace/property/background.js
generated
vendored
Normal file
50
backend/node_modules/csso/lib/replace/property/background.js
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
import { List } from 'css-tree';
|
||||
|
||||
export default function compressBackground(node) {
|
||||
function flush() {
|
||||
if (!buffer.length) {
|
||||
buffer.unshift(
|
||||
{
|
||||
type: 'Number',
|
||||
loc: null,
|
||||
value: '0'
|
||||
},
|
||||
{
|
||||
type: 'Number',
|
||||
loc: null,
|
||||
value: '0'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
newValue.push.apply(newValue, buffer);
|
||||
|
||||
buffer = [];
|
||||
}
|
||||
|
||||
let newValue = [];
|
||||
let buffer = [];
|
||||
|
||||
node.children.forEach((node) => {
|
||||
if (node.type === 'Operator' && node.value === ',') {
|
||||
flush();
|
||||
newValue.push(node);
|
||||
return;
|
||||
}
|
||||
|
||||
// remove defaults
|
||||
if (node.type === 'Identifier') {
|
||||
if (node.name === 'transparent' ||
|
||||
node.name === 'none' ||
|
||||
node.name === 'repeat' ||
|
||||
node.name === 'scroll') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
buffer.push(node);
|
||||
});
|
||||
|
||||
flush();
|
||||
node.children = new List().fromArray(newValue);
|
||||
};
|
16
backend/node_modules/csso/lib/replace/property/border.js
generated
vendored
Normal file
16
backend/node_modules/csso/lib/replace/property/border.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
export default function compressBorder(node) {
|
||||
node.children.forEach((node, item, list) => {
|
||||
if (node.type === 'Identifier' && node.name.toLowerCase() === 'none') {
|
||||
if (list.head === list.tail) {
|
||||
// replace `none` for zero when `none` is a single term
|
||||
item.data = {
|
||||
type: 'Number',
|
||||
loc: node.loc,
|
||||
value: '0'
|
||||
};
|
||||
} else {
|
||||
list.remove(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
22
backend/node_modules/csso/lib/replace/property/font-weight.js
generated
vendored
Normal file
22
backend/node_modules/csso/lib/replace/property/font-weight.js
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
export default function compressFontWeight(node) {
|
||||
const value = node.children.head.data;
|
||||
|
||||
if (value.type === 'Identifier') {
|
||||
switch (value.name) {
|
||||
case 'normal':
|
||||
node.children.head.data = {
|
||||
type: 'Number',
|
||||
loc: value.loc,
|
||||
value: '400'
|
||||
};
|
||||
break;
|
||||
case 'bold':
|
||||
node.children.head.data = {
|
||||
type: 'Number',
|
||||
loc: value.loc,
|
||||
value: '700'
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
30
backend/node_modules/csso/lib/replace/property/font.js
generated
vendored
Normal file
30
backend/node_modules/csso/lib/replace/property/font.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
export default function compressFont(node) {
|
||||
const list = node.children;
|
||||
|
||||
list.forEachRight(function(node, item) {
|
||||
if (node.type === 'Identifier') {
|
||||
if (node.name === 'bold') {
|
||||
item.data = {
|
||||
type: 'Number',
|
||||
loc: node.loc,
|
||||
value: '700'
|
||||
};
|
||||
} else if (node.name === 'normal') {
|
||||
const prev = item.prev;
|
||||
|
||||
if (prev && prev.data.type === 'Operator' && prev.data.value === '/') {
|
||||
this.remove(prev);
|
||||
}
|
||||
|
||||
this.remove(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (list.isEmpty) {
|
||||
list.insert(list.createItem({
|
||||
type: 'Identifier',
|
||||
name: 'normal'
|
||||
}));
|
||||
}
|
||||
};
|
106
backend/node_modules/csso/lib/restructure/1-mergeAtrule.js
generated
vendored
Normal file
106
backend/node_modules/csso/lib/restructure/1-mergeAtrule.js
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
import { List, walk, keyword as resolveKeyword } from 'css-tree';
|
||||
|
||||
const { hasOwnProperty } = Object.prototype;
|
||||
|
||||
function addRuleToMap(map, item, list, single) {
|
||||
const node = item.data;
|
||||
const name = resolveKeyword(node.name).basename;
|
||||
const id = node.name.toLowerCase() + '/' + (node.prelude ? node.prelude.id : null);
|
||||
|
||||
if (!hasOwnProperty.call(map, name)) {
|
||||
map[name] = Object.create(null);
|
||||
}
|
||||
|
||||
if (single) {
|
||||
delete map[name][id];
|
||||
}
|
||||
|
||||
if (!hasOwnProperty.call(map[name], id)) {
|
||||
map[name][id] = new List();
|
||||
}
|
||||
|
||||
map[name][id].append(list.remove(item));
|
||||
}
|
||||
|
||||
function relocateAtrules(ast, options) {
|
||||
const collected = Object.create(null);
|
||||
let topInjectPoint = null;
|
||||
|
||||
ast.children.forEach(function(node, item, list) {
|
||||
if (node.type === 'Atrule') {
|
||||
const name = resolveKeyword(node.name).basename;
|
||||
|
||||
switch (name) {
|
||||
case 'keyframes':
|
||||
addRuleToMap(collected, item, list, true);
|
||||
return;
|
||||
|
||||
case 'media':
|
||||
if (options.forceMediaMerge) {
|
||||
addRuleToMap(collected, item, list, false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (topInjectPoint === null &&
|
||||
name !== 'charset' &&
|
||||
name !== 'import') {
|
||||
topInjectPoint = item;
|
||||
}
|
||||
} else {
|
||||
if (topInjectPoint === null) {
|
||||
topInjectPoint = item;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (const atrule in collected) {
|
||||
for (const id in collected[atrule]) {
|
||||
ast.children.insertList(
|
||||
collected[atrule][id],
|
||||
atrule === 'media' ? null : topInjectPoint
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function isMediaRule(node) {
|
||||
return node.type === 'Atrule' && node.name === 'media';
|
||||
}
|
||||
|
||||
function processAtrule(node, item, list) {
|
||||
if (!isMediaRule(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const prev = item.prev && item.prev.data;
|
||||
|
||||
if (!prev || !isMediaRule(prev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// merge @media with same query
|
||||
if (node.prelude &&
|
||||
prev.prelude &&
|
||||
node.prelude.id === prev.prelude.id) {
|
||||
prev.block.children.appendList(node.block.children);
|
||||
list.remove(item);
|
||||
|
||||
// TODO: use it when we can refer to several points in source
|
||||
// prev.loc = {
|
||||
// primary: prev.loc,
|
||||
// merged: node.loc
|
||||
// };
|
||||
}
|
||||
}
|
||||
|
||||
export default function rejoinAtrule(ast, options) {
|
||||
relocateAtrules(ast, options);
|
||||
|
||||
walk(ast, {
|
||||
visit: 'Atrule',
|
||||
reverse: true,
|
||||
enter: processAtrule
|
||||
});
|
||||
};
|
53
backend/node_modules/csso/lib/restructure/2-initialMergeRuleset.js
generated
vendored
Normal file
53
backend/node_modules/csso/lib/restructure/2-initialMergeRuleset.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
import { walk } from 'css-tree';
|
||||
import {
|
||||
unsafeToSkipNode,
|
||||
isEqualSelectors,
|
||||
isEqualDeclarations,
|
||||
addSelectors,
|
||||
hasSimilarSelectors
|
||||
} from './utils.js';
|
||||
|
||||
function processRule(node, item, list) {
|
||||
const selectors = node.prelude.children;
|
||||
const declarations = node.block.children;
|
||||
|
||||
list.prevUntil(item.prev, function(prev) {
|
||||
// skip non-ruleset node if safe
|
||||
if (prev.type !== 'Rule') {
|
||||
return unsafeToSkipNode.call(selectors, prev);
|
||||
}
|
||||
|
||||
const prevSelectors = prev.prelude.children;
|
||||
const prevDeclarations = prev.block.children;
|
||||
|
||||
// try to join rulesets with equal pseudo signature
|
||||
if (node.pseudoSignature === prev.pseudoSignature) {
|
||||
// try to join by selectors
|
||||
if (isEqualSelectors(prevSelectors, selectors)) {
|
||||
prevDeclarations.appendList(declarations);
|
||||
list.remove(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
// try to join by declarations
|
||||
if (isEqualDeclarations(declarations, prevDeclarations)) {
|
||||
addSelectors(prevSelectors, selectors);
|
||||
list.remove(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// go to prev ruleset if has no selector similarities
|
||||
return hasSimilarSelectors(selectors, prevSelectors);
|
||||
});
|
||||
}
|
||||
|
||||
// NOTE: direction should be left to right, since rulesets merge to left
|
||||
// ruleset. When direction right to left unmerged rulesets may prevent lookup
|
||||
// TODO: remove initial merge
|
||||
export default function initialMergeRule(ast) {
|
||||
walk(ast, {
|
||||
visit: 'Rule',
|
||||
enter: processRule
|
||||
});
|
||||
};
|
42
backend/node_modules/csso/lib/restructure/3-disjoinRuleset.js
generated
vendored
Normal file
42
backend/node_modules/csso/lib/restructure/3-disjoinRuleset.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
import { List, walk } from 'css-tree';
|
||||
|
||||
function processRule(node, item, list) {
|
||||
const selectors = node.prelude.children;
|
||||
|
||||
// generate new rule sets:
|
||||
// .a, .b { color: red; }
|
||||
// ->
|
||||
// .a { color: red; }
|
||||
// .b { color: red; }
|
||||
|
||||
// while there are more than 1 simple selector split for rulesets
|
||||
while (selectors.head !== selectors.tail) {
|
||||
const newSelectors = new List();
|
||||
|
||||
newSelectors.insert(selectors.remove(selectors.head));
|
||||
|
||||
list.insert(list.createItem({
|
||||
type: 'Rule',
|
||||
loc: node.loc,
|
||||
prelude: {
|
||||
type: 'SelectorList',
|
||||
loc: node.prelude.loc,
|
||||
children: newSelectors
|
||||
},
|
||||
block: {
|
||||
type: 'Block',
|
||||
loc: node.block.loc,
|
||||
children: node.block.children.copy()
|
||||
},
|
||||
pseudoSignature: node.pseudoSignature
|
||||
}), item);
|
||||
}
|
||||
}
|
||||
|
||||
export default function disjoinRule(ast) {
|
||||
walk(ast, {
|
||||
visit: 'Rule',
|
||||
reverse: true,
|
||||
enter: processRule
|
||||
});
|
||||
};
|
425
backend/node_modules/csso/lib/restructure/4-restructShorthand.js
generated
vendored
Normal file
425
backend/node_modules/csso/lib/restructure/4-restructShorthand.js
generated
vendored
Normal file
@@ -0,0 +1,425 @@
|
||||
import { List, generate, walk } from 'css-tree';
|
||||
|
||||
const REPLACE = 1;
|
||||
const REMOVE = 2;
|
||||
const TOP = 0;
|
||||
const RIGHT = 1;
|
||||
const BOTTOM = 2;
|
||||
const LEFT = 3;
|
||||
const SIDES = ['top', 'right', 'bottom', 'left'];
|
||||
const SIDE = {
|
||||
'margin-top': 'top',
|
||||
'margin-right': 'right',
|
||||
'margin-bottom': 'bottom',
|
||||
'margin-left': 'left',
|
||||
|
||||
'padding-top': 'top',
|
||||
'padding-right': 'right',
|
||||
'padding-bottom': 'bottom',
|
||||
'padding-left': 'left',
|
||||
|
||||
'border-top-color': 'top',
|
||||
'border-right-color': 'right',
|
||||
'border-bottom-color': 'bottom',
|
||||
'border-left-color': 'left',
|
||||
'border-top-width': 'top',
|
||||
'border-right-width': 'right',
|
||||
'border-bottom-width': 'bottom',
|
||||
'border-left-width': 'left',
|
||||
'border-top-style': 'top',
|
||||
'border-right-style': 'right',
|
||||
'border-bottom-style': 'bottom',
|
||||
'border-left-style': 'left'
|
||||
};
|
||||
const MAIN_PROPERTY = {
|
||||
'margin': 'margin',
|
||||
'margin-top': 'margin',
|
||||
'margin-right': 'margin',
|
||||
'margin-bottom': 'margin',
|
||||
'margin-left': 'margin',
|
||||
|
||||
'padding': 'padding',
|
||||
'padding-top': 'padding',
|
||||
'padding-right': 'padding',
|
||||
'padding-bottom': 'padding',
|
||||
'padding-left': 'padding',
|
||||
|
||||
'border-color': 'border-color',
|
||||
'border-top-color': 'border-color',
|
||||
'border-right-color': 'border-color',
|
||||
'border-bottom-color': 'border-color',
|
||||
'border-left-color': 'border-color',
|
||||
'border-width': 'border-width',
|
||||
'border-top-width': 'border-width',
|
||||
'border-right-width': 'border-width',
|
||||
'border-bottom-width': 'border-width',
|
||||
'border-left-width': 'border-width',
|
||||
'border-style': 'border-style',
|
||||
'border-top-style': 'border-style',
|
||||
'border-right-style': 'border-style',
|
||||
'border-bottom-style': 'border-style',
|
||||
'border-left-style': 'border-style'
|
||||
};
|
||||
|
||||
class TRBL {
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
this.loc = null;
|
||||
this.iehack = undefined;
|
||||
this.sides = {
|
||||
'top': null,
|
||||
'right': null,
|
||||
'bottom': null,
|
||||
'left': null
|
||||
};
|
||||
}
|
||||
|
||||
getValueSequence(declaration, count) {
|
||||
const values = [];
|
||||
let iehack = '';
|
||||
const hasBadValues = declaration.value.type !== 'Value' || declaration.value.children.some(function(child) {
|
||||
let special = false;
|
||||
|
||||
switch (child.type) {
|
||||
case 'Identifier':
|
||||
switch (child.name) {
|
||||
case '\\0':
|
||||
case '\\9':
|
||||
iehack = child.name;
|
||||
return;
|
||||
|
||||
case 'inherit':
|
||||
case 'initial':
|
||||
case 'unset':
|
||||
case 'revert':
|
||||
special = child.name;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Dimension':
|
||||
switch (child.unit) {
|
||||
// is not supported until IE11
|
||||
case 'rem':
|
||||
|
||||
// v* units is too buggy across browsers and better
|
||||
// don't merge values with those units
|
||||
case 'vw':
|
||||
case 'vh':
|
||||
case 'vmin':
|
||||
case 'vmax':
|
||||
case 'vm': // IE9 supporting "vm" instead of "vmin".
|
||||
special = child.unit;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Hash': // color
|
||||
case 'Number':
|
||||
case 'Percentage':
|
||||
break;
|
||||
|
||||
case 'Function':
|
||||
if (child.name === 'var') {
|
||||
return true;
|
||||
}
|
||||
|
||||
special = child.name;
|
||||
break;
|
||||
|
||||
default:
|
||||
return true; // bad value
|
||||
}
|
||||
|
||||
values.push({
|
||||
node: child,
|
||||
special,
|
||||
important: declaration.important
|
||||
});
|
||||
});
|
||||
|
||||
if (hasBadValues || values.length > count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof this.iehack === 'string' && this.iehack !== iehack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.iehack = iehack; // move outside
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
canOverride(side, value) {
|
||||
const currentValue = this.sides[side];
|
||||
|
||||
return !currentValue || (value.important && !currentValue.important);
|
||||
}
|
||||
|
||||
add(name, declaration) {
|
||||
function attemptToAdd() {
|
||||
const sides = this.sides;
|
||||
const side = SIDE[name];
|
||||
|
||||
if (side) {
|
||||
if (side in sides === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const values = this.getValueSequence(declaration, 1);
|
||||
|
||||
if (!values || !values.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// can mix only if specials are equal
|
||||
for (const key in sides) {
|
||||
if (sides[key] !== null && sides[key].special !== values[0].special) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.canOverride(side, values[0])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
sides[side] = values[0];
|
||||
|
||||
return true;
|
||||
} else if (name === this.name) {
|
||||
const values = this.getValueSequence(declaration, 4);
|
||||
|
||||
if (!values || !values.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (values.length) {
|
||||
case 1:
|
||||
values[RIGHT] = values[TOP];
|
||||
values[BOTTOM] = values[TOP];
|
||||
values[LEFT] = values[TOP];
|
||||
break;
|
||||
|
||||
case 2:
|
||||
values[BOTTOM] = values[TOP];
|
||||
values[LEFT] = values[RIGHT];
|
||||
break;
|
||||
|
||||
case 3:
|
||||
values[LEFT] = values[RIGHT];
|
||||
break;
|
||||
}
|
||||
|
||||
// can mix only if specials are equal
|
||||
for (let i = 0; i < 4; i++) {
|
||||
for (const key in sides) {
|
||||
if (sides[key] !== null && sides[key].special !== values[i].special) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (this.canOverride(SIDES[i], values[i])) {
|
||||
sides[SIDES[i]] = values[i];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!attemptToAdd.call(this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: use it when we can refer to several points in source
|
||||
// if (this.loc) {
|
||||
// this.loc = {
|
||||
// primary: this.loc,
|
||||
// merged: declaration.loc
|
||||
// };
|
||||
// } else {
|
||||
// this.loc = declaration.loc;
|
||||
// }
|
||||
if (!this.loc) {
|
||||
this.loc = declaration.loc;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
isOkToMinimize() {
|
||||
const top = this.sides.top;
|
||||
const right = this.sides.right;
|
||||
const bottom = this.sides.bottom;
|
||||
const left = this.sides.left;
|
||||
|
||||
if (top && right && bottom && left) {
|
||||
const important =
|
||||
top.important +
|
||||
right.important +
|
||||
bottom.important +
|
||||
left.important;
|
||||
|
||||
return important === 0 || important === 4;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
getValue() {
|
||||
const result = new List();
|
||||
const sides = this.sides;
|
||||
const values = [
|
||||
sides.top,
|
||||
sides.right,
|
||||
sides.bottom,
|
||||
sides.left
|
||||
];
|
||||
const stringValues = [
|
||||
generate(sides.top.node),
|
||||
generate(sides.right.node),
|
||||
generate(sides.bottom.node),
|
||||
generate(sides.left.node)
|
||||
];
|
||||
|
||||
if (stringValues[LEFT] === stringValues[RIGHT]) {
|
||||
values.pop();
|
||||
if (stringValues[BOTTOM] === stringValues[TOP]) {
|
||||
values.pop();
|
||||
if (stringValues[RIGHT] === stringValues[TOP]) {
|
||||
values.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
result.appendData(values[i].node);
|
||||
}
|
||||
|
||||
if (this.iehack) {
|
||||
result.appendData({
|
||||
type: 'Identifier',
|
||||
loc: null,
|
||||
name: this.iehack
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'Value',
|
||||
loc: null,
|
||||
children: result
|
||||
};
|
||||
}
|
||||
|
||||
getDeclaration() {
|
||||
return {
|
||||
type: 'Declaration',
|
||||
loc: this.loc,
|
||||
important: this.sides.top.important,
|
||||
property: this.name,
|
||||
value: this.getValue()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function processRule(rule, shorts, shortDeclarations, lastShortSelector) {
|
||||
const declarations = rule.block.children;
|
||||
const selector = rule.prelude.children.first.id;
|
||||
|
||||
rule.block.children.forEachRight(function(declaration, item) {
|
||||
const property = declaration.property;
|
||||
|
||||
if (!MAIN_PROPERTY.hasOwnProperty(property)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = MAIN_PROPERTY[property];
|
||||
let shorthand;
|
||||
let operation;
|
||||
|
||||
if (!lastShortSelector || selector === lastShortSelector) {
|
||||
if (key in shorts) {
|
||||
operation = REMOVE;
|
||||
shorthand = shorts[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (!shorthand || !shorthand.add(property, declaration)) {
|
||||
operation = REPLACE;
|
||||
shorthand = new TRBL(key);
|
||||
|
||||
// if can't parse value ignore it and break shorthand children
|
||||
if (!shorthand.add(property, declaration)) {
|
||||
lastShortSelector = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
shorts[key] = shorthand;
|
||||
shortDeclarations.push({
|
||||
operation,
|
||||
block: declarations,
|
||||
item,
|
||||
shorthand
|
||||
});
|
||||
|
||||
lastShortSelector = selector;
|
||||
});
|
||||
|
||||
return lastShortSelector;
|
||||
}
|
||||
|
||||
function processShorthands(shortDeclarations, markDeclaration) {
|
||||
shortDeclarations.forEach(function(item) {
|
||||
const shorthand = item.shorthand;
|
||||
|
||||
if (!shorthand.isOkToMinimize()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.operation === REPLACE) {
|
||||
item.item.data = markDeclaration(shorthand.getDeclaration());
|
||||
} else {
|
||||
item.block.remove(item.item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default function restructBlock(ast, indexer) {
|
||||
const stylesheetMap = {};
|
||||
const shortDeclarations = [];
|
||||
|
||||
walk(ast, {
|
||||
visit: 'Rule',
|
||||
reverse: true,
|
||||
enter(node) {
|
||||
const stylesheet = this.block || this.stylesheet;
|
||||
const ruleId = (node.pseudoSignature || '') + '|' + node.prelude.children.first.id;
|
||||
let ruleMap;
|
||||
let shorts;
|
||||
|
||||
if (!stylesheetMap.hasOwnProperty(stylesheet.id)) {
|
||||
ruleMap = {
|
||||
lastShortSelector: null
|
||||
};
|
||||
stylesheetMap[stylesheet.id] = ruleMap;
|
||||
} else {
|
||||
ruleMap = stylesheetMap[stylesheet.id];
|
||||
}
|
||||
|
||||
if (ruleMap.hasOwnProperty(ruleId)) {
|
||||
shorts = ruleMap[ruleId];
|
||||
} else {
|
||||
shorts = {};
|
||||
ruleMap[ruleId] = shorts;
|
||||
}
|
||||
|
||||
ruleMap.lastShortSelector = processRule.call(this, node, shorts, shortDeclarations, ruleMap.lastShortSelector);
|
||||
}
|
||||
});
|
||||
|
||||
processShorthands(shortDeclarations, indexer.declaration);
|
||||
};
|
308
backend/node_modules/csso/lib/restructure/6-restructBlock.js
generated
vendored
Normal file
308
backend/node_modules/csso/lib/restructure/6-restructBlock.js
generated
vendored
Normal file
@@ -0,0 +1,308 @@
|
||||
import {
|
||||
walk,
|
||||
generate,
|
||||
property as resolveProperty,
|
||||
keyword as resolveKeyword
|
||||
} from 'css-tree';
|
||||
|
||||
let fingerprintId = 1;
|
||||
const dontRestructure = new Set([
|
||||
'src' // https://github.com/afelix/csso/issues/50
|
||||
]);
|
||||
|
||||
const DONT_MIX_VALUE = {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/display#Browser_compatibility
|
||||
'display': /table|ruby|flex|-(flex)?box$|grid|contents|run-in/i,
|
||||
// https://developer.mozilla.org/en/docs/Web/CSS/text-align
|
||||
'text-align': /^(start|end|match-parent|justify-all)$/i
|
||||
};
|
||||
|
||||
const SAFE_VALUES = {
|
||||
cursor: [
|
||||
'auto', 'crosshair', 'default', 'move', 'text', 'wait', 'help',
|
||||
'n-resize', 'e-resize', 's-resize', 'w-resize',
|
||||
'ne-resize', 'nw-resize', 'se-resize', 'sw-resize',
|
||||
'pointer', 'progress', 'not-allowed', 'no-drop', 'vertical-text', 'all-scroll',
|
||||
'col-resize', 'row-resize'
|
||||
],
|
||||
overflow: [
|
||||
'hidden', 'visible', 'scroll', 'auto'
|
||||
],
|
||||
position: [
|
||||
'static', 'relative', 'absolute', 'fixed'
|
||||
]
|
||||
};
|
||||
|
||||
const NEEDLESS_TABLE = {
|
||||
'border-width': ['border'],
|
||||
'border-style': ['border'],
|
||||
'border-color': ['border'],
|
||||
'border-top': ['border'],
|
||||
'border-right': ['border'],
|
||||
'border-bottom': ['border'],
|
||||
'border-left': ['border'],
|
||||
'border-top-width': ['border-top', 'border-width', 'border'],
|
||||
'border-right-width': ['border-right', 'border-width', 'border'],
|
||||
'border-bottom-width': ['border-bottom', 'border-width', 'border'],
|
||||
'border-left-width': ['border-left', 'border-width', 'border'],
|
||||
'border-top-style': ['border-top', 'border-style', 'border'],
|
||||
'border-right-style': ['border-right', 'border-style', 'border'],
|
||||
'border-bottom-style': ['border-bottom', 'border-style', 'border'],
|
||||
'border-left-style': ['border-left', 'border-style', 'border'],
|
||||
'border-top-color': ['border-top', 'border-color', 'border'],
|
||||
'border-right-color': ['border-right', 'border-color', 'border'],
|
||||
'border-bottom-color': ['border-bottom', 'border-color', 'border'],
|
||||
'border-left-color': ['border-left', 'border-color', 'border'],
|
||||
'margin-top': ['margin'],
|
||||
'margin-right': ['margin'],
|
||||
'margin-bottom': ['margin'],
|
||||
'margin-left': ['margin'],
|
||||
'padding-top': ['padding'],
|
||||
'padding-right': ['padding'],
|
||||
'padding-bottom': ['padding'],
|
||||
'padding-left': ['padding'],
|
||||
'font-style': ['font'],
|
||||
'font-variant': ['font'],
|
||||
'font-weight': ['font'],
|
||||
'font-size': ['font'],
|
||||
'font-family': ['font'],
|
||||
'list-style-type': ['list-style'],
|
||||
'list-style-position': ['list-style'],
|
||||
'list-style-image': ['list-style']
|
||||
};
|
||||
|
||||
function getPropertyFingerprint(propertyName, declaration, fingerprints) {
|
||||
const realName = resolveProperty(propertyName).basename;
|
||||
|
||||
if (realName === 'background') {
|
||||
return propertyName + ':' + generate(declaration.value);
|
||||
}
|
||||
|
||||
const declarationId = declaration.id;
|
||||
let fingerprint = fingerprints[declarationId];
|
||||
|
||||
if (!fingerprint) {
|
||||
switch (declaration.value.type) {
|
||||
case 'Value':
|
||||
const special = {};
|
||||
let vendorId = '';
|
||||
let iehack = '';
|
||||
let raw = false;
|
||||
|
||||
declaration.value.children.forEach(function walk(node) {
|
||||
switch (node.type) {
|
||||
case 'Value':
|
||||
case 'Brackets':
|
||||
case 'Parentheses':
|
||||
node.children.forEach(walk);
|
||||
break;
|
||||
|
||||
case 'Raw':
|
||||
raw = true;
|
||||
break;
|
||||
|
||||
case 'Identifier': {
|
||||
const { name } = node;
|
||||
|
||||
if (!vendorId) {
|
||||
vendorId = resolveKeyword(name).vendor;
|
||||
}
|
||||
|
||||
if (/\\[09]/.test(name)) {
|
||||
iehack = RegExp.lastMatch;
|
||||
}
|
||||
|
||||
if (SAFE_VALUES.hasOwnProperty(realName)) {
|
||||
if (SAFE_VALUES[realName].indexOf(name) === -1) {
|
||||
special[name] = true;
|
||||
}
|
||||
} else if (DONT_MIX_VALUE.hasOwnProperty(realName)) {
|
||||
if (DONT_MIX_VALUE[realName].test(name)) {
|
||||
special[name] = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Function': {
|
||||
let { name } = node;
|
||||
|
||||
if (!vendorId) {
|
||||
vendorId = resolveKeyword(name).vendor;
|
||||
}
|
||||
|
||||
if (name === 'rect') {
|
||||
// there are 2 forms of rect:
|
||||
// rect(<top>, <right>, <bottom>, <left>) - standart
|
||||
// rect(<top> <right> <bottom> <left>) – backwards compatible syntax
|
||||
// only the same form values can be merged
|
||||
const hasComma = node.children.some((node) =>
|
||||
node.type === 'Operator' && node.value === ','
|
||||
);
|
||||
|
||||
if (!hasComma) {
|
||||
name = 'rect-backward';
|
||||
}
|
||||
}
|
||||
|
||||
special[name + '()'] = true;
|
||||
|
||||
// check nested tokens too
|
||||
node.children.forEach(walk);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Dimension': {
|
||||
const { unit } = node;
|
||||
|
||||
if (/\\[09]/.test(unit)) {
|
||||
iehack = RegExp.lastMatch;
|
||||
}
|
||||
|
||||
switch (unit) {
|
||||
// is not supported until IE11
|
||||
case 'rem':
|
||||
|
||||
// v* units is too buggy across browsers and better
|
||||
// don't merge values with those units
|
||||
case 'vw':
|
||||
case 'vh':
|
||||
case 'vmin':
|
||||
case 'vmax':
|
||||
case 'vm': // IE9 supporting "vm" instead of "vmin".
|
||||
special[unit] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fingerprint = raw
|
||||
? '!' + fingerprintId++
|
||||
: '!' + Object.keys(special).sort() + '|' + iehack + vendorId;
|
||||
break;
|
||||
|
||||
case 'Raw':
|
||||
fingerprint = '!' + declaration.value.value;
|
||||
break;
|
||||
|
||||
default:
|
||||
fingerprint = generate(declaration.value);
|
||||
}
|
||||
|
||||
fingerprints[declarationId] = fingerprint;
|
||||
}
|
||||
|
||||
return propertyName + fingerprint;
|
||||
}
|
||||
|
||||
function needless(props, declaration, fingerprints) {
|
||||
const property = resolveProperty(declaration.property);
|
||||
|
||||
if (NEEDLESS_TABLE.hasOwnProperty(property.basename)) {
|
||||
const table = NEEDLESS_TABLE[property.basename];
|
||||
|
||||
for (const entry of table) {
|
||||
const ppre = getPropertyFingerprint(property.prefix + entry, declaration, fingerprints);
|
||||
const prev = props.hasOwnProperty(ppre) ? props[ppre] : null;
|
||||
|
||||
if (prev && (!declaration.important || prev.item.data.important)) {
|
||||
return prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processRule(rule, item, list, props, fingerprints) {
|
||||
const declarations = rule.block.children;
|
||||
|
||||
declarations.forEachRight(function(declaration, declarationItem) {
|
||||
const { property } = declaration;
|
||||
const fingerprint = getPropertyFingerprint(property, declaration, fingerprints);
|
||||
const prev = props[fingerprint];
|
||||
|
||||
if (prev && !dontRestructure.has(property)) {
|
||||
if (declaration.important && !prev.item.data.important) {
|
||||
props[fingerprint] = {
|
||||
block: declarations,
|
||||
item: declarationItem
|
||||
};
|
||||
|
||||
prev.block.remove(prev.item);
|
||||
|
||||
// TODO: use it when we can refer to several points in source
|
||||
// declaration.loc = {
|
||||
// primary: declaration.loc,
|
||||
// merged: prev.item.data.loc
|
||||
// };
|
||||
} else {
|
||||
declarations.remove(declarationItem);
|
||||
|
||||
// TODO: use it when we can refer to several points in source
|
||||
// prev.item.data.loc = {
|
||||
// primary: prev.item.data.loc,
|
||||
// merged: declaration.loc
|
||||
// };
|
||||
}
|
||||
} else {
|
||||
const prev = needless(props, declaration, fingerprints);
|
||||
|
||||
if (prev) {
|
||||
declarations.remove(declarationItem);
|
||||
|
||||
// TODO: use it when we can refer to several points in source
|
||||
// prev.item.data.loc = {
|
||||
// primary: prev.item.data.loc,
|
||||
// merged: declaration.loc
|
||||
// };
|
||||
} else {
|
||||
declaration.fingerprint = fingerprint;
|
||||
|
||||
props[fingerprint] = {
|
||||
block: declarations,
|
||||
item: declarationItem
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (declarations.isEmpty) {
|
||||
list.remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
export default function restructBlock(ast) {
|
||||
const stylesheetMap = {};
|
||||
const fingerprints = Object.create(null);
|
||||
|
||||
walk(ast, {
|
||||
visit: 'Rule',
|
||||
reverse: true,
|
||||
enter(node, item, list) {
|
||||
const stylesheet = this.block || this.stylesheet;
|
||||
const ruleId = (node.pseudoSignature || '') + '|' + node.prelude.children.first.id;
|
||||
let ruleMap;
|
||||
let props;
|
||||
|
||||
if (!stylesheetMap.hasOwnProperty(stylesheet.id)) {
|
||||
ruleMap = {};
|
||||
stylesheetMap[stylesheet.id] = ruleMap;
|
||||
} else {
|
||||
ruleMap = stylesheetMap[stylesheet.id];
|
||||
}
|
||||
|
||||
if (ruleMap.hasOwnProperty(ruleId)) {
|
||||
props = ruleMap[ruleId];
|
||||
} else {
|
||||
props = {};
|
||||
ruleMap[ruleId] = props;
|
||||
}
|
||||
|
||||
processRule.call(this, node, item, list, props, fingerprints);
|
||||
}
|
||||
});
|
||||
};
|
86
backend/node_modules/csso/lib/restructure/7-mergeRuleset.js
generated
vendored
Normal file
86
backend/node_modules/csso/lib/restructure/7-mergeRuleset.js
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
import { walk } from 'css-tree';
|
||||
import { unsafeToSkipNode, isEqualDeclarations} from './utils.js';
|
||||
|
||||
/*
|
||||
At this step all rules has single simple selector. We try to join by equal
|
||||
declaration blocks to first rule, e.g.
|
||||
|
||||
.a { color: red }
|
||||
b { ... }
|
||||
.b { color: red }
|
||||
->
|
||||
.a, .b { color: red }
|
||||
b { ... }
|
||||
*/
|
||||
|
||||
function processRule(node, item, list) {
|
||||
const selectors = node.prelude.children;
|
||||
const declarations = node.block.children;
|
||||
const nodeCompareMarker = selectors.first.compareMarker;
|
||||
const skippedCompareMarkers = {};
|
||||
|
||||
list.nextUntil(item.next, function(next, nextItem) {
|
||||
// skip non-ruleset node if safe
|
||||
if (next.type !== 'Rule') {
|
||||
return unsafeToSkipNode.call(selectors, next);
|
||||
}
|
||||
|
||||
if (node.pseudoSignature !== next.pseudoSignature) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const nextFirstSelector = next.prelude.children.head;
|
||||
const nextDeclarations = next.block.children;
|
||||
const nextCompareMarker = nextFirstSelector.data.compareMarker;
|
||||
|
||||
// if next ruleset has same marked as one of skipped then stop joining
|
||||
if (nextCompareMarker in skippedCompareMarkers) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// try to join by selectors
|
||||
if (selectors.head === selectors.tail) {
|
||||
if (selectors.first.id === nextFirstSelector.data.id) {
|
||||
declarations.appendList(nextDeclarations);
|
||||
list.remove(nextItem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// try to join by properties
|
||||
if (isEqualDeclarations(declarations, nextDeclarations)) {
|
||||
const nextStr = nextFirstSelector.data.id;
|
||||
|
||||
selectors.some((data, item) => {
|
||||
const curStr = data.id;
|
||||
|
||||
if (nextStr < curStr) {
|
||||
selectors.insert(nextFirstSelector, item);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!item.next) {
|
||||
selectors.insert(nextFirstSelector);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
list.remove(nextItem);
|
||||
return;
|
||||
}
|
||||
|
||||
// go to next ruleset if current one can be skipped (has no equal specificity nor element selector)
|
||||
if (nextCompareMarker === nodeCompareMarker) {
|
||||
return true;
|
||||
}
|
||||
|
||||
skippedCompareMarkers[nextCompareMarker] = true;
|
||||
});
|
||||
}
|
||||
|
||||
export default function mergeRule(ast) {
|
||||
walk(ast, {
|
||||
visit: 'Rule',
|
||||
enter: processRule
|
||||
});
|
||||
};
|
176
backend/node_modules/csso/lib/restructure/8-restructRuleset.js
generated
vendored
Normal file
176
backend/node_modules/csso/lib/restructure/8-restructRuleset.js
generated
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
import { List, walk } from 'css-tree';
|
||||
import {
|
||||
unsafeToSkipNode,
|
||||
isEqualSelectors,
|
||||
compareDeclarations,
|
||||
addSelectors
|
||||
} from './utils.js';
|
||||
|
||||
function calcSelectorLength(list) {
|
||||
return list.reduce((res, data) => res + data.id.length + 1, 0) - 1;
|
||||
}
|
||||
|
||||
function calcDeclarationsLength(tokens) {
|
||||
let length = 0;
|
||||
|
||||
for (const token of tokens) {
|
||||
length += token.length;
|
||||
}
|
||||
|
||||
return (
|
||||
length + // declarations
|
||||
tokens.length - 1 // delimeters
|
||||
);
|
||||
}
|
||||
|
||||
function processRule(node, item, list) {
|
||||
const avoidRulesMerge = this.block !== null ? this.block.avoidRulesMerge : false;
|
||||
const selectors = node.prelude.children;
|
||||
const block = node.block;
|
||||
const disallowDownMarkers = Object.create(null);
|
||||
let allowMergeUp = true;
|
||||
let allowMergeDown = true;
|
||||
|
||||
list.prevUntil(item.prev, function(prev, prevItem) {
|
||||
const prevBlock = prev.block;
|
||||
const prevType = prev.type;
|
||||
|
||||
if (prevType !== 'Rule') {
|
||||
const unsafe = unsafeToSkipNode.call(selectors, prev);
|
||||
|
||||
if (!unsafe && prevType === 'Atrule' && prevBlock) {
|
||||
walk(prevBlock, {
|
||||
visit: 'Rule',
|
||||
enter(node) {
|
||||
node.prelude.children.forEach((data) => {
|
||||
disallowDownMarkers[data.compareMarker] = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return unsafe;
|
||||
}
|
||||
|
||||
if (node.pseudoSignature !== prev.pseudoSignature) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const prevSelectors = prev.prelude.children;
|
||||
|
||||
allowMergeDown = !prevSelectors.some((selector) =>
|
||||
selector.compareMarker in disallowDownMarkers
|
||||
);
|
||||
|
||||
// try prev ruleset if simpleselectors has no equal specifity and element selector
|
||||
if (!allowMergeDown && !allowMergeUp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// try to join by selectors
|
||||
if (allowMergeUp && isEqualSelectors(prevSelectors, selectors)) {
|
||||
prevBlock.children.appendList(block.children);
|
||||
list.remove(item);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// try to join by properties
|
||||
const diff = compareDeclarations(block.children, prevBlock.children);
|
||||
|
||||
// console.log(diff.eq, diff.ne1, diff.ne2);
|
||||
|
||||
if (diff.eq.length) {
|
||||
if (!diff.ne1.length && !diff.ne2.length) {
|
||||
// equal blocks
|
||||
if (allowMergeDown) {
|
||||
addSelectors(selectors, prevSelectors);
|
||||
list.remove(prevItem);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (!avoidRulesMerge) { /* probably we don't need to prevent those merges for @keyframes
|
||||
TODO: need to be checked */
|
||||
|
||||
if (diff.ne1.length && !diff.ne2.length) {
|
||||
// prevBlock is subset block
|
||||
const selectorLength = calcSelectorLength(selectors);
|
||||
const blockLength = calcDeclarationsLength(diff.eq); // declarations length
|
||||
|
||||
if (allowMergeUp && selectorLength < blockLength) {
|
||||
addSelectors(prevSelectors, selectors);
|
||||
block.children.fromArray(diff.ne1);
|
||||
}
|
||||
} else if (!diff.ne1.length && diff.ne2.length) {
|
||||
// node is subset of prevBlock
|
||||
const selectorLength = calcSelectorLength(prevSelectors);
|
||||
const blockLength = calcDeclarationsLength(diff.eq); // declarations length
|
||||
|
||||
if (allowMergeDown && selectorLength < blockLength) {
|
||||
addSelectors(selectors, prevSelectors);
|
||||
prevBlock.children.fromArray(diff.ne2);
|
||||
}
|
||||
} else {
|
||||
// diff.ne1.length && diff.ne2.length
|
||||
// extract equal block
|
||||
const newSelector = {
|
||||
type: 'SelectorList',
|
||||
loc: null,
|
||||
children: addSelectors(prevSelectors.copy(), selectors)
|
||||
};
|
||||
const newBlockLength = calcSelectorLength(newSelector.children) + 2; // selectors length + curly braces length
|
||||
const blockLength = calcDeclarationsLength(diff.eq); // declarations length
|
||||
|
||||
// create new ruleset if declarations length greater than
|
||||
// ruleset description overhead
|
||||
if (blockLength >= newBlockLength) {
|
||||
const newItem = list.createItem({
|
||||
type: 'Rule',
|
||||
loc: null,
|
||||
prelude: newSelector,
|
||||
block: {
|
||||
type: 'Block',
|
||||
loc: null,
|
||||
children: new List().fromArray(diff.eq)
|
||||
},
|
||||
pseudoSignature: node.pseudoSignature
|
||||
});
|
||||
|
||||
block.children.fromArray(diff.ne1);
|
||||
prevBlock.children.fromArray(diff.ne2overrided);
|
||||
|
||||
if (allowMergeUp) {
|
||||
list.insert(newItem, prevItem);
|
||||
} else {
|
||||
list.insert(newItem, item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allowMergeUp) {
|
||||
// TODO: disallow up merge only if any property interception only (i.e. diff.ne2overrided.length > 0);
|
||||
// await property families to find property interception correctly
|
||||
allowMergeUp = !prevSelectors.some((prevSelector) =>
|
||||
selectors.some((selector) =>
|
||||
selector.compareMarker === prevSelector.compareMarker
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
prevSelectors.forEach((data) => {
|
||||
disallowDownMarkers[data.compareMarker] = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default function restructRule(ast) {
|
||||
walk(ast, {
|
||||
visit: 'Rule',
|
||||
reverse: true,
|
||||
enter: processRule
|
||||
});
|
||||
};
|
35
backend/node_modules/csso/lib/restructure/index.js
generated
vendored
Normal file
35
backend/node_modules/csso/lib/restructure/index.js
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
import prepare from './prepare/index.js';
|
||||
import mergeAtrule from './1-mergeAtrule.js';
|
||||
import initialMergeRuleset from './2-initialMergeRuleset.js';
|
||||
import disjoinRuleset from './3-disjoinRuleset.js';
|
||||
import restructShorthand from './4-restructShorthand.js';
|
||||
import restructBlock from './6-restructBlock.js';
|
||||
import mergeRuleset from './7-mergeRuleset.js';
|
||||
import restructRuleset from './8-restructRuleset.js';
|
||||
|
||||
export default function(ast, options) {
|
||||
// prepare ast for restructing
|
||||
const indexer = prepare(ast, options);
|
||||
options.logger('prepare', ast);
|
||||
|
||||
mergeAtrule(ast, options);
|
||||
options.logger('mergeAtrule', ast);
|
||||
|
||||
initialMergeRuleset(ast);
|
||||
options.logger('initialMergeRuleset', ast);
|
||||
|
||||
disjoinRuleset(ast);
|
||||
options.logger('disjoinRuleset', ast);
|
||||
|
||||
restructShorthand(ast, indexer);
|
||||
options.logger('restructShorthand', ast);
|
||||
|
||||
restructBlock(ast);
|
||||
options.logger('restructBlock', ast);
|
||||
|
||||
mergeRuleset(ast);
|
||||
options.logger('mergeRuleset', ast);
|
||||
|
||||
restructRuleset(ast);
|
||||
options.logger('restructRuleset', ast);
|
||||
};
|
31
backend/node_modules/csso/lib/restructure/prepare/createDeclarationIndexer.js
generated
vendored
Normal file
31
backend/node_modules/csso/lib/restructure/prepare/createDeclarationIndexer.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import { generate } from 'css-tree';
|
||||
|
||||
class Index {
|
||||
constructor() {
|
||||
this.map = new Map();
|
||||
}
|
||||
resolve(str) {
|
||||
let index = this.map.get(str);
|
||||
|
||||
if (index === undefined) {
|
||||
index = this.map.size + 1;
|
||||
this.map.set(str, index);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
};
|
||||
|
||||
export default function createDeclarationIndexer() {
|
||||
const ids = new Index();
|
||||
|
||||
return function markDeclaration(node) {
|
||||
const id = generate(node);
|
||||
|
||||
node.id = ids.resolve(id);
|
||||
node.length = id.length;
|
||||
node.fingerprint = null;
|
||||
|
||||
return node;
|
||||
};
|
||||
};
|
41
backend/node_modules/csso/lib/restructure/prepare/index.js
generated
vendored
Normal file
41
backend/node_modules/csso/lib/restructure/prepare/index.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
import { walk, generate, keyword as resolveKeyword } from 'css-tree';
|
||||
import createDeclarationIndexer from './createDeclarationIndexer.js';
|
||||
import processSelector from './processSelector.js';
|
||||
|
||||
export default function prepare(ast, options) {
|
||||
const markDeclaration = createDeclarationIndexer();
|
||||
|
||||
walk(ast, {
|
||||
visit: 'Rule',
|
||||
enter(node) {
|
||||
node.block.children.forEach(markDeclaration);
|
||||
processSelector(node, options.usage);
|
||||
}
|
||||
});
|
||||
|
||||
walk(ast, {
|
||||
visit: 'Atrule',
|
||||
enter(node) {
|
||||
if (node.prelude) {
|
||||
node.prelude.id = null; // pre-init property to avoid multiple hidden class for generate
|
||||
node.prelude.id = generate(node.prelude);
|
||||
}
|
||||
|
||||
// compare keyframe selectors by its values
|
||||
// NOTE: still no clarification about problems with keyframes selector grouping (issue #197)
|
||||
if (resolveKeyword(node.name).basename === 'keyframes') {
|
||||
node.block.avoidRulesMerge = true; /* probably we don't need to prevent those merges for @keyframes
|
||||
TODO: need to be checked */
|
||||
node.block.children.forEach(function(rule) {
|
||||
rule.prelude.children.forEach(function(simpleselector) {
|
||||
simpleselector.compareMarker = simpleselector.id;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
declaration: markDeclaration
|
||||
};
|
||||
};
|
97
backend/node_modules/csso/lib/restructure/prepare/processSelector.js
generated
vendored
Normal file
97
backend/node_modules/csso/lib/restructure/prepare/processSelector.js
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
import { generate } from 'css-tree';
|
||||
import specificity from './specificity.js';
|
||||
|
||||
const nonFreezePseudoElements = new Set([
|
||||
'first-letter',
|
||||
'first-line',
|
||||
'after',
|
||||
'before'
|
||||
]);
|
||||
const nonFreezePseudoClasses = new Set([
|
||||
'link',
|
||||
'visited',
|
||||
'hover',
|
||||
'active',
|
||||
'first-letter',
|
||||
'first-line',
|
||||
'after',
|
||||
'before'
|
||||
]);
|
||||
|
||||
export default function processSelector(node, usageData) {
|
||||
const pseudos = new Set();
|
||||
|
||||
node.prelude.children.forEach(function(simpleSelector) {
|
||||
let tagName = '*';
|
||||
let scope = 0;
|
||||
|
||||
simpleSelector.children.forEach(function(node) {
|
||||
switch (node.type) {
|
||||
case 'ClassSelector':
|
||||
if (usageData && usageData.scopes) {
|
||||
const classScope = usageData.scopes[node.name] || 0;
|
||||
|
||||
if (scope !== 0 && classScope !== scope) {
|
||||
throw new Error('Selector can\'t has classes from different scopes: ' + generate(simpleSelector));
|
||||
}
|
||||
|
||||
scope = classScope;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'PseudoClassSelector': {
|
||||
const name = node.name.toLowerCase();
|
||||
|
||||
if (!nonFreezePseudoClasses.has(name)) {
|
||||
pseudos.add(`:${name}`);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'PseudoElementSelector': {
|
||||
const name = node.name.toLowerCase();
|
||||
|
||||
if (!nonFreezePseudoElements.has(name)) {
|
||||
pseudos.add(`::${name}`);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'TypeSelector':
|
||||
tagName = node.name.toLowerCase();
|
||||
break;
|
||||
|
||||
case 'AttributeSelector':
|
||||
if (node.flags) {
|
||||
pseudos.add(`[${node.flags.toLowerCase()}]`);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'Combinator':
|
||||
tagName = '*';
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
simpleSelector.compareMarker = specificity(simpleSelector).toString();
|
||||
simpleSelector.id = null; // pre-init property to avoid multiple hidden class
|
||||
simpleSelector.id = generate(simpleSelector);
|
||||
|
||||
if (scope) {
|
||||
simpleSelector.compareMarker += ':' + scope;
|
||||
}
|
||||
|
||||
if (tagName !== '*') {
|
||||
simpleSelector.compareMarker += ',' + tagName;
|
||||
}
|
||||
});
|
||||
|
||||
// add property to all rule nodes to avoid multiple hidden class
|
||||
node.pseudoSignature = pseudos.size > 0
|
||||
? [...pseudos].sort().join(',')
|
||||
: false;
|
||||
};
|
131
backend/node_modules/csso/lib/restructure/prepare/specificity.js
generated
vendored
Normal file
131
backend/node_modules/csso/lib/restructure/prepare/specificity.js
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
import { parse } from 'css-tree';
|
||||
|
||||
function ensureSelectorList(node) {
|
||||
if (node.type === 'Raw') {
|
||||
return parse(node.value, { context: 'selectorList' });
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
function maxSpecificity(a, b) {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (a[i] !== b[i]) {
|
||||
return a[i] > b[i] ? a : b;
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
function maxSelectorListSpecificity(selectorList) {
|
||||
return ensureSelectorList(selectorList).children.reduce(
|
||||
(result, node) => maxSpecificity(specificity(node), result),
|
||||
[0, 0, 0]
|
||||
);
|
||||
}
|
||||
|
||||
// §16. Calculating a selector’s specificity
|
||||
// https://www.w3.org/TR/selectors-4/#specificity-rules
|
||||
function specificity(simpleSelector) {
|
||||
let A = 0;
|
||||
let B = 0;
|
||||
let C = 0;
|
||||
|
||||
// A selector’s specificity is calculated for a given element as follows:
|
||||
simpleSelector.children.forEach((node) => {
|
||||
switch (node.type) {
|
||||
// count the number of ID selectors in the selector (= A)
|
||||
case 'IdSelector':
|
||||
A++;
|
||||
break;
|
||||
|
||||
// count the number of class selectors, attributes selectors, ...
|
||||
case 'ClassSelector':
|
||||
case 'AttributeSelector':
|
||||
B++;
|
||||
break;
|
||||
|
||||
// ... and pseudo-classes in the selector (= B)
|
||||
case 'PseudoClassSelector':
|
||||
switch (node.name.toLowerCase()) {
|
||||
// The specificity of an :is(), :not(), or :has() pseudo-class is replaced
|
||||
// by the specificity of the most specific complex selector in its selector list argument.
|
||||
case 'not':
|
||||
case 'has':
|
||||
case 'is':
|
||||
// :matches() is used before it was renamed to :is()
|
||||
// https://github.com/w3c/csswg-drafts/issues/3258
|
||||
case 'matches':
|
||||
// Older browsers support :is() functionality as prefixed pseudo-class :any()
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/:is
|
||||
case '-webkit-any':
|
||||
case '-moz-any': {
|
||||
const [a, b, c] = maxSelectorListSpecificity(node.children.first);
|
||||
|
||||
A += a;
|
||||
B += b;
|
||||
C += c;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Analogously, the specificity of an :nth-child() or :nth-last-child() selector
|
||||
// is the specificity of the pseudo class itself (counting as one pseudo-class selector)
|
||||
// plus the specificity of the most specific complex selector in its selector list argument (if any).
|
||||
case 'nth-child':
|
||||
case 'nth-last-child': {
|
||||
const arg = node.children.first;
|
||||
|
||||
if (arg.type === 'Nth' && arg.selector) {
|
||||
const [a, b, c] = maxSelectorListSpecificity(arg.selector);
|
||||
|
||||
A += a;
|
||||
B += b + 1;
|
||||
C += c;
|
||||
} else {
|
||||
B++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// The specificity of a :where() pseudo-class is replaced by zero.
|
||||
case 'where':
|
||||
break;
|
||||
|
||||
// The four Level 2 pseudo-elements (::before, ::after, ::first-line, and ::first-letter) may,
|
||||
// for legacy reasons, be represented using the <pseudo-class-selector> grammar,
|
||||
// with only a single ":" character at their start.
|
||||
// https://www.w3.org/TR/selectors-4/#single-colon-pseudos
|
||||
case 'before':
|
||||
case 'after':
|
||||
case 'first-line':
|
||||
case 'first-letter':
|
||||
C++;
|
||||
break;
|
||||
|
||||
default:
|
||||
B++;
|
||||
}
|
||||
break;
|
||||
|
||||
// count the number of type selectors ...
|
||||
case 'TypeSelector':
|
||||
// ignore the universal selector
|
||||
if (!node.name.endsWith('*')) {
|
||||
C++;
|
||||
}
|
||||
break;
|
||||
|
||||
// ... and pseudo-elements in the selector (= C)
|
||||
case 'PseudoElementSelector':
|
||||
C++;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return [A, B, C];
|
||||
};
|
||||
|
||||
export default specificity;
|
142
backend/node_modules/csso/lib/restructure/utils.js
generated
vendored
Normal file
142
backend/node_modules/csso/lib/restructure/utils.js
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
const { hasOwnProperty } = Object.prototype;
|
||||
|
||||
export function isEqualSelectors(a, b) {
|
||||
let cursor1 = a.head;
|
||||
let cursor2 = b.head;
|
||||
|
||||
while (cursor1 !== null && cursor2 !== null && cursor1.data.id === cursor2.data.id) {
|
||||
cursor1 = cursor1.next;
|
||||
cursor2 = cursor2.next;
|
||||
}
|
||||
|
||||
return cursor1 === null && cursor2 === null;
|
||||
}
|
||||
|
||||
export function isEqualDeclarations(a, b) {
|
||||
let cursor1 = a.head;
|
||||
let cursor2 = b.head;
|
||||
|
||||
while (cursor1 !== null && cursor2 !== null && cursor1.data.id === cursor2.data.id) {
|
||||
cursor1 = cursor1.next;
|
||||
cursor2 = cursor2.next;
|
||||
}
|
||||
|
||||
return cursor1 === null && cursor2 === null;
|
||||
}
|
||||
|
||||
export function compareDeclarations(declarations1, declarations2) {
|
||||
const result = {
|
||||
eq: [],
|
||||
ne1: [],
|
||||
ne2: [],
|
||||
ne2overrided: []
|
||||
};
|
||||
|
||||
const fingerprints = Object.create(null);
|
||||
const declarations2hash = Object.create(null);
|
||||
|
||||
for (let cursor = declarations2.head; cursor; cursor = cursor.next) {
|
||||
declarations2hash[cursor.data.id] = true;
|
||||
}
|
||||
|
||||
for (let cursor = declarations1.head; cursor; cursor = cursor.next) {
|
||||
const data = cursor.data;
|
||||
|
||||
if (data.fingerprint) {
|
||||
fingerprints[data.fingerprint] = data.important;
|
||||
}
|
||||
|
||||
if (declarations2hash[data.id]) {
|
||||
declarations2hash[data.id] = false;
|
||||
result.eq.push(data);
|
||||
} else {
|
||||
result.ne1.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
for (let cursor = declarations2.head; cursor; cursor = cursor.next) {
|
||||
const data = cursor.data;
|
||||
|
||||
if (declarations2hash[data.id]) {
|
||||
// when declarations1 has an overriding declaration, this is not a difference
|
||||
// unless no !important is used on prev and !important is used on the following
|
||||
if (!hasOwnProperty.call(fingerprints, data.fingerprint) ||
|
||||
(!fingerprints[data.fingerprint] && data.important)) {
|
||||
result.ne2.push(data);
|
||||
}
|
||||
|
||||
result.ne2overrided.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function addSelectors(dest, source) {
|
||||
source.forEach((sourceData) => {
|
||||
const newStr = sourceData.id;
|
||||
let cursor = dest.head;
|
||||
|
||||
while (cursor) {
|
||||
const nextStr = cursor.data.id;
|
||||
|
||||
if (nextStr === newStr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextStr > newStr) {
|
||||
break;
|
||||
}
|
||||
|
||||
cursor = cursor.next;
|
||||
}
|
||||
|
||||
dest.insert(dest.createItem(sourceData), cursor);
|
||||
});
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
// check if simpleselectors has no equal specificity and element selector
|
||||
export function hasSimilarSelectors(selectors1, selectors2) {
|
||||
let cursor1 = selectors1.head;
|
||||
|
||||
while (cursor1 !== null) {
|
||||
let cursor2 = selectors2.head;
|
||||
|
||||
while (cursor2 !== null) {
|
||||
if (cursor1.data.compareMarker === cursor2.data.compareMarker) {
|
||||
return true;
|
||||
}
|
||||
|
||||
cursor2 = cursor2.next;
|
||||
}
|
||||
|
||||
cursor1 = cursor1.next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// test node can't to be skipped
|
||||
export function unsafeToSkipNode(node) {
|
||||
switch (node.type) {
|
||||
case 'Rule':
|
||||
// unsafe skip ruleset with selector similarities
|
||||
return hasSimilarSelectors(node.prelude.children, this);
|
||||
|
||||
case 'Atrule':
|
||||
// can skip at-rules with blocks
|
||||
if (node.block) {
|
||||
// unsafe skip at-rule if block contains something unsafe to skip
|
||||
return node.block.children.some(unsafeToSkipNode, this);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Declaration':
|
||||
return false;
|
||||
}
|
||||
|
||||
// unsafe by default
|
||||
return true;
|
||||
}
|
60
backend/node_modules/csso/lib/syntax.js
generated
vendored
Normal file
60
backend/node_modules/csso/lib/syntax.js
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
import { fork, string, url, tokenTypes as TYPE } from 'css-tree';
|
||||
import compress from './compress.js';
|
||||
import specificity from './restructure/prepare/specificity.js';
|
||||
|
||||
function encodeString(value) {
|
||||
const stringApostrophe = string.encode(value, true);
|
||||
const stringQuote = string.encode(value);
|
||||
|
||||
return stringApostrophe.length < stringQuote.length
|
||||
? stringApostrophe
|
||||
: stringQuote;
|
||||
}
|
||||
|
||||
const {
|
||||
lexer,
|
||||
tokenize,
|
||||
parse,
|
||||
generate,
|
||||
walk,
|
||||
find,
|
||||
findLast,
|
||||
findAll,
|
||||
fromPlainObject,
|
||||
toPlainObject
|
||||
} = fork({
|
||||
node: {
|
||||
String: {
|
||||
generate(node) {
|
||||
this.token(TYPE.String, encodeString(node.value));
|
||||
}
|
||||
},
|
||||
Url: {
|
||||
generate(node) {
|
||||
const encodedUrl = url.encode(node.value);
|
||||
const string = encodeString(node.value);
|
||||
|
||||
this.token(TYPE.Url,
|
||||
encodedUrl.length <= string.length + 5 /* "url()".length */
|
||||
? encodedUrl
|
||||
: 'url(' + string + ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export {
|
||||
lexer,
|
||||
tokenize,
|
||||
parse,
|
||||
generate,
|
||||
walk,
|
||||
find,
|
||||
findLast,
|
||||
findAll,
|
||||
fromPlainObject,
|
||||
toPlainObject,
|
||||
specificity,
|
||||
compress
|
||||
};
|
71
backend/node_modules/csso/lib/usage.js
generated
vendored
Normal file
71
backend/node_modules/csso/lib/usage.js
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
const { hasOwnProperty } = Object.prototype;
|
||||
|
||||
function buildMap(list, caseInsensitive) {
|
||||
const map = Object.create(null);
|
||||
|
||||
if (!Array.isArray(list)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (let name of list) {
|
||||
if (caseInsensitive) {
|
||||
name = name.toLowerCase();
|
||||
}
|
||||
|
||||
map[name] = true;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
function buildList(data) {
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tags = buildMap(data.tags, true);
|
||||
const ids = buildMap(data.ids);
|
||||
const classes = buildMap(data.classes);
|
||||
|
||||
if (tags === null &&
|
||||
ids === null &&
|
||||
classes === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
tags,
|
||||
ids,
|
||||
classes
|
||||
};
|
||||
}
|
||||
|
||||
export function buildIndex(data) {
|
||||
let scopes = false;
|
||||
|
||||
if (data.scopes && Array.isArray(data.scopes)) {
|
||||
scopes = Object.create(null);
|
||||
|
||||
for (let i = 0; i < data.scopes.length; i++) {
|
||||
const list = data.scopes[i];
|
||||
|
||||
if (!list || !Array.isArray(list)) {
|
||||
throw new Error('Wrong usage format');
|
||||
}
|
||||
|
||||
for (const name of list) {
|
||||
if (hasOwnProperty.call(scopes, name)) {
|
||||
throw new Error(`Class can't be used for several scopes: ${name}`);
|
||||
}
|
||||
|
||||
scopes[name] = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
whitelist: buildList(data),
|
||||
blacklist: buildList(data.blacklist),
|
||||
scopes
|
||||
};
|
||||
}
|
2
backend/node_modules/csso/lib/utils.js
generated
vendored
Normal file
2
backend/node_modules/csso/lib/utils.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as processSelector } from './restructure/prepare/processSelector.js';
|
||||
export * from './restructure/utils.js';
|
5
backend/node_modules/csso/lib/version.js
generated
vendored
Normal file
5
backend/node_modules/csso/lib/version.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import { createRequire } from 'module';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
export const { version } = require('../package.json');
|
19
backend/node_modules/csso/node_modules/css-tree/LICENSE
generated
vendored
Normal file
19
backend/node_modules/csso/node_modules/css-tree/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (C) 2016-2022 by Roman Dvornov
|
||||
|
||||
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.
|
193
backend/node_modules/csso/node_modules/css-tree/README.md
generated
vendored
Normal file
193
backend/node_modules/csso/node_modules/css-tree/README.md
generated
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
<img align="right" width="111" height="111"
|
||||
alt="CSSTree logo"
|
||||
src="https://cloud.githubusercontent.com/assets/270491/19243723/6f9136c6-8f21-11e6-82ac-eeeee4c6c452.png"/>
|
||||
|
||||
# CSSTree
|
||||
|
||||
[](https://www.npmjs.com/package/css-tree)
|
||||
[](https://github.com/csstree/csstree/actions/workflows/build.yml)
|
||||
[](https://coveralls.io/github/csstree/csstree?branch=master)
|
||||
[](https://www.npmjs.com/package/css-tree)
|
||||
[](https://twitter.com/csstree)
|
||||
|
||||
CSSTree is a tool set for CSS: [fast](https://github.com/postcss/benchmark) detailed parser (CSS → AST), walker (AST traversal), generator (AST → CSS) and lexer (validation and matching) based on specs and browser implementations. The main goal is to be efficient and W3C spec compliant, with focus on CSS analyzing and source-to-source transforming tasks.
|
||||
|
||||
## Features
|
||||
|
||||
- **Detailed parsing with an adjustable level of detail**
|
||||
|
||||
By default CSSTree parses CSS as detailed as possible, i.e. each single logical part is representing with its own AST node (see [AST format](docs/ast.md) for all possible node types). The parsing detail level can be changed through [parser options](docs/parsing.md#parsesource-options), for example, you can disable parsing of selectors or declaration values for component parts.
|
||||
|
||||
- **Tolerant to errors by design**
|
||||
|
||||
Parser behaves as [spec says](https://www.w3.org/TR/css-syntax-3/#error-handling): "When errors occur in CSS, the parser attempts to recover gracefully, throwing away only the minimum amount of content before returning to parsing as normal". The only thing the parser departs from the specification is that it doesn't throw away bad content, but wraps it in a special node type (`Raw`) that allows processing it later.
|
||||
|
||||
- **Fast and efficient**
|
||||
|
||||
CSSTree is created with focus on performance and effective memory consumption. Therefore it's [one of the fastest CSS parsers](https://github.com/postcss/benchmark) at the moment.
|
||||
|
||||
- **Syntax validation**
|
||||
|
||||
The build-in lexer can test CSS against syntaxes defined by W3C. CSSTree uses [mdn/data](https://github.com/mdn/data/) as a basis for lexer's dictionaries and extends it with vendor specific and legacy syntaxes. Lexer can only check the declaration values currently, but this feature will be extended to other parts of the CSS in the future.
|
||||
|
||||
## Projects using CSSTree
|
||||
|
||||
- [Svelte](https://github.com/sveltejs/svelte) – Cybernetically enhanced web apps
|
||||
- [SVGO](https://github.com/svg/svgo) – Node.js tool for optimizing SVG files
|
||||
- [CSSO](https://github.com/css/csso) – CSS minifier with structural optimizations
|
||||
- [NativeScript](https://github.com/NativeScript/NativeScript) – NativeScript empowers you to access native APIs from JavaScript directly
|
||||
- [react-native-svg](https://github.com/react-native-svg/react-native-svg) – SVG library for React Native, React Native Web, and plain React web projects
|
||||
- [penthouse](https://github.com/pocketjoso/penthouse) – Critical Path CSS Generator
|
||||
- [Bit](https://github.com/teambit/bit) – Bit is the platform for collaborating on components
|
||||
- and more...
|
||||
|
||||
## Documentation
|
||||
|
||||
- [AST format](docs/ast.md)
|
||||
- [Parsing CSS → AST](docs/parsing.md)
|
||||
- [parse(source[, options])](docs/parsing.md#parsesource-options)
|
||||
- [Serialization AST → CSS](docs/generate.md)
|
||||
- [generate(ast[, options])](docs/generate.md#generateast-options)
|
||||
- [AST traversal](docs/traversal.md)
|
||||
- [walk(ast, options)](docs/traversal.md#walkast-options)
|
||||
- [find(ast, fn)](docs/traversal.md#findast-fn)
|
||||
- [findLast(ast, fn)](docs/traversal.md#findlastast-fn)
|
||||
- [findAll(ast, fn)](docs/traversal.md#findallast-fn)
|
||||
- [Util functions](docs/utils.md)
|
||||
- Value encoding & decoding
|
||||
- [property(name)](docs/utils.md#propertyname)
|
||||
- [keyword(name)](docs/utils.md#keywordname)
|
||||
- [ident](docs/utils.md#ident)
|
||||
- [string](docs/utils.md#string)
|
||||
- [url](docs/utils.md#url)
|
||||
- AST transforming
|
||||
- [clone(ast)](docs/utils.md#cloneast)
|
||||
- [fromPlainObject(object)](docs/utils.md#fromplainobjectobject)
|
||||
- [toPlainObject(ast)](docs/utils.md#toplainobjectast)
|
||||
- [Value Definition Syntax](docs/definition-syntax.md)
|
||||
- [parse(source)](docs/definition-syntax.md#parsesource)
|
||||
- [walk(node, options, context)](docs/definition-syntax.md#walknode-options-context)
|
||||
- [generate(node, options)](docs/definition-syntax.md#generatenode-options)
|
||||
- [AST format](docs/definition-syntax.md#ast-format)
|
||||
|
||||
## Tools
|
||||
|
||||
* [AST Explorer](https://astexplorer.net/#/gist/244e2fb4da940df52bf0f4b94277db44/e79aff44611020b22cfd9708f3a99ce09b7d67a8) – explore CSSTree AST format with zero setup
|
||||
* [CSS syntax reference](https://csstree.github.io/docs/syntax.html)
|
||||
* [CSS syntax validator](https://csstree.github.io/docs/validator.html)
|
||||
|
||||
## Related projects
|
||||
|
||||
* [csstree-validator](https://github.com/csstree/validator) – NPM package to validate CSS
|
||||
* [stylelint-csstree-validator](https://github.com/csstree/stylelint-validator) – plugin for stylelint to validate CSS
|
||||
* [Grunt plugin](https://github.com/sergejmueller/grunt-csstree-validator)
|
||||
* [Gulp plugin](https://github.com/csstree/gulp-csstree)
|
||||
* [Sublime plugin](https://github.com/csstree/SublimeLinter-contrib-csstree)
|
||||
* [VS Code plugin](https://github.com/csstree/vscode-plugin)
|
||||
* [Atom plugin](https://github.com/csstree/atom-plugin)
|
||||
|
||||
## Usage
|
||||
|
||||
Install with npm:
|
||||
|
||||
```
|
||||
npm install css-tree
|
||||
```
|
||||
|
||||
Basic usage:
|
||||
|
||||
```js
|
||||
import * as csstree from 'css-tree';
|
||||
|
||||
// parse CSS to AST
|
||||
const ast = csstree.parse('.example { world: "!" }');
|
||||
|
||||
// traverse AST and modify it
|
||||
csstree.walk(ast, (node) => {
|
||||
if (node.type === 'ClassSelector' && node.name === 'example') {
|
||||
node.name = 'hello';
|
||||
}
|
||||
});
|
||||
|
||||
// generate CSS from AST
|
||||
console.log(csstree.generate(ast));
|
||||
// .hello{world:"!"}
|
||||
```
|
||||
|
||||
Syntax matching:
|
||||
|
||||
```js
|
||||
// parse CSS to AST as a declaration value
|
||||
const ast = csstree.parse('red 1px solid', { context: 'value' });
|
||||
|
||||
// match to syntax of `border` property
|
||||
const matchResult = csstree.lexer.matchProperty('border', ast);
|
||||
|
||||
// check first value node is a <color>
|
||||
console.log(matchResult.isType(ast.children.first, 'color'));
|
||||
// true
|
||||
|
||||
// get a type list matched to a node
|
||||
console.log(matchResult.getTrace(ast.children.first));
|
||||
// [ { type: 'Property', name: 'border' },
|
||||
// { type: 'Type', name: 'color' },
|
||||
// { type: 'Type', name: 'named-color' },
|
||||
// { type: 'Keyword', name: 'red' } ]
|
||||
```
|
||||
|
||||
### Exports
|
||||
|
||||
Is it possible to import just a needed part of library like a parser or a walker. That's might useful for loading time or bundle size optimisations.
|
||||
|
||||
```js
|
||||
import * as tokenizer from 'css-tree/tokenizer';
|
||||
import * as parser from 'css-tree/parser';
|
||||
import * as walker from 'css-tree/walker';
|
||||
import * as lexer from 'css-tree/lexer';
|
||||
import * as definitionSyntax from 'css-tree/definition-syntax';
|
||||
import * as data from 'css-tree/definition-syntax-data';
|
||||
import * as dataPatch from 'css-tree/definition-syntax-data-patch';
|
||||
import * as utils from 'css-tree/utils';
|
||||
```
|
||||
|
||||
### Using in a browser
|
||||
|
||||
Bundles are available for use in a browser:
|
||||
|
||||
- `dist/csstree.js` – minified IIFE with `csstree` as global
|
||||
```html
|
||||
<script src="node_modules/css-tree/dist/csstree.js"></script>
|
||||
<script>
|
||||
csstree.parse('.example { color: green }');
|
||||
</script>
|
||||
```
|
||||
|
||||
- `dist/csstree.esm.js` – minified ES module
|
||||
```html
|
||||
<script type="module">
|
||||
import { parse } from 'node_modules/css-tree/dist/csstree.esm.js'
|
||||
parse('.example { color: green }');
|
||||
</script>
|
||||
```
|
||||
|
||||
One of CDN services like `unpkg` or `jsDelivr` can be used. By default (for short path) a ESM version is exposing. For IIFE version a full path to a bundle should be specified:
|
||||
|
||||
```html
|
||||
<!-- ESM -->
|
||||
<script type="module">
|
||||
import * as csstree from 'https://cdn.jsdelivr.net/npm/css-tree';
|
||||
import * as csstree from 'https://unpkg.com/css-tree';
|
||||
</script>
|
||||
|
||||
<!-- IIFE with an export to global -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/css-tree/dist/csstree.js"></script>
|
||||
<script src="https://unpkg.com/css-tree/dist/csstree.js"></script>
|
||||
```
|
||||
|
||||
## Top level API
|
||||
|
||||

|
||||
|
||||
## License
|
||||
|
||||
MIT
|
32
backend/node_modules/csso/node_modules/css-tree/cjs/convertor/create.cjs
generated
vendored
Normal file
32
backend/node_modules/csso/node_modules/css-tree/cjs/convertor/create.cjs
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
const List = require('../utils/List.cjs');
|
||||
|
||||
function createConvertor(walk) {
|
||||
return {
|
||||
fromPlainObject(ast) {
|
||||
walk(ast, {
|
||||
enter(node) {
|
||||
if (node.children && node.children instanceof List.List === false) {
|
||||
node.children = new List.List().fromArray(node.children);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ast;
|
||||
},
|
||||
toPlainObject(ast) {
|
||||
walk(ast, {
|
||||
leave(node) {
|
||||
if (node.children && node.children instanceof List.List) {
|
||||
node.children = node.children.toArray();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ast;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exports.createConvertor = createConvertor;
|
8
backend/node_modules/csso/node_modules/css-tree/cjs/convertor/index.cjs
generated
vendored
Normal file
8
backend/node_modules/csso/node_modules/css-tree/cjs/convertor/index.cjs
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const create = require('./create.cjs');
|
||||
const index$1 = require('../walker/index.cjs');
|
||||
|
||||
const index = create.createConvertor(index$1);
|
||||
|
||||
module.exports = index;
|
7
backend/node_modules/csso/node_modules/css-tree/cjs/data-patch.cjs
generated
vendored
Normal file
7
backend/node_modules/csso/node_modules/css-tree/cjs/data-patch.cjs
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const patch = require('../data/patch.json');
|
||||
|
||||
const patch$1 = patch;
|
||||
|
||||
module.exports = patch$1;
|
97
backend/node_modules/csso/node_modules/css-tree/cjs/data.cjs
generated
vendored
Normal file
97
backend/node_modules/csso/node_modules/css-tree/cjs/data.cjs
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
'use strict';
|
||||
|
||||
const dataPatch = require('./data-patch.cjs');
|
||||
|
||||
const mdnAtrules = require('mdn-data/css/at-rules.json');
|
||||
const mdnProperties = require('mdn-data/css/properties.json');
|
||||
const mdnSyntaxes = require('mdn-data/css/syntaxes.json');
|
||||
|
||||
const extendSyntax = /^\s*\|\s*/;
|
||||
|
||||
function preprocessAtrules(dict) {
|
||||
const result = Object.create(null);
|
||||
|
||||
for (const atruleName in dict) {
|
||||
const atrule = dict[atruleName];
|
||||
let descriptors = null;
|
||||
|
||||
if (atrule.descriptors) {
|
||||
descriptors = Object.create(null);
|
||||
|
||||
for (const descriptor in atrule.descriptors) {
|
||||
descriptors[descriptor] = atrule.descriptors[descriptor].syntax;
|
||||
}
|
||||
}
|
||||
|
||||
result[atruleName.substr(1)] = {
|
||||
prelude: atrule.syntax.trim().replace(/\{(.|\s)+\}/, '').match(/^@\S+\s+([^;\{]*)/)[1].trim() || null,
|
||||
descriptors
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function patchDictionary(dict, patchDict) {
|
||||
const result = {};
|
||||
|
||||
// copy all syntaxes for an original dict
|
||||
for (const key in dict) {
|
||||
result[key] = dict[key].syntax || dict[key];
|
||||
}
|
||||
|
||||
// apply a patch
|
||||
for (const key in patchDict) {
|
||||
if (key in dict) {
|
||||
if (patchDict[key].syntax) {
|
||||
result[key] = extendSyntax.test(patchDict[key].syntax)
|
||||
? result[key] + ' ' + patchDict[key].syntax.trim()
|
||||
: patchDict[key].syntax;
|
||||
} else {
|
||||
delete result[key];
|
||||
}
|
||||
} else {
|
||||
if (patchDict[key].syntax) {
|
||||
result[key] = patchDict[key].syntax.replace(extendSyntax, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function patchAtrules(dict, patchDict) {
|
||||
const result = {};
|
||||
|
||||
// copy all syntaxes for an original dict
|
||||
for (const key in dict) {
|
||||
const patchDescriptors = (patchDict[key] && patchDict[key].descriptors) || null;
|
||||
|
||||
result[key] = {
|
||||
prelude: key in patchDict && 'prelude' in patchDict[key]
|
||||
? patchDict[key].prelude
|
||||
: dict[key].prelude || null,
|
||||
descriptors: patchDictionary(dict[key].descriptors || {}, patchDescriptors || {})
|
||||
};
|
||||
}
|
||||
|
||||
// apply a patch
|
||||
for (const key in patchDict) {
|
||||
if (!hasOwnProperty.call(dict, key)) {
|
||||
result[key] = {
|
||||
prelude: patchDict[key].prelude || null,
|
||||
descriptors: patchDict[key].descriptors && patchDictionary({}, patchDict[key].descriptors)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const definitions = {
|
||||
types: patchDictionary(mdnSyntaxes, dataPatch.types),
|
||||
atrules: patchAtrules(preprocessAtrules(mdnAtrules), dataPatch.atrules),
|
||||
properties: patchDictionary(mdnProperties, dataPatch.properties)
|
||||
};
|
||||
|
||||
module.exports = definitions;
|
16
backend/node_modules/csso/node_modules/css-tree/cjs/definition-syntax/SyntaxError.cjs
generated
vendored
Normal file
16
backend/node_modules/csso/node_modules/css-tree/cjs/definition-syntax/SyntaxError.cjs
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const createCustomError = require('../utils/create-custom-error.cjs');
|
||||
|
||||
function SyntaxError(message, input, offset) {
|
||||
return Object.assign(createCustomError.createCustomError('SyntaxError', message), {
|
||||
input,
|
||||
offset,
|
||||
rawMessage: message,
|
||||
message: message + '\n' +
|
||||
' ' + input + '\n' +
|
||||
'--' + new Array((offset || input.length) + 1).join('-') + '^'
|
||||
});
|
||||
}
|
||||
|
||||
exports.SyntaxError = SyntaxError;
|
135
backend/node_modules/csso/node_modules/css-tree/cjs/definition-syntax/generate.cjs
generated
vendored
Normal file
135
backend/node_modules/csso/node_modules/css-tree/cjs/definition-syntax/generate.cjs
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
'use strict';
|
||||
|
||||
function noop(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
function generateMultiplier(multiplier) {
|
||||
const { min, max, comma } = multiplier;
|
||||
|
||||
if (min === 0 && max === 0) {
|
||||
return comma ? '#?' : '*';
|
||||
}
|
||||
|
||||
if (min === 0 && max === 1) {
|
||||
return '?';
|
||||
}
|
||||
|
||||
if (min === 1 && max === 0) {
|
||||
return comma ? '#' : '+';
|
||||
}
|
||||
|
||||
if (min === 1 && max === 1) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return (
|
||||
(comma ? '#' : '') +
|
||||
(min === max
|
||||
? '{' + min + '}'
|
||||
: '{' + min + ',' + (max !== 0 ? max : '') + '}'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function generateTypeOpts(node) {
|
||||
switch (node.type) {
|
||||
case 'Range':
|
||||
return (
|
||||
' [' +
|
||||
(node.min === null ? '-∞' : node.min) +
|
||||
',' +
|
||||
(node.max === null ? '∞' : node.max) +
|
||||
']'
|
||||
);
|
||||
|
||||
default:
|
||||
throw new Error('Unknown node type `' + node.type + '`');
|
||||
}
|
||||
}
|
||||
|
||||
function generateSequence(node, decorate, forceBraces, compact) {
|
||||
const combinator = node.combinator === ' ' || compact ? node.combinator : ' ' + node.combinator + ' ';
|
||||
const result = node.terms
|
||||
.map(term => internalGenerate(term, decorate, forceBraces, compact))
|
||||
.join(combinator);
|
||||
|
||||
if (node.explicit || forceBraces) {
|
||||
return (compact || result[0] === ',' ? '[' : '[ ') + result + (compact ? ']' : ' ]');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function internalGenerate(node, decorate, forceBraces, compact) {
|
||||
let result;
|
||||
|
||||
switch (node.type) {
|
||||
case 'Group':
|
||||
result =
|
||||
generateSequence(node, decorate, forceBraces, compact) +
|
||||
(node.disallowEmpty ? '!' : '');
|
||||
break;
|
||||
|
||||
case 'Multiplier':
|
||||
// return since node is a composition
|
||||
return (
|
||||
internalGenerate(node.term, decorate, forceBraces, compact) +
|
||||
decorate(generateMultiplier(node), node)
|
||||
);
|
||||
|
||||
case 'Type':
|
||||
result = '<' + node.name + (node.opts ? decorate(generateTypeOpts(node.opts), node.opts) : '') + '>';
|
||||
break;
|
||||
|
||||
case 'Property':
|
||||
result = '<\'' + node.name + '\'>';
|
||||
break;
|
||||
|
||||
case 'Keyword':
|
||||
result = node.name;
|
||||
break;
|
||||
|
||||
case 'AtKeyword':
|
||||
result = '@' + node.name;
|
||||
break;
|
||||
|
||||
case 'Function':
|
||||
result = node.name + '(';
|
||||
break;
|
||||
|
||||
case 'String':
|
||||
case 'Token':
|
||||
result = node.value;
|
||||
break;
|
||||
|
||||
case 'Comma':
|
||||
result = ',';
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error('Unknown node type `' + node.type + '`');
|
||||
}
|
||||
|
||||
return decorate(result, node);
|
||||
}
|
||||
|
||||
function generate(node, options) {
|
||||
let decorate = noop;
|
||||
let forceBraces = false;
|
||||
let compact = false;
|
||||
|
||||
if (typeof options === 'function') {
|
||||
decorate = options;
|
||||
} else if (options) {
|
||||
forceBraces = Boolean(options.forceBraces);
|
||||
compact = Boolean(options.compact);
|
||||
if (typeof options.decorate === 'function') {
|
||||
decorate = options.decorate;
|
||||
}
|
||||
}
|
||||
|
||||
return internalGenerate(node, decorate, forceBraces, compact);
|
||||
}
|
||||
|
||||
exports.generate = generate;
|
13
backend/node_modules/csso/node_modules/css-tree/cjs/definition-syntax/index.cjs
generated
vendored
Normal file
13
backend/node_modules/csso/node_modules/css-tree/cjs/definition-syntax/index.cjs
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const SyntaxError = require('./SyntaxError.cjs');
|
||||
const generate = require('./generate.cjs');
|
||||
const parse = require('./parse.cjs');
|
||||
const walk = require('./walk.cjs');
|
||||
|
||||
|
||||
|
||||
exports.SyntaxError = SyntaxError.SyntaxError;
|
||||
exports.generate = generate.generate;
|
||||
exports.parse = parse.parse;
|
||||
exports.walk = walk.walk;
|
588
backend/node_modules/csso/node_modules/css-tree/cjs/definition-syntax/parse.cjs
generated
vendored
Normal file
588
backend/node_modules/csso/node_modules/css-tree/cjs/definition-syntax/parse.cjs
generated
vendored
Normal file
@@ -0,0 +1,588 @@
|
||||
'use strict';
|
||||
|
||||
const tokenizer = require('./tokenizer.cjs');
|
||||
|
||||
const TAB = 9;
|
||||
const N = 10;
|
||||
const F = 12;
|
||||
const R = 13;
|
||||
const SPACE = 32;
|
||||
const EXCLAMATIONMARK = 33; // !
|
||||
const NUMBERSIGN = 35; // #
|
||||
const AMPERSAND = 38; // &
|
||||
const APOSTROPHE = 39; // '
|
||||
const LEFTPARENTHESIS = 40; // (
|
||||
const RIGHTPARENTHESIS = 41; // )
|
||||
const ASTERISK = 42; // *
|
||||
const PLUSSIGN = 43; // +
|
||||
const COMMA = 44; // ,
|
||||
const HYPERMINUS = 45; // -
|
||||
const LESSTHANSIGN = 60; // <
|
||||
const GREATERTHANSIGN = 62; // >
|
||||
const QUESTIONMARK = 63; // ?
|
||||
const COMMERCIALAT = 64; // @
|
||||
const LEFTSQUAREBRACKET = 91; // [
|
||||
const RIGHTSQUAREBRACKET = 93; // ]
|
||||
const LEFTCURLYBRACKET = 123; // {
|
||||
const VERTICALLINE = 124; // |
|
||||
const RIGHTCURLYBRACKET = 125; // }
|
||||
const INFINITY = 8734; // ∞
|
||||
const NAME_CHAR = new Uint8Array(128).map((_, idx) =>
|
||||
/[a-zA-Z0-9\-]/.test(String.fromCharCode(idx)) ? 1 : 0
|
||||
);
|
||||
const COMBINATOR_PRECEDENCE = {
|
||||
' ': 1,
|
||||
'&&': 2,
|
||||
'||': 3,
|
||||
'|': 4
|
||||
};
|
||||
|
||||
function scanSpaces(tokenizer) {
|
||||
return tokenizer.substringToPos(
|
||||
tokenizer.findWsEnd(tokenizer.pos)
|
||||
);
|
||||
}
|
||||
|
||||
function scanWord(tokenizer) {
|
||||
let end = tokenizer.pos;
|
||||
|
||||
for (; end < tokenizer.str.length; end++) {
|
||||
const code = tokenizer.str.charCodeAt(end);
|
||||
if (code >= 128 || NAME_CHAR[code] === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenizer.pos === end) {
|
||||
tokenizer.error('Expect a keyword');
|
||||
}
|
||||
|
||||
return tokenizer.substringToPos(end);
|
||||
}
|
||||
|
||||
function scanNumber(tokenizer) {
|
||||
let end = tokenizer.pos;
|
||||
|
||||
for (; end < tokenizer.str.length; end++) {
|
||||
const code = tokenizer.str.charCodeAt(end);
|
||||
if (code < 48 || code > 57) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenizer.pos === end) {
|
||||
tokenizer.error('Expect a number');
|
||||
}
|
||||
|
||||
return tokenizer.substringToPos(end);
|
||||
}
|
||||
|
||||
function scanString(tokenizer) {
|
||||
const end = tokenizer.str.indexOf('\'', tokenizer.pos + 1);
|
||||
|
||||
if (end === -1) {
|
||||
tokenizer.pos = tokenizer.str.length;
|
||||
tokenizer.error('Expect an apostrophe');
|
||||
}
|
||||
|
||||
return tokenizer.substringToPos(end + 1);
|
||||
}
|
||||
|
||||
function readMultiplierRange(tokenizer) {
|
||||
let min = null;
|
||||
let max = null;
|
||||
|
||||
tokenizer.eat(LEFTCURLYBRACKET);
|
||||
|
||||
min = scanNumber(tokenizer);
|
||||
|
||||
if (tokenizer.charCode() === COMMA) {
|
||||
tokenizer.pos++;
|
||||
if (tokenizer.charCode() !== RIGHTCURLYBRACKET) {
|
||||
max = scanNumber(tokenizer);
|
||||
}
|
||||
} else {
|
||||
max = min;
|
||||
}
|
||||
|
||||
tokenizer.eat(RIGHTCURLYBRACKET);
|
||||
|
||||
return {
|
||||
min: Number(min),
|
||||
max: max ? Number(max) : 0
|
||||
};
|
||||
}
|
||||
|
||||
function readMultiplier(tokenizer) {
|
||||
let range = null;
|
||||
let comma = false;
|
||||
|
||||
switch (tokenizer.charCode()) {
|
||||
case ASTERISK:
|
||||
tokenizer.pos++;
|
||||
|
||||
range = {
|
||||
min: 0,
|
||||
max: 0
|
||||
};
|
||||
|
||||
break;
|
||||
|
||||
case PLUSSIGN:
|
||||
tokenizer.pos++;
|
||||
|
||||
range = {
|
||||
min: 1,
|
||||
max: 0
|
||||
};
|
||||
|
||||
break;
|
||||
|
||||
case QUESTIONMARK:
|
||||
tokenizer.pos++;
|
||||
|
||||
range = {
|
||||
min: 0,
|
||||
max: 1
|
||||
};
|
||||
|
||||
break;
|
||||
|
||||
case NUMBERSIGN:
|
||||
tokenizer.pos++;
|
||||
|
||||
comma = true;
|
||||
|
||||
if (tokenizer.charCode() === LEFTCURLYBRACKET) {
|
||||
range = readMultiplierRange(tokenizer);
|
||||
} else if (tokenizer.charCode() === QUESTIONMARK) {
|
||||
// https://www.w3.org/TR/css-values-4/#component-multipliers
|
||||
// > the # and ? multipliers may be stacked as #?
|
||||
// In this case just treat "#?" as a single multiplier
|
||||
// { min: 0, max: 0, comma: true }
|
||||
tokenizer.pos++;
|
||||
range = {
|
||||
min: 0,
|
||||
max: 0
|
||||
};
|
||||
} else {
|
||||
range = {
|
||||
min: 1,
|
||||
max: 0
|
||||
};
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LEFTCURLYBRACKET:
|
||||
range = readMultiplierRange(tokenizer);
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'Multiplier',
|
||||
comma,
|
||||
min: range.min,
|
||||
max: range.max,
|
||||
term: null
|
||||
};
|
||||
}
|
||||
|
||||
function maybeMultiplied(tokenizer, node) {
|
||||
const multiplier = readMultiplier(tokenizer);
|
||||
|
||||
if (multiplier !== null) {
|
||||
multiplier.term = node;
|
||||
|
||||
// https://www.w3.org/TR/css-values-4/#component-multipliers
|
||||
// > The + and # multipliers may be stacked as +#;
|
||||
// Represent "+#" as nested multipliers:
|
||||
// { ...<multiplier #>,
|
||||
// term: {
|
||||
// ...<multipler +>,
|
||||
// term: node
|
||||
// }
|
||||
// }
|
||||
if (tokenizer.charCode() === NUMBERSIGN &&
|
||||
tokenizer.charCodeAt(tokenizer.pos - 1) === PLUSSIGN) {
|
||||
return maybeMultiplied(tokenizer, multiplier);
|
||||
}
|
||||
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
function maybeToken(tokenizer) {
|
||||
const ch = tokenizer.peek();
|
||||
|
||||
if (ch === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'Token',
|
||||
value: ch
|
||||
};
|
||||
}
|
||||
|
||||
function readProperty(tokenizer) {
|
||||
let name;
|
||||
|
||||
tokenizer.eat(LESSTHANSIGN);
|
||||
tokenizer.eat(APOSTROPHE);
|
||||
|
||||
name = scanWord(tokenizer);
|
||||
|
||||
tokenizer.eat(APOSTROPHE);
|
||||
tokenizer.eat(GREATERTHANSIGN);
|
||||
|
||||
return maybeMultiplied(tokenizer, {
|
||||
type: 'Property',
|
||||
name
|
||||
});
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-values-3/#numeric-ranges
|
||||
// 4.1. Range Restrictions and Range Definition Notation
|
||||
//
|
||||
// Range restrictions can be annotated in the numeric type notation using CSS bracketed
|
||||
// range notation—[min,max]—within the angle brackets, after the identifying keyword,
|
||||
// indicating a closed range between (and including) min and max.
|
||||
// For example, <integer [0, 10]> indicates an integer between 0 and 10, inclusive.
|
||||
function readTypeRange(tokenizer) {
|
||||
// use null for Infinity to make AST format JSON serializable/deserializable
|
||||
let min = null; // -Infinity
|
||||
let max = null; // Infinity
|
||||
let sign = 1;
|
||||
|
||||
tokenizer.eat(LEFTSQUAREBRACKET);
|
||||
|
||||
if (tokenizer.charCode() === HYPERMINUS) {
|
||||
tokenizer.peek();
|
||||
sign = -1;
|
||||
}
|
||||
|
||||
if (sign == -1 && tokenizer.charCode() === INFINITY) {
|
||||
tokenizer.peek();
|
||||
} else {
|
||||
min = sign * Number(scanNumber(tokenizer));
|
||||
|
||||
if (NAME_CHAR[tokenizer.charCode()] !== 0) {
|
||||
min += scanWord(tokenizer);
|
||||
}
|
||||
}
|
||||
|
||||
scanSpaces(tokenizer);
|
||||
tokenizer.eat(COMMA);
|
||||
scanSpaces(tokenizer);
|
||||
|
||||
if (tokenizer.charCode() === INFINITY) {
|
||||
tokenizer.peek();
|
||||
} else {
|
||||
sign = 1;
|
||||
|
||||
if (tokenizer.charCode() === HYPERMINUS) {
|
||||
tokenizer.peek();
|
||||
sign = -1;
|
||||
}
|
||||
|
||||
max = sign * Number(scanNumber(tokenizer));
|
||||
|
||||
if (NAME_CHAR[tokenizer.charCode()] !== 0) {
|
||||
max += scanWord(tokenizer);
|
||||
}
|
||||
}
|
||||
|
||||
tokenizer.eat(RIGHTSQUAREBRACKET);
|
||||
|
||||
return {
|
||||
type: 'Range',
|
||||
min,
|
||||
max
|
||||
};
|
||||
}
|
||||
|
||||
function readType(tokenizer) {
|
||||
let name;
|
||||
let opts = null;
|
||||
|
||||
tokenizer.eat(LESSTHANSIGN);
|
||||
name = scanWord(tokenizer);
|
||||
|
||||
if (tokenizer.charCode() === LEFTPARENTHESIS &&
|
||||
tokenizer.nextCharCode() === RIGHTPARENTHESIS) {
|
||||
tokenizer.pos += 2;
|
||||
name += '()';
|
||||
}
|
||||
|
||||
if (tokenizer.charCodeAt(tokenizer.findWsEnd(tokenizer.pos)) === LEFTSQUAREBRACKET) {
|
||||
scanSpaces(tokenizer);
|
||||
opts = readTypeRange(tokenizer);
|
||||
}
|
||||
|
||||
tokenizer.eat(GREATERTHANSIGN);
|
||||
|
||||
return maybeMultiplied(tokenizer, {
|
||||
type: 'Type',
|
||||
name,
|
||||
opts
|
||||
});
|
||||
}
|
||||
|
||||
function readKeywordOrFunction(tokenizer) {
|
||||
const name = scanWord(tokenizer);
|
||||
|
||||
if (tokenizer.charCode() === LEFTPARENTHESIS) {
|
||||
tokenizer.pos++;
|
||||
|
||||
return {
|
||||
type: 'Function',
|
||||
name
|
||||
};
|
||||
}
|
||||
|
||||
return maybeMultiplied(tokenizer, {
|
||||
type: 'Keyword',
|
||||
name
|
||||
});
|
||||
}
|
||||
|
||||
function regroupTerms(terms, combinators) {
|
||||
function createGroup(terms, combinator) {
|
||||
return {
|
||||
type: 'Group',
|
||||
terms,
|
||||
combinator,
|
||||
disallowEmpty: false,
|
||||
explicit: false
|
||||
};
|
||||
}
|
||||
|
||||
let combinator;
|
||||
|
||||
combinators = Object.keys(combinators)
|
||||
.sort((a, b) => COMBINATOR_PRECEDENCE[a] - COMBINATOR_PRECEDENCE[b]);
|
||||
|
||||
while (combinators.length > 0) {
|
||||
combinator = combinators.shift();
|
||||
|
||||
let i = 0;
|
||||
let subgroupStart = 0;
|
||||
|
||||
for (; i < terms.length; i++) {
|
||||
const term = terms[i];
|
||||
|
||||
if (term.type === 'Combinator') {
|
||||
if (term.value === combinator) {
|
||||
if (subgroupStart === -1) {
|
||||
subgroupStart = i - 1;
|
||||
}
|
||||
terms.splice(i, 1);
|
||||
i--;
|
||||
} else {
|
||||
if (subgroupStart !== -1 && i - subgroupStart > 1) {
|
||||
terms.splice(
|
||||
subgroupStart,
|
||||
i - subgroupStart,
|
||||
createGroup(terms.slice(subgroupStart, i), combinator)
|
||||
);
|
||||
i = subgroupStart + 1;
|
||||
}
|
||||
subgroupStart = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (subgroupStart !== -1 && combinators.length) {
|
||||
terms.splice(
|
||||
subgroupStart,
|
||||
i - subgroupStart,
|
||||
createGroup(terms.slice(subgroupStart, i), combinator)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return combinator;
|
||||
}
|
||||
|
||||
function readImplicitGroup(tokenizer) {
|
||||
const terms = [];
|
||||
const combinators = {};
|
||||
let token;
|
||||
let prevToken = null;
|
||||
let prevTokenPos = tokenizer.pos;
|
||||
|
||||
while (token = peek(tokenizer)) {
|
||||
if (token.type !== 'Spaces') {
|
||||
if (token.type === 'Combinator') {
|
||||
// check for combinator in group beginning and double combinator sequence
|
||||
if (prevToken === null || prevToken.type === 'Combinator') {
|
||||
tokenizer.pos = prevTokenPos;
|
||||
tokenizer.error('Unexpected combinator');
|
||||
}
|
||||
|
||||
combinators[token.value] = true;
|
||||
} else if (prevToken !== null && prevToken.type !== 'Combinator') {
|
||||
combinators[' '] = true; // a b
|
||||
terms.push({
|
||||
type: 'Combinator',
|
||||
value: ' '
|
||||
});
|
||||
}
|
||||
|
||||
terms.push(token);
|
||||
prevToken = token;
|
||||
prevTokenPos = tokenizer.pos;
|
||||
}
|
||||
}
|
||||
|
||||
// check for combinator in group ending
|
||||
if (prevToken !== null && prevToken.type === 'Combinator') {
|
||||
tokenizer.pos -= prevTokenPos;
|
||||
tokenizer.error('Unexpected combinator');
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'Group',
|
||||
terms,
|
||||
combinator: regroupTerms(terms, combinators) || ' ',
|
||||
disallowEmpty: false,
|
||||
explicit: false
|
||||
};
|
||||
}
|
||||
|
||||
function readGroup(tokenizer) {
|
||||
let result;
|
||||
|
||||
tokenizer.eat(LEFTSQUAREBRACKET);
|
||||
result = readImplicitGroup(tokenizer);
|
||||
tokenizer.eat(RIGHTSQUAREBRACKET);
|
||||
|
||||
result.explicit = true;
|
||||
|
||||
if (tokenizer.charCode() === EXCLAMATIONMARK) {
|
||||
tokenizer.pos++;
|
||||
result.disallowEmpty = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function peek(tokenizer) {
|
||||
let code = tokenizer.charCode();
|
||||
|
||||
if (code < 128 && NAME_CHAR[code] === 1) {
|
||||
return readKeywordOrFunction(tokenizer);
|
||||
}
|
||||
|
||||
switch (code) {
|
||||
case RIGHTSQUAREBRACKET:
|
||||
// don't eat, stop scan a group
|
||||
break;
|
||||
|
||||
case LEFTSQUAREBRACKET:
|
||||
return maybeMultiplied(tokenizer, readGroup(tokenizer));
|
||||
|
||||
case LESSTHANSIGN:
|
||||
return tokenizer.nextCharCode() === APOSTROPHE
|
||||
? readProperty(tokenizer)
|
||||
: readType(tokenizer);
|
||||
|
||||
case VERTICALLINE:
|
||||
return {
|
||||
type: 'Combinator',
|
||||
value: tokenizer.substringToPos(
|
||||
tokenizer.pos + (tokenizer.nextCharCode() === VERTICALLINE ? 2 : 1)
|
||||
)
|
||||
};
|
||||
|
||||
case AMPERSAND:
|
||||
tokenizer.pos++;
|
||||
tokenizer.eat(AMPERSAND);
|
||||
|
||||
return {
|
||||
type: 'Combinator',
|
||||
value: '&&'
|
||||
};
|
||||
|
||||
case COMMA:
|
||||
tokenizer.pos++;
|
||||
return {
|
||||
type: 'Comma'
|
||||
};
|
||||
|
||||
case APOSTROPHE:
|
||||
return maybeMultiplied(tokenizer, {
|
||||
type: 'String',
|
||||
value: scanString(tokenizer)
|
||||
});
|
||||
|
||||
case SPACE:
|
||||
case TAB:
|
||||
case N:
|
||||
case R:
|
||||
case F:
|
||||
return {
|
||||
type: 'Spaces',
|
||||
value: scanSpaces(tokenizer)
|
||||
};
|
||||
|
||||
case COMMERCIALAT:
|
||||
code = tokenizer.nextCharCode();
|
||||
|
||||
if (code < 128 && NAME_CHAR[code] === 1) {
|
||||
tokenizer.pos++;
|
||||
return {
|
||||
type: 'AtKeyword',
|
||||
name: scanWord(tokenizer)
|
||||
};
|
||||
}
|
||||
|
||||
return maybeToken(tokenizer);
|
||||
|
||||
case ASTERISK:
|
||||
case PLUSSIGN:
|
||||
case QUESTIONMARK:
|
||||
case NUMBERSIGN:
|
||||
case EXCLAMATIONMARK:
|
||||
// prohibited tokens (used as a multiplier start)
|
||||
break;
|
||||
|
||||
case LEFTCURLYBRACKET:
|
||||
// LEFTCURLYBRACKET is allowed since mdn/data uses it w/o quoting
|
||||
// check next char isn't a number, because it's likely a disjoined multiplier
|
||||
code = tokenizer.nextCharCode();
|
||||
|
||||
if (code < 48 || code > 57) {
|
||||
return maybeToken(tokenizer);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return maybeToken(tokenizer);
|
||||
}
|
||||
}
|
||||
|
||||
function parse(source) {
|
||||
const tokenizer$1 = new tokenizer.Tokenizer(source);
|
||||
const result = readImplicitGroup(tokenizer$1);
|
||||
|
||||
if (tokenizer$1.pos !== source.length) {
|
||||
tokenizer$1.error('Unexpected input');
|
||||
}
|
||||
|
||||
// reduce redundant groups with single group term
|
||||
if (result.terms.length === 1 && result.terms[0].type === 'Group') {
|
||||
return result.terms[0];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
exports.parse = parse;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user