Fix code quality violations and exclude Manifest from checks

Document application modes (development/debug/production)
Add global file drop handler, order column normalization, SPA hash fix
Serve CDN assets via /_vendor/ URLs instead of merging into bundles
Add production minification with license preservation
Improve JSON formatting for debugging and production optimization
Add CDN asset caching with CSS URL inlining for production builds
Add three-mode system (development, debug, production)
Update Manifest CLAUDE.md to reflect helper class architecture
Refactor Manifest.php into helper classes for better organization
Pre-manifest-refactor checkpoint: Add app_mode documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2026-01-14 10:38:22 +00:00
parent bb9046af1b
commit d523f0f600
2355 changed files with 231384 additions and 32223 deletions

421
node_modules/csso/CHANGELOG.md generated vendored
View File

@@ -1,421 +0,0 @@
## 4.2.0 (November 26, 2020)
- Trim Custom Property values when possible (#393)
- Fixed removing unit for zero-length dimentions in `min()`, `max()` and `clamp()` functions (#426)
- Fixed crash on bad value in TRBL declaration value (#412)
## 4.1.1 (November 15, 2020)
- Fixed build setup to exclude full `mdn/data` that reduced the lib size:
* dist/csso.js: 794.5Kb -> 255.2Kb
* dist/csso.min.js: 394.4Kb -> 194.2Kb
* package size: 237.8 kB -> 156.1 kB
* package unpacked size: 1.3 MB -> 586.8 kB
## 4.1.0 (October 27, 2020)
- Bumped [CSSTree](https://github.com/csstree/csstree) to `^1.0.0`
- Fixed wrongly merging of TRBL values when one of them contains `var()` (#420)
- Fixed wrongly merging of pseudo class and element with the same name, e.g. `:-ms-input-placeholder` and `::-ms-input-placeholder` (#383, #416)
- Fixed wrongly merging of `overflow` fallback (#415)
## 4.0.3 (March 24, 2020)
- Prevented percent sign removal in `flex`/`-ms-flex` (#410)
- Fixed restructuring optimisation in some cases (@charlessuh & @chsuh, #358, #411)
- Bumped dependencies (@AviVahl, #409)
## 4.0.2 (October 28, 2019)
- Fixed clean stage to avoid exceptions when source has unparsed or bad parts (#380)
- Fixed wrong percentage sign removal for zero values (#395)
## 4.0.1 (October 22, 2019)
- Bumped CSSTree to [`1.0.0-alpha.37`](https://github.com/csstree/csstree/releases/tag/v1.0.0-alpha.37) to avoid source map generation inconsistency across Node.js versions
## 4.0.0 (October 21, 2019)
- Dropped support for Node.js < 8
- Refreshed dev dependencies and scripts
- Bumped [CSSTree](https://github.com/csstree/csstree) to `1.0.0-alpha.36` (#399)
- Changed bundle files: `dist/csso.js` and `dist/csso.min.js` instead single `dist/csso-browser.js` (min version)
- Expose `compress()` as `syntax.compress()`
## 3.5.1 (June 7, 2018)
- Bumped [CSSTree](https://github.com/csstree/csstree) to `1.0.0-alpha.29` (fixes some issues)
## 3.5.0 (January 14, 2018)
- Migrated to [CSSTree](https://github.com/csstree/csstree) `1.0.0-alpha.27`
## 3.4.0 (November 3, 2017)
- Added percent sign removal for zero percentages for some properties that is safe (@RubaXa, #286)
- Removed unit removal for zero values in `-ms-flex` due it breaks flex in IE10/11 (#362)
- Improved performance of selectors comparison (@smelukov, #343)
## 3.3.1 (October 17, 2017)
- Fixed merge of `position` declarations when `sticky` fallback is using (@gruzzilkin, #356)
## 3.3.0 (October 12, 2017)
- Migrated to [CSSTree](https://github.com/csstree/csstree) `1.0.0-alpha25`
- Changed AST format (see [CSSTree change log](https://github.com/csstree/csstree/blob/master/HISTORY.md) for details)
- Fixed performance issue when generate CSS with source map (quadratic increase in time depending on the size of the CSS)
## 3.2.0 (September 10, 2017)
- Fixed named color compression to apply only when an identifier is guaranteed to be a color
- Added lifting of `@keyframes` to the beginning of style sheet (chunk), but after `@charset` and `@import` rules
- Added removal of `@keyframes`, `@media` and `@supports` with no prelude
- Added removal of duplicate `@keyframes` (#202)
- Added new option `forceMediaMerge` to force media rules merging. It's unsafe in general, but works fine in many cases. Use it on your own risk (#350)
- Bumped `CSSTree` to `1.0.0-alpha23`
## 3.1.1 (April 25, 2017)
- Fixed crash on a number processing when it used not in a list (#335)
## 3.1.0 (April 24, 2017)
- Implemented optimisation for `none` keyword in `border` and `outline` properties (@zoobestik, #41)
- Implemented replacing `rgba(x, x, x, 0)` to `transparent`
- Fixed plus sign omitting for numbers following identifier, hex color, number or unicode range, since it can change the meaning of CSS (e.g. `calc(1px+2px)` has been optimized to `calc(1px2px)` before, now it stays the same)
- Improved usage filtering for nested selectors (i.e. for `:nth-*()`, `:has()`, `:matches` and other pseudos)
- Implemented `blacklist` filtering in usage (#334, see [Black list filtering](https://github.com/css/csso#black-list-filtering))
- Improved white space removing, now white spaces are removing in the beginning and at the ending of sequences, and between stylesheet and block nodes
- Bumped `CSSTree` to `1.0.0-alpha19`
## 3.0.1 (March 14, 2017)
- Fixed declaration merging when declaration contains an `!important`
## 3.0.0 (March 13, 2017)
- Migrated to [CSSTree](https://github.com/csstree/csstree) as AST backend and exposed its API behind `syntax` property
- Extracted CLI into standalone package [css/csso-cli](https://github.com/css/csso-cli)
## 2.3.1 (January 6, 2017)
- Added `\0` IE hack support (#320)
## 2.3.0 (October 25, 2016)
- Added `beforeCompress` and `afterCompress` options support (#316)
- Fixed crash on empty argument in function (#317)
## 2.2.1 (July 25, 2016)
- Fixed shorthand optimisation issue when value has a color value or something unknown (#311)
- Fixed `cursor` broken fallback (#306)
## 2.2.0 (June 23, 2016)
- Implement AST cloning by adding `clone()` [function](https://github.com/css/csso#cloneast) and `clone` [option](https://github.com/css/csso#compressast-options) for `compress()` function (#296)
- Fix parse and translate attribute selector with flags but w/o operator (i.e. `[attrName i]`)
- Don't merge rules with flagged attribute selectors with others (#291)
- Take in account functions when merge TRBL-properties (#297, thanks to @ArturAralin)
- Improve partial merge (#304)
- Tweak scanner, reduce code deoptimizations and other small improvements
## 2.1.1 (May 11, 2016)
- Fix wrong declaration with `\9` hack merge (#295)
## 2.1.0 (May 8, 2016)
- New option `comments` to specify what comments to left: `exclamation`, `first-exclamation` and `none`
- Add `offset` to CSS parse error details
- Fix token `offset` computation
## 2.0.0 (April 5, 2016)
- No more `gonzales` AST format and related code
- `minify()` and `minifyBlock()` is always return an object as result now (i.e. `{ css: String, map: SourceMapGenerator or null }`)
- `parse()`
- Returns AST in new format (so called `internal`)
- Dynamic scanner implemented
- New AST format + dynamic scanner = performance boost and less memory consumption
- No more `context` argument, context should be specified via `options`
- Supported contexts now: `stylesheet`, `atrule`, `atruleExpression`, `ruleset`, `selector`, `simpleSelector`, `block`, `declaration` and `value`
- Drop `needPositions` option, `positions` option should be used instead
- Drop `needInfo` option, `info` object is attaching to nodes when some information is requested by `options`
- `options` should be an object, otherwise it treats as empty object
- `compress()`
- No more AST converting (performance boost and less memory consumption)
- Drop `outputAst` option
- Returns an object as result instead of AST (i.e. `{ ast: Object }`)
- Drop methods: `justDoIt()`, `stringify()`, `cleanInfo()`
## 1.8.1 (March 30, 2016)
- Don't remove spaces after function/braces/urls since unsafe (#289)
## 1.8.0 (March 24, 2016)
- Usage data support:
- Filter rulesets by tag names, class names and ids white lists.
- More aggressive ruleset moving using class name scopes information.
- New CLI option `--usage` to pass usage data file.
- Improve initial ruleset merge
- Change order of ruleset processing, now it's left to right. Previously unmerged rulesets may prevent lookup and other rulesets merge.
- Difference in pseudo signature just prevents ruleset merging, but don't stop lookup.
- Simplify block comparison (performance).
- New method `csso.minifyBlock()` for css block compression (e.g. `style` attribute content).
- Ruleset merge improvement: at-rules with block (like `@media` or `@supports`) now can be skipped during ruleset merge lookup if doesn't contain something prevents it.
- FIX: Add negation (`:not()`) to pseudo signature to avoid unsafe merge (old browsers doesn't support it).
- FIX: Check nested parts of value when compute compatibility. It fixes unsafe property merging.
## 1.7.1 (March 16, 2016)
- pass block mode to tokenizer for correct parsing of declarations properties with `//` hack
- fix wrongly `@import` and `@charset` removal on double exclamation comment
## 1.7.0 (March 10, 2016)
- support for [CSS Custom Properties](https://www.w3.org/TR/css-variables/) (#279)
- rework RTBL properties merge better merge for values with special units and don't merge values with CSS-wide keywords (#255)
- remove redundant universal selectors (#178)
- take in account `!important` when check for property overriding (#280)
- don't merge `text-align` declarations with some values (#281)
- add spaces around `/deep/` combinator on translate, since it together with universal selector can produce a comment
- better keyword and property name resolving (tolerant to hacks and so on)
- integration improvements
- compression log function could be customized by `logger` option for `compress()` and `minify()`
- make possible to set initial line and column for parser
## 1.6.4 (March 1, 2016)
- `npm` publish issue (#276)
## 1.6.3 (February 29, 2016)
- add `file` to generated source map since other tools can relay on it in source map transform chain
## 1.6.2 (February 29, 2016)
- tweak some parse error messages and their positions
- fix `:not()` parsing and selector groups in `:not()` is supported now (#215)
- `needPosition` parser option is deprecated, `positions` option should be used instead (`needPosition` is used still if `positions` option omitted)
- expose internal AST API as `csso.internal.*`
- `minify()` adds `sourcesContent` by default when source map is generated
- bring back support for node.js `0.10` until major release (#275)
## 1.6.1 (February 28, 2016)
- fix exception on zero length dimension compress outside declaration (#273)
## 1.6.0 (February 27, 2016)
- **source maps support**
- parser remake:
- various parsing issues fixed
- fix unicode sequence processing in ident (#191)
- support for flags in attribute selector (#270)
- position (line and column) of parse error (#109)
- 4x performance boost, less memory consumption
- compressor refactoring
- internal AST is using doubly linked lists (with safe transformation support during iteration) instead of arrays
- rename `restructuring` to `restructure` option for `minify()`/`compress()` (`restructuring` is alias for `restructure` now, with lower priority)
- unquote urls when possible (#141, #60)
- setup code coverage and a number of related fixes
- add eslint to check unused things
## 1.5.4 (January 27, 2016)
- one more fix (in `restructRuleset` this time) with merge of rulesets when a ruleset with same specificity places between them (#264)
- disable partial merge of rulesets in `@keyframes` rulesets (until sure it's correct)
## 1.5.3 (January 25, 2016)
- don't override display values with different browser support (#259)
- fix publish issue (one of modules leak in development state)
## 1.5.2 (January 24, 2016)
- don't merge rulesets if between them a ruleset with same specificity (#264)
## 1.5.1 (January 14, 2016)
- ensure `-` is not used as an identifier in attribute selectors (thanks to @mathiasbynens)
- fix broken `justDoIt()` function
- various small fixes
## 1.5.0 (January 14, 2016)
### Parser
- attach minus to number
### Compressor
- split code base into small modules and related refactoring
- introduce internal AST format for compressor (`gonzales``internal` and `internal``gonzales` convertors, walkers, translator)
- various optimizations: no snapshots, using caches and indexes
- sort selectors, merge selectors in alphabet order
- compute selector's specificity
- better ruleset restructuring, improve compression of partially equal blocks
- better ruleset merge not only closest but also disjoined by other rulesets when safe
- join `@media` with same query
- `outputAst` new option to specify output AST format (`gonzales` by default for backward compatibility)
- remove quotes surrounding attribute values in attribute selectors when possible (#73)
- replace `from``0%` and `100%``to` at `@keyframes` (#205)
- prevent partial merge of rulesets at `@keyframes` (#80, #197)
### API
- walker for `gonzales` AST was implemented
### CLI
- new option `--stat` (output stat in `stderr`)
- new optional parameter `level` for `--debug` option
## 1.4.4 (December 10, 2015)
- prevent removal of spaces after braces that before identifier that breaking at-rules expressions (#258)
## 1.4.3 (December 4, 2015)
- fix unicode-range parsing that cause to wrong function detection (#250)
## 1.4.2 (November 9, 2015)
- allow spaces between `progid:` and rest part of value for IE's `filter` property as `autoprefixer` generates this kind of code (#249)
- fixes for Windows:
- correct processing new lines
- normalize file content in test suite
- fixes to work in strict mode (#252)
- init compressor dictionaries for every css block (#248, #251)
- bump uglify-js version
## 1.4.1 (October 20, 2015)
- allow merge for `display` property (#167, #244)
- more accurate `rect` (`clip` property value) merge
- fix typo when specifying options in cli (thanks to @Taritsyn)
- fix safe unit values merge with keyword values (#244)
- fix wrong descendant combinator removal (#246)
- build browser version on `prepublish` (thanks to @silentroach)
- parser: store whitespaces as single token (performance and reduce memory consumption)
- rearrange compress tests layout
## 1.4 (October 16, 2015)
Bringing project back to life. Changed files structure, cleaned up and refactored most of sources.
### Common
- single code base (no more `src` folder)
- build browser version with `browserify` (no more `make`, and `web` folder), browser version is available at `dist/csso-browser.js`
- main file is `lib/index.js` now
- minimal `node.js` version is `0.12` now
- restrict file list to publish on npm (no more useless folders and files in package)
- add `jscs` to control code style
- automate `gh-pages` update
- util functions reworked
- translator reworked
- test suite reworked
- compressor refactored
- initial parser refactoring
### API
- new method `minify(src, options)`, options:
- `restructuring` if set to `false`, disable structure optimisations (`true` by default)
- `debug` - outputs intermediate state of CSS during compression (`false` by default)
- deprecate `justDoIt()` method (use `minify` instead)
- rename `treeToString()` method to `stringify()`
- drop `printTree()` method
- AST node info
- `column` and `offset` added
- `ln` renamed to `line`
- fix line counting across multiple files and input with CR LF (#147)
### CLI
- completely reworked, use [clap](https://github.com/lahmatiy/clap) to parse argv
- add support for input from stdin (#128)
- drop undocumented and obsoleted options `--rule` and `--parser` (suppose nobody use it)
- drop `-off` alias for `--restructure-off` as incorrect (only one letter options should starts with single `-`)
- new option `--debug` that reflecting to `options.debug` for `minify`
### Parsing and optimizations
- keep all exclamation comments (#194)
- add `/deep/` combinator support (#209)
- attribute selector
- allow colon in attribute name (#237)
- support for namespaces (#233)
- color
- support all css/html colors
- convert `hsla` to `rgba` and `hls` to `rgb`
- convert `rgba` with 1 as alpha value to `rgb` (#122)
- interpolate `rgb` and `rgba` percentage values to absolute values
- replace percentage values in `rgba` for normalized/interpolated values
- lowercase hex colors and color names (#169)
- fix color minification when hex value replaced for color name (#176)
- fit rgb values to 0..255 range (#181)
- calc
- remove spaces for multiple operator in calc
- don't remove units inside calc (#222)
- fix wrong white space removal around `+` and `-` (#228)
- don't remove units in `flex` property as it could change value meaning (#200)
- don't merge `\9` hack values (#231)
- merge property values only if they have the same functions (#150, #227)
- don't merge property values with some sort of units (#140, #161)
- fix `!important` issue for `top-right-bottom-left` properties (#189)
- fix `top-right-bottom-left` properties merge (#139, #175)
- support for unicode-range (#148)
- don't crash on ruleset with no selector (#135)
- tolerant to class names that starts with digit (#99, #105)
- fix background compressing (#170)
## 1.3.12 (October 8, 2015)
- Case insensitive check for `!important` (#187)
- Fix problems with using `csso` as cli command on Windows (#83, #136, #142 and others)
- Remove byte order marker (the UTF-8 BOM) from input
- Don't strip space between funktion-funktion and funktion-vhash (#134)
- Don't merge TRBL values having \9 (hack for IE8 in bootstrap) (#159, #214, #230, #231 and others)
- Don't strip units off dimensions of non-length (#226, #229 and others)
## 1.3.7 (February 11, 2013)
- Gonzales 1.0.7.
## 1.3.6 (November 26, 2012)
- Gonzales 1.0.6.
## 1.3.5 (October 28, 2012)
- Gonzales 1.0.5.
- Protecting copyright notices in CSS: https://github.com/css/csso/issues/92
- Zero CSS throws an error: https://github.com/css/csso/issues/96
- Don't minify the second `0s` in Firefox for animations: https://github.com/css/csso/issues/100
- Japan manual
- BEM ready documentation
## 1.3.4 (October 10, 2012)
- @page inside @media Causes Error: https://github.com/css/csso/issues/90
## 1.3.3 (October 9, 2012)
- CSSO 1.3.2 compresses ".t-1" and ".t-01" as identical classes: https://github.com/css/csso/issues/88
## 1.3.2 (October 8, 2012)
- filter + important breaks CSSO v1.3.1: https://github.com/css/csso/issues/87
## 1.3.1 (October 8, 2012)
- "filter" IE property breaks CSSO v1.3.0: https://github.com/css/csso/issues/86
## 1.3.0 (October 4, 2012)
- PeCode CSS parser replaced by Gonzales CSS parser

2
node_modules/csso/LICENSE generated vendored
View File

@@ -1,4 +1,4 @@
Copyright (C) 2015-2019 by Roman Dvornov
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

146
node_modules/csso/README.md generated vendored
View File

@@ -1,15 +1,92 @@
[![NPM version](https://img.shields.io/npm/v/csso.svg)](https://www.npmjs.com/package/csso)
[![Build Status](https://travis-ci.org/css/csso.svg?branch=master)](https://travis-ci.org/css/csso)
[![Build Status](https://github.com/css/csso/actions/workflows/build.yml/badge.svg)](https://github.com/css/csso/actions/workflows/build.yml)
[![Coverage Status](https://coveralls.io/repos/github/css/csso/badge.svg?branch=master)](https://coveralls.io/github/css/csso?branch=master)
[![NPM Downloads](https://img.shields.io/npm/dm/csso.svg)](https://www.npmjs.com/package/csso)
[![Twitter](https://img.shields.io/badge/Twitter-@cssoptimizer-blue.svg)](https://twitter.com/cssoptimizer)
CSSO (CSS Optimizer) is a CSS minifier. It performs three sort of transformations: cleaning (removing redundant), compression (replacement for shorter form) and restructuring (merge of declarations, rulesets and so on). As a result your CSS becomes much smaller.
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.
[![Originated by Yandex](https://cdn.rawgit.com/css/csso/8d1b89211ac425909f735e7d5df87ee16c2feec6/docs/yandex.svg)](https://www.yandex.com/)
[![Sponsored by Avito](https://cdn.rawgit.com/css/csso/8d1b89211ac425909f735e7d5df87ee16c2feec6/docs/avito.svg)](https://www.avito.ru/)
## Install
## Ready to use
```
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
@@ -20,12 +97,9 @@ CSSO (CSS Optimizer) is a CSS minifier. It performs three sort of transformation
- [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)
## Install
```
npm install 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
@@ -42,37 +116,12 @@ npm install csso
<!-- /TOC -->
Basic usage:
```js
var csso = require('csso');
var minifiedCss = csso.minify('.test { color: #ff0000; }').css;
console.log(minifiedCss);
// .test{color:red}
```
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. You may minify CSS step by step:
```js
var csso = require('csso');
var ast = csso.syntax.parse('.test { color: #ff0000; }');
var compressedAst = csso.syntax.compress(ast).ast;
var minifiedCss = csso.syntax.generate(compressedAst);
console.log(minifiedCss);
// .test{color:red}
```
> Warning: CSSO uses early versions of CSSTree that still in active development. CSSO doesn't guarantee API behind `syntax` field or AST format will not change in future releases of CSSO, since it's subject to change in CSSTree. Be careful with CSSO updates if you use `syntax` API until this warning removal.
### minify(source[, options])
Minify `source` CSS passed as `String`.
```js
var result = csso.minify('.test { color: #ff0000; }', {
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)
@@ -131,7 +180,7 @@ Options:
The same as `minify()` but for list of declarations. Usually it's a `style` attribute value.
```js
var result = csso.minifyBlock('color: rgba(255, 0, 0, 1); color: #ff0000');
const result = csso.minifyBlock('color: rgba(255, 0, 0, 1); color: #ff0000');
console.log(result.css);
// > color:red
@@ -200,9 +249,9 @@ Options:
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
var csso = require('csso');
var css = fs.readFileSync('path/to/my.css', 'utf8');
var result = csso.minify(css, {
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
});
@@ -217,12 +266,13 @@ console.log(result.map.toString());
Example of generating source map with respect of source map from input CSS:
```js
var require('source-map');
var csso = require('csso');
var inputFile = 'path/to/my.css';
var input = fs.readFileSync(inputFile, 'utf8');
var inputMap = input.match(/\/\*# sourceMappingURL=(\S+)\s*\*\/\s*$/);
var output = csso.minify(input, {
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
});
@@ -239,7 +289,7 @@ if (inputMap) {
console.log(
output.css +
'/*# sourceMappingURL=data:application/json;base64,' +
new Buffer(output.map.toString()).toString('base64') +
Buffer.from(output.map.toString()).toString('base64') +
' */'
);
```

73
node_modules/csso/cjs/clean/Atrule.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/clean/Comment.cjs generated vendored Normal file
View File

@@ -0,0 +1,7 @@
'use strict';
function cleanComment(data, item, list) {
list.remove(item);
}
module.exports = cleanComment;

18
node_modules/csso/cjs/clean/Declaration.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/clean/Raw.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/clean/Rule.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/clean/TypeSelector.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/clean/WhiteSpace.cjs generated vendored Normal file
View File

@@ -0,0 +1,7 @@
'use strict';
function cleanWhitespace(node, item, list) {
list.remove(item);
}
module.exports = cleanWhitespace;

32
node_modules/csso/cjs/clean/index.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/clean/utils.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/compress.cjs generated vendored Executable file
View 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
node_modules/csso/cjs/index.cjs generated vendored Executable file
View 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
node_modules/csso/cjs/replace/Atrule.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/replace/AttributeSelector.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/replace/Dimension.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/replace/Number.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/replace/Percentage.cjs generated vendored Normal file
View 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: Dont 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
node_modules/csso/cjs/replace/Url.cjs generated vendored Normal file
View File

@@ -0,0 +1,8 @@
'use strict';
function Url(node) {
// convert `\\` to `/`
node.value = node.value.replace(/\\/g, '/');
}
module.exports = Url;

29
node_modules/csso/cjs/replace/Value.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/replace/atrule/keyframes.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/replace/color.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/replace/index.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/replace/property/background.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/replace/property/border.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/replace/property/font-weight.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/replace/property/font.cjs generated vendored Normal file
View 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
node_modules/csso/cjs/restructure/1-mergeAtrule.cjs generated vendored Executable file
View 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
node_modules/csso/cjs/restructure/2-initialMergeRuleset.cjs generated vendored Executable file
View 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
node_modules/csso/cjs/restructure/3-disjoinRuleset.cjs generated vendored Executable file
View 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
node_modules/csso/cjs/restructure/4-restructShorthand.cjs generated vendored Executable file
View 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
node_modules/csso/cjs/restructure/6-restructBlock.cjs generated vendored Executable file
View 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
node_modules/csso/cjs/restructure/7-mergeRuleset.cjs generated vendored Executable file
View 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
node_modules/csso/cjs/restructure/8-restructRuleset.cjs generated vendored Executable file
View 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
node_modules/csso/cjs/restructure/index.cjs generated vendored Executable file
View 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;

View 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
node_modules/csso/cjs/restructure/prepare/index.cjs generated vendored Normal file
View 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;

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

View 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 selectors specificity
// https://www.w3.org/TR/selectors-4/#specificity-rules
function specificity(simpleSelector) {
let A = 0;
let B = 0;
let C = 0;
// A selectors 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
node_modules/csso/cjs/restructure/utils.cjs generated vendored Executable file
View 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
node_modules/csso/cjs/syntax.cjs generated vendored Executable file
View 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
node_modules/csso/cjs/usage.cjs generated vendored Executable file
View 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
node_modules/csso/cjs/utils.cjs generated vendored Executable file
View 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
node_modules/csso/cjs/version.cjs generated vendored Executable file
View File

@@ -0,0 +1,5 @@
'use strict';
const { version } = require('../package.json');
exports.version = version;

16
node_modules/csso/dist/csso.esm.js generated vendored Normal file

File diff suppressed because one or more lines are too long

3338
node_modules/csso/dist/csso.js generated vendored

File diff suppressed because one or more lines are too long

1
node_modules/csso/dist/csso.min.js generated vendored

File diff suppressed because one or more lines are too long

1
node_modules/csso/dist/version.cjs generated vendored Normal file
View File

@@ -0,0 +1 @@
module.exports = "5.0.5";

1
node_modules/csso/dist/version.js generated vendored Normal file
View File

@@ -0,0 +1 @@
export const version = "5.0.5";

View File

@@ -1,7 +1,7 @@
var resolveKeyword = require('css-tree').keyword;
var { hasNoChildren } = require('./utils');
import { keyword as resolveKeyword } from 'css-tree';
import { hasNoChildren } from './utils.js';
module.exports = function cleanAtrule(node, item, list) {
export default function cleanAtrule(node, item, list) {
if (node.block) {
// otherwise removed at-rule don't prevent @import for removal
if (this.stylesheet !== null) {
@@ -46,13 +46,15 @@ module.exports = function cleanAtrule(node, item, list) {
this.root.firstAtrulesAllowed = false;
list.remove(item);
return true;
}, this);
break;
default:
var name = resolveKeyword(node.name).basename;
default: {
const name = resolveKeyword(node.name).basename;
if (name === 'keyframes' ||
name === 'media' ||
name === 'supports') {
@@ -62,5 +64,6 @@ module.exports = function cleanAtrule(node, item, list) {
list.remove(item);
}
}
}
}
};

View File

@@ -1,3 +1,3 @@
module.exports = function cleanComment(data, item, list) {
export default function cleanComment(data, item, list) {
list.remove(item);
};

View File

@@ -1,7 +1,7 @@
var property = require('css-tree').property;
import { property } from 'css-tree';
module.exports = function cleanDeclartion(node, item, list) {
if (node.value.children && node.value.children.isEmpty()) {
export default function cleanDeclartion(node, item, list) {
if (node.value.children && node.value.children.isEmpty) {
list.remove(item);
return;
}

4
node_modules/csso/lib/clean/Raw.js generated vendored
View File

@@ -1,6 +1,6 @@
var { isNodeChildrenList } = require('./utils');
import { isNodeChildrenList } from './utils.js';
module.exports = function cleanRaw(node, item, list) {
export default function cleanRaw(node, item, list) {
// raw in stylesheet or block children
if (isNodeChildrenList(this.stylesheet, list) ||
isNodeChildrenList(this.block, list)) {

27
node_modules/csso/lib/clean/Rule.js generated vendored
View File

@@ -1,10 +1,12 @@
var hasOwnProperty = Object.prototype.hasOwnProperty;
var walk = require('css-tree').walk;
var { hasNoChildren } = require('./utils');
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.each(function(selector, item, list) {
var shouldRemove = false;
selectorList.children.forEach((selector, item, list) => {
let shouldRemove = false;
walk(selector, function(node) {
// ignore nodes in nested selectors
@@ -71,19 +73,24 @@ function cleanUnused(selectorList, usageData) {
}
});
return selectorList.children.isEmpty();
return selectorList.children.isEmpty;
}
module.exports = function cleanRule(node, item, list, options) {
export default function cleanRule(node, item, list, options) {
if (hasNoChildren(node.prelude) || hasNoChildren(node.block)) {
list.remove(item);
return;
}
var usageData = options.usage;
// avoid usage filtering for some at-rules
if (this.atrule && skipUsageFilteringAtrule.has(keyword(this.atrule.name).basename)) {
return;
}
if (usageData && (usageData.whitelist !== null || usageData.blacklist !== null)) {
cleanUnused(node.prelude, usageData);
const { usage } = options;
if (usage && (usage.whitelist !== null || usage.blacklist !== null)) {
cleanUnused(node.prelude, usage);
if (hasNoChildren(node.prelude)) {
list.remove(item);

View File

@@ -1,6 +1,6 @@
// remove useless universal selector
module.exports = function cleanTypeSelector(node, item, list) {
var name = item.data.name;
export default function cleanTypeSelector(node, item, list) {
const name = item.data.name;
// check it's a non-namespaced universal selector
if (name !== '*') {
@@ -8,7 +8,7 @@ module.exports = function cleanTypeSelector(node, item, list) {
}
// remove when universal selector before other selectors
var nextType = item.next && item.next.data.type;
const nextType = item.next && item.next.data.type;
if (nextType === 'IdSelector' ||
nextType === 'ClassSelector' ||
nextType === 'AttributeSelector' ||

View File

@@ -1,30 +1,3 @@
var { isNodeChildrenList } = require('./utils');
function isSafeOperator(node) {
return node.type === 'Operator' && node.value !== '+' && node.value !== '-';
}
module.exports = function cleanWhitespace(node, item, list) {
// remove when first or last item in sequence
if (item.next === null || item.prev === null) {
list.remove(item);
return;
}
// white space in stylesheet or block children
if (isNodeChildrenList(this.stylesheet, list) ||
isNodeChildrenList(this.block, list)) {
list.remove(item);
return;
}
if (item.next.data.type === 'WhiteSpace') {
list.remove(item);
return;
}
if (isSafeOperator(item.prev.data) || isSafeOperator(item.next.data)) {
list.remove(item);
return;
}
export default function cleanWhitespace(node, item, list) {
list.remove(item);
};

30
node_modules/csso/lib/clean/index.js generated vendored
View File

@@ -1,17 +1,25 @@
var walk = require('css-tree').walk;
var handlers = {
Atrule: require('./Atrule'),
Comment: require('./Comment'),
Declaration: require('./Declaration'),
Raw: require('./Raw'),
Rule: require('./Rule'),
TypeSelector: require('./TypeSelector'),
WhiteSpace: require('./WhiteSpace')
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
};
module.exports = function(ast, options) {
export default function(ast, options) {
walk(ast, {
leave: function(node, item, list) {
leave(node, item, list) {
if (handlers.hasOwnProperty(node.type)) {
handlers[node.type].call(this, node, item, list, options);
}

15
node_modules/csso/lib/clean/utils.js generated vendored
View File

@@ -1,8 +1,7 @@
module.exports = {
hasNoChildren: function(node) {
return !node || !node.children || node.children.isEmpty();
},
isNodeChildrenList: function(node, list) {
return node !== null && node.children === list;
}
};
export function hasNoChildren(node) {
return !node || !node.children || node.children.isEmpty;
}
export function isNodeChildrenList(node, list) {
return node !== null && node.children === list;
}

69
node_modules/csso/lib/compress.js generated vendored
View File

@@ -1,17 +1,15 @@
var List = require('css-tree').List;
var clone = require('css-tree').clone;
var usageUtils = require('./usage');
var clean = require('./clean');
var replace = require('./replace');
var restructure = require('./restructure');
var walk = require('css-tree').walk;
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(children, specialComments) {
var buffer = new List();
var nonSpaceTokenInBuffer = false;
var protectedComment;
function readChunk(input, specialComments) {
const children = new List();
let nonSpaceTokenInBuffer = false;
let protectedComment;
children.nextUntil(children.head, function(node, item, list) {
input.nextUntil(input.head, (node, item, list) => {
if (node.type === 'Comment') {
if (!specialComments || node.value.charAt(0) !== '!') {
list.remove(item);
@@ -24,6 +22,7 @@ function readChunk(children, specialComments) {
list.remove(item);
protectedComment = node;
return;
}
@@ -31,7 +30,7 @@ function readChunk(children, specialComments) {
nonSpaceTokenInBuffer = true;
}
buffer.insert(list.remove(item));
children.insert(list.remove(item));
});
return {
@@ -39,15 +38,15 @@ function readChunk(children, specialComments) {
stylesheet: {
type: 'StyleSheet',
loc: null,
children: buffer
children
}
};
}
function compressChunk(ast, firstAtrulesAllowed, num, options) {
options.logger('Compress block #' + num, null, true);
options.logger(`Compress block #${num}`, null, true);
var seed = 1;
let seed = 1;
if (ast.type === 'StyleSheet') {
ast.firstAtrulesAllowed = firstAtrulesAllowed;
@@ -56,7 +55,7 @@ function compressChunk(ast, firstAtrulesAllowed, num, options) {
walk(ast, {
visit: 'Atrule',
enter: function markScopes(node) {
enter(node) {
if (node.block !== null) {
node.block.id = seed++;
}
@@ -81,7 +80,7 @@ function compressChunk(ast, firstAtrulesAllowed, num, options) {
}
function getCommentsOption(options) {
var comments = 'comments' in options ? options.comments : 'exclamation';
let comments = 'comments' in options ? options.comments : 'exclamation';
if (typeof comments === 'boolean') {
comments = comments ? 'exclamation' : false;
@@ -117,27 +116,27 @@ function wrapBlock(block) {
})
})
},
block: block
block
});
}
module.exports = function compress(ast, options) {
export default function compress(ast, options) {
ast = ast || { type: 'StyleSheet', loc: null, children: new List() };
options = options || {};
var compressOptions = {
const compressOptions = {
logger: typeof options.logger === 'function' ? options.logger : function() {},
restructuring: getRestructureOption(options),
forceMediaMerge: Boolean(options.forceMediaMerge),
usage: options.usage ? usageUtils.buildIndex(options.usage) : false
usage: options.usage ? buildIndex(options.usage) : false
};
var specialComments = getCommentsOption(options);
var firstAtrulesAllowed = true;
var input;
var output = new List();
var chunk;
var chunkNum = 1;
var chunkChildren;
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);
@@ -157,7 +156,7 @@ module.exports = function compress(ast, options) {
if (chunk.comment) {
// add \n before comment if there is another content in output
if (!output.isEmpty()) {
if (!output.isEmpty) {
output.insert(List.createItem({
type: 'Raw',
value: '\n'
@@ -167,7 +166,7 @@ module.exports = function compress(ast, options) {
output.insert(List.createItem(chunk.comment));
// add \n after comment if chunk is not empty
if (!chunkChildren.isEmpty()) {
if (!chunkChildren.isEmpty) {
output.insert(List.createItem({
type: 'Raw',
value: '\n'
@@ -175,8 +174,8 @@ module.exports = function compress(ast, options) {
}
}
if (firstAtrulesAllowed && !chunkChildren.isEmpty()) {
var lastRule = chunkChildren.last();
if (firstAtrulesAllowed && !chunkChildren.isEmpty) {
const lastRule = chunkChildren.last;
if (lastRule.type !== 'Atrule' ||
(lastRule.name !== 'import' && lastRule.name !== 'charset')) {
@@ -189,9 +188,9 @@ module.exports = function compress(ast, options) {
}
output.appendList(chunkChildren);
} while (!input.isEmpty());
} while (!input.isEmpty);
return {
ast: ast
ast
};
};

70
node_modules/csso/lib/index.js generated vendored
View File

@@ -1,35 +1,36 @@
var csstree = require('css-tree');
var parse = csstree.parse;
var compress = require('./compress');
var generate = csstree.generate;
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);
console.error(`## ${name} done in %d ms\n`, Date.now() - startTime);
}
return data;
}
function createDefaultLogger(level) {
var lastDebug;
let lastDebug;
return function logger(title, ast) {
var line = title;
let line = title;
if (ast) {
line = '[' + ((Date.now() - lastDebug) / 1000).toFixed(3) + 's] ' + line;
line = `[${((Date.now() - lastDebug) / 1000).toFixed(3)}s] ${line}`;
}
if (level > 1 && ast) {
var css = generate(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';
line += `\n ${css}\n`;
}
console.error(line);
@@ -37,18 +38,8 @@ function createDefaultLogger(level) {
};
}
function copy(obj) {
var result = {};
for (var key in obj) {
result[key] = obj[key];
}
return result;
}
function buildCompressOptions(options) {
options = copy(options);
options = { ...options };
if (typeof options.logger !== 'function' && options.debug) {
options.logger = createDefaultLogger(options.debug);
@@ -62,22 +53,20 @@ function runHandler(ast, options, handlers) {
handlers = [handlers];
}
handlers.forEach(function(fn) {
fn(ast, options);
});
handlers.forEach(fn => fn(ast, options));
}
function minify(context, source, options) {
options = options || {};
var filename = options.filename || '<unknown>';
var result;
const filename = options.filename || '<unknown>';
let result;
// parse
var ast = debugOutput('parsing', options, Date.now(),
const ast = debugOutput('parsing', options, Date.now(),
parse(source, {
context: context,
filename: filename,
context,
filename,
positions: Boolean(options.sourceMap)
})
);
@@ -90,7 +79,7 @@ function minify(context, source, options) {
}
// compress
var compressResult = debugOutput('compress', options, Date.now(),
const compressResult = debugOutput('compress', options, Date.now(),
compress(ast, buildCompressOptions(options))
);
@@ -103,12 +92,14 @@ function minify(context, source, options) {
// generate
if (options.sourceMap) {
result = debugOutput('generate(sourceMap: true)', options, Date.now(), (function() {
var tmp = generate(compressResult.ast, { sourceMap: true });
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),
@@ -127,15 +118,14 @@ function minifyBlock(source, options) {
return minify('declarationList', source, options);
}
module.exports = {
version: require('../package.json').version,
export {
version,
utils,
// main methods
minify: minifyStylesheet,
minifyBlock: minifyBlock,
minifyStylesheet as minify,
minifyBlock,
// css syntax parser/walkers/generator/etc
syntax: Object.assign({
compress: compress
}, csstree)
syntax
};

View File

@@ -1,7 +1,7 @@
var resolveKeyword = require('css-tree').keyword;
var compressKeyframes = require('./atrule/keyframes');
import { keyword as resolveKeyword } from 'css-tree';
import compressKeyframes from './atrule/keyframes.js';
module.exports = function(node) {
export default function(node) {
// compress @keyframe selectors
if (resolveKeyword(node.name).basename === 'keyframes') {
compressKeyframes(node);

View File

@@ -1,33 +1,28 @@
// Can unquote attribute detection
// Adopted implementation of Mathias Bynens
// https://github.com/mathiasbynens/mothereff.in/blob/master/unquoted-attributes/eff.js
var escapesRx = /\\([0-9A-Fa-f]{1,6})(\r\n|[ \t\n\f\r])?|\\./g;
var blockUnquoteRx = /^(-?\d|--)|[\u0000-\u002c\u002e\u002f\u003A-\u0040\u005B-\u005E\u0060\u007B-\u009f]/;
const blockUnquoteRx = /^(-?\d|--)|[\u0000-\u002c\u002e\u002f\u003A-\u0040\u005B-\u005E\u0060\u007B-\u009f]/;
function canUnquote(value) {
if (value === '' || value === '-') {
return;
return false;
}
// Escapes are valid, so replace them with a valid non-empty string
value = value.replace(escapesRx, 'a');
return !blockUnquoteRx.test(value);
}
module.exports = function(node) {
var attrValue = node.value;
export default function(node) {
const attrValue = node.value;
if (!attrValue || attrValue.type !== 'String') {
return;
}
var unquotedValue = attrValue.value.replace(/^(.)(.*)\1$/, '$2');
if (canUnquote(unquotedValue)) {
if (canUnquote(attrValue.value)) {
node.value = {
type: 'Identifier',
loc: attrValue.loc,
name: unquotedValue
name: attrValue.value
};
}
};

View File

@@ -1,43 +1,44 @@
var packNumber = require('./Number').pack;
var MATH_FUNCTIONS = {
'calc': true,
'min': true,
'max': true,
'clamp': true
};
var LENGTH_UNIT = {
import { packNumber } from './Number.js';
const MATH_FUNCTIONS = new Set([
'calc',
'min',
'max',
'clamp'
]);
const LENGTH_UNIT = new Set([
// absolute length units
'px': true,
'mm': true,
'cm': true,
'in': true,
'pt': true,
'pc': true,
'px',
'mm',
'cm',
'in',
'pt',
'pc',
// relative length units
'em': true,
'ex': true,
'ch': true,
'rem': true,
'em',
'ex',
'ch',
'rem',
// viewport-percentage lengths
'vh': true,
'vw': true,
'vmin': true,
'vmax': true,
'vm': true
};
'vh',
'vw',
'vmin',
'vmax',
'vm'
]);
module.exports = function compressDimension(node, item) {
var value = packNumber(node.value, item);
export default function compressDimension(node, item) {
const value = packNumber(node.value);
node.value = value;
if (value === '0' && this.declaration !== null && this.atrulePrelude === null) {
var unit = node.unit.toLowerCase();
const unit = node.unit.toLowerCase();
// only length values can be compressed
if (!LENGTH_UNIT.hasOwnProperty(unit)) {
if (!LENGTH_UNIT.has(unit)) {
return;
}
@@ -49,14 +50,14 @@ module.exports = function compressDimension(node, item) {
}
// issue #222: don't remove units inside calc
if (this.function && MATH_FUNCTIONS.hasOwnProperty(this.function.name)) {
if (this.function && MATH_FUNCTIONS.has(this.function.name)) {
return;
}
item.data = {
type: 'Number',
loc: node.loc,
value: value
value
};
}
};

View File

@@ -1,23 +1,23 @@
var OMIT_PLUSSIGN = /^(?:\+|(-))?0*(\d*)(?:\.0*|(\.\d*?)0*)?$/;
var KEEP_PLUSSIGN = /^([\+\-])?0*(\d*)(?:\.0*|(\.\d*?)0*)?$/;
var unsafeToRemovePlusSignAfter = {
Dimension: true,
Hash: true,
Identifier: true,
Number: true,
Raw: true,
UnicodeRange: true
};
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) {
export function packNumber(value, item) {
// omit plus sign only if no prev or prev is safe type
var regexp = item && item.prev !== null && unsafeToRemovePlusSignAfter.hasOwnProperty(item.prev.data.type)
const regexp = item && item.prev !== null && unsafeToRemovePlusSignAfter.has(item.prev.data.type)
? KEEP_PLUSSIGN
: OMIT_PLUSSIGN;
// 100 -> '100'
// 00100 -> '100'
// +100 -> '100' (only when safe, e.g. omitting plus sign for 1px+1px leads to single dimension instead of two)
// +100 -> '100'
// -100 -> '-100'
// 0.123 -> '.123'
// 0.12300 -> '.123'
@@ -29,11 +29,12 @@ function packNumber(value, item) {
if (value === '' || value === '-') {
value = '0';
}
// FIXME: is it solution simplier?
// value = String(Number(value)).replace(/^(-?)0+\./, '$1.');
return value;
}
module.exports = function(node, item) {
node.value = packNumber(node.value, item);
export function Number(node) {
node.value = packNumber(node.value);
};
module.exports.pack = packNumber;

View File

@@ -1,6 +1,7 @@
var lexer = require('css-tree').lexer;
var packNumber = require('./Number').pack;
var blacklist = new Set([
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',
@@ -16,8 +17,8 @@ var blacklist = new Set([
'-ms-flex'
]);
module.exports = function compressPercentage(node, item) {
node.value = packNumber(node.value, item);
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

View File

@@ -1,12 +0,0 @@
module.exports = function(node) {
var value = node.value;
// remove escaped newlines, i.e.
// .a { content: "foo\
// bar"}
// ->
// .a { content: "foobar" }
value = value.replace(/\\(\r\n|\r|\n|\f)/g, '');
node.value = value;
};

33
node_modules/csso/lib/replace/Url.js generated vendored
View File

@@ -1,33 +1,4 @@
var UNICODE = '\\\\[0-9a-f]{1,6}(\\r\\n|[ \\n\\r\\t\\f])?';
var ESCAPE = '(' + UNICODE + '|\\\\[^\\n\\r\\f0-9a-fA-F])';
var NONPRINTABLE = '\u0000\u0008\u000b\u000e-\u001f\u007f';
var SAFE_URL = new RegExp('^(' + ESCAPE + '|[^\"\'\\(\\)\\\\\\s' + NONPRINTABLE + '])*$', 'i');
module.exports = function(node) {
var value = node.value;
if (value.type !== 'String') {
return;
}
var quote = value.value[0];
var url = value.value.substr(1, value.value.length - 2);
export default function(node) {
// convert `\\` to `/`
url = url.replace(/\\\\/g, '/');
// remove quotes when safe
// https://www.w3.org/TR/css-syntax-3/#url-unquoted-diagram
if (SAFE_URL.test(url)) {
node.value = {
type: 'Raw',
loc: node.value.loc,
value: url
};
} else {
// use double quotes if string has no double quotes
// otherwise use original quotes
// TODO: make better quote type selection
node.value.value = url.indexOf('"') === -1 ? '"' + url + '"' : quote + url + quote;
}
node.value = node.value.replace(/\\/g, '/');
};

View File

@@ -1,18 +1,24 @@
var resolveName = require('css-tree').property;
var handlers = {
'font': require('./property/font'),
'font-weight': require('./property/font-weight'),
'background': require('./property/background'),
'border': require('./property/border'),
'outline': require('./property/border')
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
};
module.exports = function compressValue(node) {
export default function compressValue(node) {
if (!this.declaration) {
return;
}
var property = resolveName(this.declaration.property);
const property = resolveName(this.declaration.property);
if (handlers.hasOwnProperty(property.basename)) {
handlers[property.basename](node);

View File

@@ -1,7 +1,7 @@
module.exports = function(node) {
node.block.children.each(function(rule) {
rule.prelude.children.each(function(simpleselector) {
simpleselector.children.each(function(data, item) {
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',

View File

@@ -1,8 +1,8 @@
var lexer = require('css-tree').lexer;
var packNumber = require('./Number').pack;
import { lexer } from 'css-tree';
import { packNumber } from './Number.js';
// http://www.w3.org/TR/css3-color/#svg-color
var NAME_TO_HEX = {
const NAME_TO_HEX = {
'aliceblue': 'f0f8ff',
'antiquewhite': 'faebd7',
'aqua': '0ff',
@@ -153,7 +153,7 @@ var NAME_TO_HEX = {
'yellowgreen': '9acd32'
};
var HEX_TO_NAME = {
const HEX_TO_NAME = {
'800000': 'maroon',
'800080': 'purple',
'808000': 'olive',
@@ -214,15 +214,15 @@ function hueToRgb(p, q, t) {
}
function hslToRgb(h, s, l, a) {
var r;
var g;
var b;
let r;
let g;
let b;
if (s === 0) {
r = g = b = l; // achromatic
} else {
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
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);
@@ -239,17 +239,17 @@ function hslToRgb(h, s, l, a) {
function toHex(value) {
value = value.toString(16);
return value.length === 1 ? '0' + value : value;
}
function parseFunctionArgs(functionArgs, count, rgb) {
var cursor = functionArgs.head;
var args = [];
var wasValue = false;
let cursor = functionArgs.head;
let args = [];
let wasValue = false;
while (cursor !== null) {
var node = cursor.data;
var type = node.type;
const { type, value } = cursor.data;
switch (type) {
case 'Number':
@@ -260,20 +260,22 @@ function parseFunctionArgs(functionArgs, count, rgb) {
wasValue = true;
args.push({
type: type,
value: Number(node.value)
type,
value: Number(value)
});
break;
case 'Operator':
if (node.value === ',') {
if (value === ',') {
if (!wasValue) {
return;
}
wasValue = false;
} else if (wasValue || node.value !== '+') {
} else if (wasValue || value !== '+') {
return;
}
break;
default:
@@ -319,7 +321,7 @@ function parseFunctionArgs(functionArgs, count, rgb) {
}
return args.map(function(arg) {
var value = Math.max(0, arg.value);
let value = Math.max(0, arg.value);
switch (arg.type) {
case 'Number':
@@ -351,9 +353,9 @@ function parseFunctionArgs(functionArgs, count, rgb) {
});
}
function compressFunction(node, item, list) {
var functionName = node.name;
var args;
export function compressFunction(node, item) {
let functionName = node.name;
let args;
if (functionName === 'rgba' || functionName === 'hsla') {
args = parseFunctionArgs(node.children, 4, functionName === 'rgba');
@@ -364,7 +366,7 @@ function compressFunction(node, item, list) {
}
if (functionName === 'hsla') {
args = hslToRgb.apply(null, args);
args = hslToRgb(...args);
node.name = 'rgba';
}
@@ -373,7 +375,8 @@ function compressFunction(node, item, list) {
// 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
var scopeFunctionName = this.function && this.function.name;
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)) {
@@ -389,7 +392,7 @@ function compressFunction(node, item, list) {
if (args[3] !== 1) {
// replace argument values for normalized/interpolated
node.children.each(function(node, item, list) {
node.children.forEach((node, item, list) => {
if (node.type === 'Operator') {
if (node.value !== ',') {
list.remove(item);
@@ -400,7 +403,7 @@ function compressFunction(node, item, list) {
item.data = {
type: 'Number',
loc: node.loc,
value: packNumber(args.shift(), null)
value: packNumber(args.shift())
};
});
@@ -420,7 +423,7 @@ function compressFunction(node, item, list) {
}
// convert to rgb
args = hslToRgb.apply(null, args);
args = hslToRgb(...args);
functionName = 'rgb';
}
@@ -432,15 +435,6 @@ function compressFunction(node, item, list) {
return;
}
// check if color is not at the end and not followed by space
var next = item.next;
if (next && next.data.type !== 'WhiteSpace') {
list.insert(list.createItem({
type: 'WhiteSpace',
value: ' '
}), next);
}
item.data = {
type: 'Hash',
loc: node.loc,
@@ -451,16 +445,16 @@ function compressFunction(node, item, list) {
}
}
function compressIdent(node, item) {
export function compressIdent(node, item) {
if (this.declaration === null) {
return;
}
var color = node.name.toLowerCase();
let color = node.name.toLowerCase();
if (NAME_TO_HEX.hasOwnProperty(color) &&
lexer.matchDeclaration(this.declaration).isType(node, 'color')) {
var hex = NAME_TO_HEX[color];
const hex = NAME_TO_HEX[color];
if (hex.length + 1 <= color.length) {
// replace for shorter hex value
@@ -481,8 +475,8 @@ function compressIdent(node, item) {
}
}
function compressHex(node, item) {
var color = node.value.toLowerCase();
export function compressHex(node, item) {
let color = node.value.toLowerCase();
// #112233 -> #123
if (color.length === 6 &&
@@ -502,9 +496,3 @@ function compressHex(node, item) {
node.value = color;
}
}
module.exports = {
compressFunction: compressFunction,
compressIdent: compressIdent,
compressHex: compressHex
};

View File

@@ -1,21 +1,29 @@
var walk = require('css-tree').walk;
var handlers = {
Atrule: require('./Atrule'),
AttributeSelector: require('./AttributeSelector'),
Value: require('./Value'),
Dimension: require('./Dimension'),
Percentage: require('./Percentage'),
Number: require('./Number'),
String: require('./String'),
Url: require('./Url'),
Hash: require('./color').compressHex,
Identifier: require('./color').compressIdent,
Function: require('./color').compressFunction
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
};
module.exports = function(ast) {
export default function(ast) {
walk(ast, {
leave: function(node, item, list) {
leave(node, item, list) {
if (handlers.hasOwnProperty(node.type)) {
handlers[node.type].call(this, node, item, list);
}

View File

@@ -1,17 +1,7 @@
var List = require('css-tree').List;
module.exports = function compressBackground(node) {
function lastType() {
if (buffer.length) {
return buffer[buffer.length - 1].type;
}
}
import { List } from 'css-tree';
export default function compressBackground(node) {
function flush() {
if (lastType() === 'WhiteSpace') {
buffer.pop();
}
if (!buffer.length) {
buffer.unshift(
{
@@ -19,10 +9,6 @@ module.exports = function compressBackground(node) {
loc: null,
value: '0'
},
{
type: 'WhiteSpace',
value: ' '
},
{
type: 'Number',
loc: null,
@@ -36,10 +22,10 @@ module.exports = function compressBackground(node) {
buffer = [];
}
var newValue = [];
var buffer = [];
let newValue = [];
let buffer = [];
node.children.each(function(node) {
node.children.forEach((node) => {
if (node.type === 'Operator' && node.value === ',') {
flush();
newValue.push(node);
@@ -56,11 +42,6 @@ module.exports = function compressBackground(node) {
}
}
// don't add redundant spaces
if (node.type === 'WhiteSpace' && (!buffer.length || lastType() === 'WhiteSpace')) {
return;
}
buffer.push(node);
});

View File

@@ -1,20 +1,5 @@
function removeItemAndRedundantWhiteSpace(list, item) {
var prev = item.prev;
var next = item.next;
if (next !== null) {
if (next.data.type === 'WhiteSpace' && (prev === null || prev.data.type === 'WhiteSpace')) {
list.remove(next);
}
} else if (prev !== null && prev.data.type === 'WhiteSpace') {
list.remove(prev);
}
list.remove(item);
}
module.exports = function compressBorder(node) {
node.children.each(function(node, item, list) {
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
@@ -24,7 +9,7 @@ module.exports = function compressBorder(node) {
value: '0'
};
} else {
removeItemAndRedundantWhiteSpace(list, item);
list.remove(item);
}
}
});

View File

@@ -1,5 +1,5 @@
module.exports = function compressFontWeight(node) {
var value = node.children.head.data;
export default function compressFontWeight(node) {
const value = node.children.head.data;
if (value.type === 'Identifier') {
switch (value.name) {

View File

@@ -1,7 +1,7 @@
module.exports = function compressFont(node) {
var list = node.children;
export default function compressFont(node) {
const list = node.children;
list.eachRight(function(node, item) {
list.forEachRight(function(node, item) {
if (node.type === 'Identifier') {
if (node.name === 'bold') {
item.data = {
@@ -10,33 +10,18 @@ module.exports = function compressFont(node) {
value: '700'
};
} else if (node.name === 'normal') {
var prev = item.prev;
const prev = item.prev;
if (prev && prev.data.type === 'Operator' && prev.data.value === '/') {
this.remove(prev);
}
this.remove(item);
} else if (node.name === 'medium') {
var next = item.next;
if (!next || next.data.type !== 'Operator') {
this.remove(item);
}
}
}
});
// remove redundant spaces
list.each(function(node, item) {
if (node.type === 'WhiteSpace') {
if (!item.prev || !item.next || item.next.data.type === 'WhiteSpace') {
this.remove(item);
}
}
});
if (list.isEmpty()) {
if (list.isEmpty) {
list.insert(list.createItem({
type: 'Identifier',
name: 'normal'

View File

@@ -1,12 +1,11 @@
var List = require('css-tree').List;
var resolveKeyword = require('css-tree').keyword;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var walk = require('css-tree').walk;
import { List, walk, keyword as resolveKeyword } from 'css-tree';
const { hasOwnProperty } = Object.prototype;
function addRuleToMap(map, item, list, single) {
var node = item.data;
var name = resolveKeyword(node.name).basename;
var id = node.name.toLowerCase() + '/' + (node.prelude ? node.prelude.id : null);
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);
@@ -24,12 +23,12 @@ function addRuleToMap(map, item, list, single) {
}
function relocateAtrules(ast, options) {
var collected = Object.create(null);
var topInjectPoint = null;
const collected = Object.create(null);
let topInjectPoint = null;
ast.children.each(function(node, item, list) {
ast.children.forEach(function(node, item, list) {
if (node.type === 'Atrule') {
var name = resolveKeyword(node.name).basename;
const name = resolveKeyword(node.name).basename;
switch (name) {
case 'keyframes':
@@ -56,8 +55,8 @@ function relocateAtrules(ast, options) {
}
});
for (var atrule in collected) {
for (var id in collected[atrule]) {
for (const atrule in collected) {
for (const id in collected[atrule]) {
ast.children.insertList(
collected[atrule][id],
atrule === 'media' ? null : topInjectPoint
@@ -75,7 +74,7 @@ function processAtrule(node, item, list) {
return;
}
var prev = item.prev && item.prev.data;
const prev = item.prev && item.prev.data;
if (!prev || !isMediaRule(prev)) {
return;
@@ -96,7 +95,7 @@ function processAtrule(node, item, list) {
}
}
module.exports = function rejoinAtrule(ast, options) {
export default function rejoinAtrule(ast, options) {
relocateAtrules(ast, options);
walk(ast, {

View File

@@ -1,45 +1,51 @@
var walk = require('css-tree').walk;
var utils = require('./utils');
import { walk } from 'css-tree';
import {
unsafeToSkipNode,
isEqualSelectors,
isEqualDeclarations,
addSelectors,
hasSimilarSelectors
} from './utils.js';
function processRule(node, item, list) {
var selectors = node.prelude.children;
var declarations = node.block.children;
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);
return unsafeToSkipNode.call(selectors, prev);
}
var prevSelectors = prev.prelude.children;
var prevDeclarations = prev.block.children;
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)) {
if (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);
if (isEqualDeclarations(declarations, prevDeclarations)) {
addSelectors(prevSelectors, selectors);
list.remove(item);
return true;
}
}
// go to prev ruleset if has no selector similarities
return utils.hasSimilarSelectors(selectors, prevSelectors);
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
module.exports = function initialMergeRule(ast) {
export default function initialMergeRule(ast) {
walk(ast, {
visit: 'Rule',
enter: processRule

View File

@@ -1,8 +1,7 @@
var List = require('css-tree').List;
var walk = require('css-tree').walk;
import { List, walk } from 'css-tree';
function processRule(node, item, list) {
var selectors = node.prelude.children;
const selectors = node.prelude.children;
// generate new rule sets:
// .a, .b { color: red; }
@@ -12,7 +11,8 @@ function processRule(node, item, list) {
// while there are more than 1 simple selector split for rulesets
while (selectors.head !== selectors.tail) {
var newSelectors = new List();
const newSelectors = new List();
newSelectors.insert(selectors.remove(selectors.head));
list.insert(list.createItem({
@@ -33,7 +33,7 @@ function processRule(node, item, list) {
}
}
module.exports = function disjoinRule(ast) {
export default function disjoinRule(ast) {
walk(ast, {
visit: 'Rule',
reverse: true,

View File

@@ -1,15 +1,13 @@
var List = require('css-tree').List;
var generate = require('css-tree').generate;
var walk = require('css-tree').walk;
import { List, generate, walk } from 'css-tree';
var REPLACE = 1;
var REMOVE = 2;
var TOP = 0;
var RIGHT = 1;
var BOTTOM = 2;
var LEFT = 3;
var SIDES = ['top', 'right', 'bottom', 'left'];
var SIDE = {
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',
@@ -33,7 +31,7 @@ var SIDE = {
'border-bottom-style': 'bottom',
'border-left-style': 'left'
};
var MAIN_PROPERTY = {
const MAIN_PROPERTY = {
'margin': 'margin',
'margin-top': 'margin',
'margin-right': 'margin',
@@ -63,289 +61,284 @@ var MAIN_PROPERTY = {
'border-left-style': 'border-style'
};
function TRBL(name) {
this.name = name;
this.loc = null;
this.iehack = undefined;
this.sides = {
'top': null,
'right': null,
'bottom': null,
'left': null
};
}
class TRBL {
constructor(name) {
this.name = name;
this.loc = null;
this.iehack = undefined;
this.sides = {
'top': null,
'right': null,
'bottom': null,
'left': null
};
}
TRBL.prototype.getValueSequence = function(declaration, count) {
var values = [];
var iehack = '';
var hasBadValues = declaration.value.type !== 'Value' || declaration.value.children.some(function(child) {
var special = false;
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;
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 'inherit':
case 'initial':
case 'unset':
case 'revert':
special = child.name;
break;
}
break;
case 'Dimension':
switch (child.unit) {
// is not supported until IE11
case 'rem':
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;
// 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 'Hash': // color
case 'Number':
case 'Percentage':
break;
case 'Function':
if (child.name === 'var') {
return true;
}
case 'Function':
if (child.name === 'var') {
return true;
}
special = child.name;
break;
special = child.name;
break;
case 'WhiteSpace':
return false; // ignore space
default:
return true; // bad value
}
default:
return true; // bad value
values.push({
node: child,
special,
important: declaration.important
});
});
if (hasBadValues || values.length > count) {
return false;
}
values.push({
node: child,
special: special,
important: declaration.important
});
});
if (typeof this.iehack === 'string' && this.iehack !== iehack) {
return false;
}
if (hasBadValues || values.length > count) {
return false;
this.iehack = iehack; // move outside
return values;
}
if (typeof this.iehack === 'string' && this.iehack !== iehack) {
return false;
canOverride(side, value) {
const currentValue = this.sides[side];
return !currentValue || (value.important && !currentValue.important);
}
this.iehack = iehack; // move outside
add(name, declaration) {
function attemptToAdd() {
const sides = this.sides;
const side = SIDE[name];
return values;
};
TRBL.prototype.canOverride = function(side, value) {
var currentValue = this.sides[side];
return !currentValue || (value.important && !currentValue.important);
};
TRBL.prototype.add = function(name, declaration) {
function attemptToAdd() {
var sides = this.sides;
var side = SIDE[name];
if (side) {
if (side in sides === false) {
return false;
}
var values = this.getValueSequence(declaration, 1);
if (!values || !values.length) {
return false;
}
// can mix only if specials are equal
for (var key in sides) {
if (sides[key] !== null && sides[key].special !== values[0].special) {
if (side) {
if (side in sides === false) {
return false;
}
}
if (!this.canOverride(side, values[0])) {
return true;
}
const values = this.getValueSequence(declaration, 1);
sides[side] = values[0];
return true;
} else if (name === this.name) {
var values = this.getValueSequence(declaration, 4);
if (!values || !values.length) {
return false;
}
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 (var i = 0; i < 4; i++) {
for (var key in sides) {
if (sides[key] !== null && sides[key].special !== values[i].special) {
// can mix only if specials are equal
for (const key in sides) {
if (sides[key] !== null && sides[key].special !== values[0].special) {
return false;
}
}
}
for (var i = 0; i < 4; i++) {
if (this.canOverride(SIDES[i], values[i])) {
sides[SIDES[i]] = values[i];
if (!this.canOverride(side, values[0])) {
return true;
}
}
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;
}
if (!attemptToAdd.call(this)) {
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;
}
// 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;
}
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)
];
return true;
};
TRBL.prototype.isOkToMinimize = function() {
var top = this.sides.top;
var right = this.sides.right;
var bottom = this.sides.bottom;
var left = this.sides.left;
if (top && right && bottom && left) {
var important =
top.important +
right.important +
bottom.important +
left.important;
return important === 0 || important === 4;
}
return false;
};
TRBL.prototype.getValue = function() {
var result = new List();
var sides = this.sides;
var values = [
sides.top,
sides.right,
sides.bottom,
sides.left
];
var 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]) {
if (stringValues[LEFT] === stringValues[RIGHT]) {
values.pop();
if (stringValues[RIGHT] === stringValues[TOP]) {
if (stringValues[BOTTOM] === stringValues[TOP]) {
values.pop();
if (stringValues[RIGHT] === stringValues[TOP]) {
values.pop();
}
}
}
}
for (var i = 0; i < values.length; i++) {
if (i) {
result.appendData({ type: 'WhiteSpace', value: ' ' });
for (let i = 0; i < values.length; i++) {
result.appendData(values[i].node);
}
result.appendData(values[i].node);
}
if (this.iehack) {
result.appendData({
type: 'Identifier',
loc: null,
name: this.iehack
});
}
if (this.iehack) {
result.appendData({ type: 'WhiteSpace', value: ' ' });
result.appendData({
type: 'Identifier',
return {
type: 'Value',
loc: null,
name: this.iehack
});
children: result
};
}
return {
type: 'Value',
loc: null,
children: result
};
};
TRBL.prototype.getDeclaration = function() {
return {
type: 'Declaration',
loc: this.loc,
important: this.sides.top.important,
property: this.name,
value: this.getValue()
};
};
getDeclaration() {
return {
type: 'Declaration',
loc: this.loc,
important: this.sides.top.important,
property: this.name,
value: this.getValue()
};
}
}
function processRule(rule, shorts, shortDeclarations, lastShortSelector) {
var declarations = rule.block.children;
var selector = rule.prelude.children.first().id;
const declarations = rule.block.children;
const selector = rule.prelude.children.first.id;
rule.block.children.eachRight(function(declaration, item) {
var property = declaration.property;
rule.block.children.forEachRight(function(declaration, item) {
const property = declaration.property;
if (!MAIN_PROPERTY.hasOwnProperty(property)) {
return;
}
var key = MAIN_PROPERTY[property];
var shorthand;
var operation;
const key = MAIN_PROPERTY[property];
let shorthand;
let operation;
if (!lastShortSelector || selector === lastShortSelector) {
if (key in shorts) {
@@ -367,10 +360,10 @@ function processRule(rule, shorts, shortDeclarations, lastShortSelector) {
shorts[key] = shorthand;
shortDeclarations.push({
operation: operation,
operation,
block: declarations,
item: item,
shorthand: shorthand
item,
shorthand
});
lastShortSelector = selector;
@@ -381,7 +374,7 @@ function processRule(rule, shorts, shortDeclarations, lastShortSelector) {
function processShorthands(shortDeclarations, markDeclaration) {
shortDeclarations.forEach(function(item) {
var shorthand = item.shorthand;
const shorthand = item.shorthand;
if (!shorthand.isOkToMinimize()) {
return;
@@ -395,18 +388,18 @@ function processShorthands(shortDeclarations, markDeclaration) {
});
}
module.exports = function restructBlock(ast, indexer) {
var stylesheetMap = {};
var shortDeclarations = [];
export default function restructBlock(ast, indexer) {
const stylesheetMap = {};
const shortDeclarations = [];
walk(ast, {
visit: 'Rule',
reverse: true,
enter: function(node) {
var stylesheet = this.block || this.stylesheet;
var ruleId = (node.pseudoSignature || '') + '|' + node.prelude.children.first().id;
var ruleMap;
var shorts;
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 = {

View File

@@ -1,20 +1,23 @@
var resolveProperty = require('css-tree').property;
var resolveKeyword = require('css-tree').keyword;
var walk = require('css-tree').walk;
var generate = require('css-tree').generate;
var fingerprintId = 1;
var dontRestructure = {
'src': 1 // https://github.com/afelix/csso/issues/50
};
import {
walk,
generate,
property as resolveProperty,
keyword as resolveKeyword
} from 'css-tree';
var DONT_MIX_VALUE = {
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
};
var SAFE_VALUES = {
const SAFE_VALUES = {
cursor: [
'auto', 'crosshair', 'default', 'move', 'text', 'wait', 'help',
'n-resize', 'e-resize', 's-resize', 'w-resize',
@@ -30,7 +33,7 @@ var SAFE_VALUES = {
]
};
var NEEDLESS_TABLE = {
const NEEDLESS_TABLE = {
'border-width': ['border'],
'border-style': ['border'],
'border-color': ['border'],
@@ -69,37 +72,37 @@ var NEEDLESS_TABLE = {
};
function getPropertyFingerprint(propertyName, declaration, fingerprints) {
var realName = resolveProperty(propertyName).basename;
const realName = resolveProperty(propertyName).basename;
if (realName === 'background') {
return propertyName + ':' + generate(declaration.value);
}
var declarationId = declaration.id;
var fingerprint = fingerprints[declarationId];
const declarationId = declaration.id;
let fingerprint = fingerprints[declarationId];
if (!fingerprint) {
switch (declaration.value.type) {
case 'Value':
var vendorId = '';
var iehack = '';
var special = {};
var raw = false;
const special = {};
let vendorId = '';
let iehack = '';
let raw = false;
declaration.value.children.each(function walk(node) {
declaration.value.children.forEach(function walk(node) {
switch (node.type) {
case 'Value':
case 'Brackets':
case 'Parentheses':
node.children.each(walk);
node.children.forEach(walk);
break;
case 'Raw':
raw = true;
break;
case 'Identifier':
var name = node.name;
case 'Identifier': {
const { name } = node;
if (!vendorId) {
vendorId = resolveKeyword(name).vendor;
@@ -120,9 +123,10 @@ function getPropertyFingerprint(propertyName, declaration, fingerprints) {
}
break;
}
case 'Function':
var name = node.name;
case 'Function': {
let { name } = node;
if (!vendorId) {
vendorId = resolveKeyword(name).vendor;
@@ -133,9 +137,10 @@ function getPropertyFingerprint(propertyName, declaration, fingerprints) {
// rect(<top>, <right>, <bottom>, <left>) - standart
// rect(<top> <right> <bottom> <left>) backwards compatible syntax
// only the same form values can be merged
var hasComma = node.children.some(function(node) {
return node.type === 'Operator' && node.value === ',';
});
const hasComma = node.children.some((node) =>
node.type === 'Operator' && node.value === ','
);
if (!hasComma) {
name = 'rect-backward';
}
@@ -144,12 +149,13 @@ function getPropertyFingerprint(propertyName, declaration, fingerprints) {
special[name + '()'] = true;
// check nested tokens too
node.children.each(walk);
node.children.forEach(walk);
break;
}
case 'Dimension':
var unit = node.unit;
case 'Dimension': {
const { unit } = node;
if (/\\[09]/.test(unit)) {
iehack = RegExp.lastMatch;
@@ -169,7 +175,9 @@ function getPropertyFingerprint(propertyName, declaration, fingerprints) {
special[unit] = true;
break;
}
break;
}
}
});
@@ -193,14 +201,14 @@ function getPropertyFingerprint(propertyName, declaration, fingerprints) {
}
function needless(props, declaration, fingerprints) {
var property = resolveProperty(declaration.property);
const property = resolveProperty(declaration.property);
if (NEEDLESS_TABLE.hasOwnProperty(property.basename)) {
var table = NEEDLESS_TABLE[property.basename];
const table = NEEDLESS_TABLE[property.basename];
for (var i = 0; i < table.length; i++) {
var ppre = getPropertyFingerprint(property.prefix + table[i], declaration, fingerprints);
var prev = props.hasOwnProperty(ppre) ? props[ppre] : null;
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;
@@ -210,14 +218,14 @@ function needless(props, declaration, fingerprints) {
}
function processRule(rule, item, list, props, fingerprints) {
var declarations = rule.block.children;
const declarations = rule.block.children;
declarations.eachRight(function(declaration, declarationItem) {
var property = declaration.property;
var fingerprint = getPropertyFingerprint(property, declaration, fingerprints);
var prev = props[fingerprint];
declarations.forEachRight(function(declaration, declarationItem) {
const { property } = declaration;
const fingerprint = getPropertyFingerprint(property, declaration, fingerprints);
const prev = props[fingerprint];
if (prev && !dontRestructure.hasOwnProperty(property)) {
if (prev && !dontRestructure.has(property)) {
if (declaration.important && !prev.item.data.important) {
props[fingerprint] = {
block: declarations,
@@ -241,7 +249,7 @@ function processRule(rule, item, list, props, fingerprints) {
// };
}
} else {
var prev = needless(props, declaration, fingerprints);
const prev = needless(props, declaration, fingerprints);
if (prev) {
declarations.remove(declarationItem);
@@ -262,23 +270,23 @@ function processRule(rule, item, list, props, fingerprints) {
}
});
if (declarations.isEmpty()) {
if (declarations.isEmpty) {
list.remove(item);
}
}
module.exports = function restructBlock(ast) {
var stylesheetMap = {};
var fingerprints = Object.create(null);
export default function restructBlock(ast) {
const stylesheetMap = {};
const fingerprints = Object.create(null);
walk(ast, {
visit: 'Rule',
reverse: true,
enter: function(node, item, list) {
var stylesheet = this.block || this.stylesheet;
var ruleId = (node.pseudoSignature || '') + '|' + node.prelude.children.first().id;
var ruleMap;
var props;
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 = {};

View File

@@ -1,5 +1,5 @@
var walk = require('css-tree').walk;
var utils = require('./utils');
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
@@ -14,24 +14,24 @@ var utils = require('./utils');
*/
function processRule(node, item, list) {
var selectors = node.prelude.children;
var declarations = node.block.children;
var nodeCompareMarker = selectors.first().compareMarker;
var skippedCompareMarkers = {};
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);
return unsafeToSkipNode.call(selectors, next);
}
if (node.pseudoSignature !== next.pseudoSignature) {
return true;
}
var nextFirstSelector = next.prelude.children.head;
var nextDeclarations = next.block.children;
var nextCompareMarker = nextFirstSelector.data.compareMarker;
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) {
@@ -40,7 +40,7 @@ function processRule(node, item, list) {
// try to join by selectors
if (selectors.head === selectors.tail) {
if (selectors.first().id === nextFirstSelector.data.id) {
if (selectors.first.id === nextFirstSelector.data.id) {
declarations.appendList(nextDeclarations);
list.remove(nextItem);
return;
@@ -48,11 +48,11 @@ function processRule(node, item, list) {
}
// try to join by properties
if (utils.isEqualDeclarations(declarations, nextDeclarations)) {
var nextStr = nextFirstSelector.data.id;
if (isEqualDeclarations(declarations, nextDeclarations)) {
const nextStr = nextFirstSelector.data.id;
selectors.some(function(data, item) {
var curStr = data.id;
selectors.some((data, item) => {
const curStr = data.id;
if (nextStr < curStr) {
selectors.insert(nextFirstSelector, item);
@@ -78,7 +78,7 @@ function processRule(node, item, list) {
});
}
module.exports = function mergeRule(ast) {
export default function mergeRule(ast) {
walk(ast, {
visit: 'Rule',
enter: processRule

View File

@@ -1,22 +1,20 @@
var List = require('css-tree').List;
var walk = require('css-tree').walk;
var utils = require('./utils');
import { List, walk } from 'css-tree';
import {
unsafeToSkipNode,
isEqualSelectors,
compareDeclarations,
addSelectors
} from './utils.js';
function calcSelectorLength(list) {
var length = 0;
list.each(function(data) {
length += data.id.length + 1;
});
return length - 1;
return list.reduce((res, data) => res + data.id.length + 1, 0) - 1;
}
function calcDeclarationsLength(tokens) {
var length = 0;
let length = 0;
for (var i = 0; i < tokens.length; i++) {
length += tokens[i].length;
for (const token of tokens) {
length += token.length;
}
return (
@@ -26,25 +24,25 @@ function calcDeclarationsLength(tokens) {
}
function processRule(node, item, list) {
var avoidRulesMerge = this.block !== null ? this.block.avoidRulesMerge : false;
var selectors = node.prelude.children;
var block = node.block;
var disallowDownMarkers = Object.create(null);
var allowMergeUp = true;
var allowMergeDown = true;
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) {
var prevBlock = prev.block;
var prevType = prev.type;
const prevBlock = prev.block;
const prevType = prev.type;
if (prevType !== 'Rule') {
var unsafe = utils.unsafeToSkipNode.call(selectors, prev);
const unsafe = unsafeToSkipNode.call(selectors, prev);
if (!unsafe && prevType === 'Atrule' && prevBlock) {
walk(prevBlock, {
visit: 'Rule',
enter: function(node) {
node.prelude.children.each(function(data) {
enter(node) {
node.prelude.children.forEach((data) => {
disallowDownMarkers[data.compareMarker] = true;
});
}
@@ -54,15 +52,15 @@ function processRule(node, item, list) {
return unsafe;
}
var prevSelectors = prev.prelude.children;
if (node.pseudoSignature !== prev.pseudoSignature) {
return true;
}
allowMergeDown = !prevSelectors.some(function(selector) {
return selector.compareMarker in disallowDownMarkers;
});
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) {
@@ -70,14 +68,15 @@ function processRule(node, item, list) {
}
// try to join by selectors
if (allowMergeUp && utils.isEqualSelectors(prevSelectors, selectors)) {
if (allowMergeUp && isEqualSelectors(prevSelectors, selectors)) {
prevBlock.children.appendList(block.children);
list.remove(item);
return true;
}
// try to join by properties
var diff = utils.compareDeclarations(block.children, prevBlock.children);
const diff = compareDeclarations(block.children, prevBlock.children);
// console.log(diff.eq, diff.ne1, diff.ne2);
@@ -85,7 +84,7 @@ function processRule(node, item, list) {
if (!diff.ne1.length && !diff.ne2.length) {
// equal blocks
if (allowMergeDown) {
utils.addSelectors(selectors, prevSelectors);
addSelectors(selectors, prevSelectors);
list.remove(prevItem);
}
@@ -95,37 +94,37 @@ function processRule(node, item, list) {
if (diff.ne1.length && !diff.ne2.length) {
// prevBlock is subset block
var selectorLength = calcSelectorLength(selectors);
var blockLength = calcDeclarationsLength(diff.eq); // declarations length
const selectorLength = calcSelectorLength(selectors);
const blockLength = calcDeclarationsLength(diff.eq); // declarations length
if (allowMergeUp && selectorLength < blockLength) {
utils.addSelectors(prevSelectors, selectors);
block.children = new List().fromArray(diff.ne1);
addSelectors(prevSelectors, selectors);
block.children.fromArray(diff.ne1);
}
} else if (!diff.ne1.length && diff.ne2.length) {
// node is subset of prevBlock
var selectorLength = calcSelectorLength(prevSelectors);
var blockLength = calcDeclarationsLength(diff.eq); // declarations length
const selectorLength = calcSelectorLength(prevSelectors);
const blockLength = calcDeclarationsLength(diff.eq); // declarations length
if (allowMergeDown && selectorLength < blockLength) {
utils.addSelectors(selectors, prevSelectors);
prevBlock.children = new List().fromArray(diff.ne2);
addSelectors(selectors, prevSelectors);
prevBlock.children.fromArray(diff.ne2);
}
} else {
// diff.ne1.length && diff.ne2.length
// extract equal block
var newSelector = {
const newSelector = {
type: 'SelectorList',
loc: null,
children: utils.addSelectors(prevSelectors.copy(), selectors)
children: addSelectors(prevSelectors.copy(), selectors)
};
var newBlockLength = calcSelectorLength(newSelector.children) + 2; // selectors length + curly braces length
var blockLength = calcDeclarationsLength(diff.eq); // declarations length
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) {
var newItem = list.createItem({
const newItem = list.createItem({
type: 'Rule',
loc: null,
prelude: newSelector,
@@ -137,8 +136,8 @@ function processRule(node, item, list) {
pseudoSignature: node.pseudoSignature
});
block.children = new List().fromArray(diff.ne1);
prevBlock.children = new List().fromArray(diff.ne2overrided);
block.children.fromArray(diff.ne1);
prevBlock.children.fromArray(diff.ne2overrided);
if (allowMergeUp) {
list.insert(newItem, prevItem);
@@ -155,20 +154,20 @@ function processRule(node, item, list) {
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(function(prevSelector) {
return selectors.some(function(selector) {
return selector.compareMarker === prevSelector.compareMarker;
});
});
allowMergeUp = !prevSelectors.some((prevSelector) =>
selectors.some((selector) =>
selector.compareMarker === prevSelector.compareMarker
)
);
}
prevSelectors.each(function(data) {
prevSelectors.forEach((data) => {
disallowDownMarkers[data.compareMarker] = true;
});
});
}
module.exports = function restructRule(ast) {
export default function restructRule(ast) {
walk(ast, {
visit: 'Rule',
reverse: true,

View File

@@ -1,15 +1,15 @@
var prepare = require('./prepare/index');
var mergeAtrule = require('./1-mergeAtrule');
var initialMergeRuleset = require('./2-initialMergeRuleset');
var disjoinRuleset = require('./3-disjoinRuleset');
var restructShorthand = require('./4-restructShorthand');
var restructBlock = require('./6-restructBlock');
var mergeRuleset = require('./7-mergeRuleset');
var restructRuleset = require('./8-restructRuleset');
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';
module.exports = function(ast, options) {
export default function(ast, options) {
// prepare ast for restructing
var indexer = prepare(ast, options);
const indexer = prepare(ast, options);
options.logger('prepare', ast);
mergeAtrule(ast, options);

View File

@@ -1,26 +1,26 @@
var generate = require('css-tree').generate;
import { generate } from 'css-tree';
function Index() {
this.seed = 0;
this.map = Object.create(null);
}
Index.prototype.resolve = function(str) {
var index = this.map[str];
if (!index) {
index = ++this.seed;
this.map[str] = index;
class Index {
constructor() {
this.map = new Map();
}
resolve(str) {
let index = this.map.get(str);
return index;
if (index === undefined) {
index = this.map.size + 1;
this.map.set(str, index);
}
return index;
}
};
module.exports = function createDeclarationIndexer() {
var ids = new Index();
export default function createDeclarationIndexer() {
const ids = new Index();
return function markDeclaration(node) {
var id = generate(node);
const id = generate(node);
node.id = ids.resolve(id);
node.length = id.length;

View File

@@ -1,23 +1,21 @@
var resolveKeyword = require('css-tree').keyword;
var walk = require('css-tree').walk;
var generate = require('css-tree').generate;
var createDeclarationIndexer = require('./createDeclarationIndexer');
var processSelector = require('./processSelector');
import { walk, generate, keyword as resolveKeyword } from 'css-tree';
import createDeclarationIndexer from './createDeclarationIndexer.js';
import processSelector from './processSelector.js';
module.exports = function prepare(ast, options) {
var markDeclaration = createDeclarationIndexer();
export default function prepare(ast, options) {
const markDeclaration = createDeclarationIndexer();
walk(ast, {
visit: 'Rule',
enter: function processRule(node) {
node.block.children.each(markDeclaration);
enter(node) {
node.block.children.forEach(markDeclaration);
processSelector(node, options.usage);
}
});
walk(ast, {
visit: 'Atrule',
enter: function(node) {
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);
@@ -28,8 +26,8 @@ module.exports = function prepare(ast, options) {
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.each(function(rule) {
rule.prelude.children.each(function(simpleselector) {
node.block.children.forEach(function(rule) {
rule.prelude.children.forEach(function(simpleselector) {
simpleselector.compareMarker = simpleselector.id;
});
});

View File

@@ -1,36 +1,35 @@
var generate = require('css-tree').generate;
var specificity = require('./specificity');
import { generate } from 'css-tree';
import specificity from './specificity.js';
var nonFreezePseudoElements = {
'first-letter': true,
'first-line': true,
'after': true,
'before': true
};
var nonFreezePseudoClasses = {
'link': true,
'visited': true,
'hover': true,
'active': true,
'first-letter': true,
'first-line': true,
'after': true,
'before': true
};
const nonFreezePseudoElements = new Set([
'first-letter',
'first-line',
'after',
'before'
]);
const nonFreezePseudoClasses = new Set([
'link',
'visited',
'hover',
'active',
'first-letter',
'first-line',
'after',
'before'
]);
module.exports = function freeze(node, usageData) {
var pseudos = Object.create(null);
var hasPseudo = false;
export default function processSelector(node, usageData) {
const pseudos = new Set();
node.prelude.children.each(function(simpleSelector) {
var tagName = '*';
var scope = 0;
node.prelude.children.forEach(function(simpleSelector) {
let tagName = '*';
let scope = 0;
simpleSelector.children.each(function(node) {
simpleSelector.children.forEach(function(node) {
switch (node.type) {
case 'ClassSelector':
if (usageData && usageData.scopes) {
var classScope = usageData.scopes[node.name] || 0;
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));
@@ -38,25 +37,28 @@ module.exports = function freeze(node, usageData) {
scope = classScope;
}
break;
case 'PseudoClassSelector':
var name = node.name.toLowerCase();
case 'PseudoClassSelector': {
const name = node.name.toLowerCase();
if (!nonFreezePseudoClasses.hasOwnProperty(name)) {
pseudos[':' + name] = true;
hasPseudo = true;
if (!nonFreezePseudoClasses.has(name)) {
pseudos.add(`:${name}`);
}
break;
}
case 'PseudoElementSelector':
var name = node.name.toLowerCase();
case 'PseudoElementSelector': {
const name = node.name.toLowerCase();
if (!nonFreezePseudoElements.hasOwnProperty(name)) {
pseudos['::' + name] = true;
hasPseudo = true;
if (!nonFreezePseudoElements.has(name)) {
pseudos.add(`::${name}`);
}
break;
}
case 'TypeSelector':
tagName = node.name.toLowerCase();
@@ -64,12 +66,11 @@ module.exports = function freeze(node, usageData) {
case 'AttributeSelector':
if (node.flags) {
pseudos['[' + node.flags.toLowerCase() + ']'] = true;
hasPseudo = true;
pseudos.add(`[${node.flags.toLowerCase()}]`);
}
break;
case 'WhiteSpace':
case 'Combinator':
tagName = '*';
break;
@@ -90,5 +91,7 @@ module.exports = function freeze(node, usageData) {
});
// add property to all rule nodes to avoid multiple hidden class
node.pseudoSignature = hasPseudo && Object.keys(pseudos).sort().join(',');
node.pseudoSignature = pseudos.size > 0
? [...pseudos].sort().join(',')
: false;
};

View File

@@ -1,30 +1,103 @@
module.exports = function specificity(simpleSelector) {
var A = 0;
var B = 0;
var C = 0;
import { parse } from 'css-tree';
simpleSelector.children.each(function walk(node) {
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 selectors specificity
// https://www.w3.org/TR/selectors-4/#specificity-rules
function specificity(simpleSelector) {
let A = 0;
let B = 0;
let C = 0;
// A selectors specificity is calculated for a given element as follows:
simpleSelector.children.forEach((node) => {
switch (node.type) {
case 'SelectorList':
case 'Selector':
node.children.each(walk);
break;
// 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':
node.children.each(walk);
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':
@@ -32,25 +105,27 @@ module.exports = function specificity(simpleSelector) {
C++;
break;
// TODO: support for :nth-*(.. of <SelectorList>), :matches(), :has()
default:
B++;
}
break;
case 'PseudoElementSelector':
C++;
break;
// count the number of type selectors ...
case 'TypeSelector':
// ignore universal selector
if (node.name.charAt(node.name.length - 1) !== '*') {
// 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;

View File

@@ -1,8 +1,8 @@
var hasOwnProperty = Object.prototype.hasOwnProperty;
const { hasOwnProperty } = Object.prototype;
function isEqualSelectors(a, b) {
var cursor1 = a.head;
var cursor2 = b.head;
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;
@@ -12,9 +12,9 @@ function isEqualSelectors(a, b) {
return cursor1 === null && cursor2 === null;
}
function isEqualDeclarations(a, b) {
var cursor1 = a.head;
var cursor2 = b.head;
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;
@@ -24,23 +24,23 @@ function isEqualDeclarations(a, b) {
return cursor1 === null && cursor2 === null;
}
function compareDeclarations(declarations1, declarations2) {
var result = {
export function compareDeclarations(declarations1, declarations2) {
const result = {
eq: [],
ne1: [],
ne2: [],
ne2overrided: []
};
var fingerprints = Object.create(null);
var declarations2hash = Object.create(null);
const fingerprints = Object.create(null);
const declarations2hash = Object.create(null);
for (var cursor = declarations2.head; cursor; cursor = cursor.next) {
for (let cursor = declarations2.head; cursor; cursor = cursor.next) {
declarations2hash[cursor.data.id] = true;
}
for (var cursor = declarations1.head; cursor; cursor = cursor.next) {
var data = cursor.data;
for (let cursor = declarations1.head; cursor; cursor = cursor.next) {
const data = cursor.data;
if (data.fingerprint) {
fingerprints[data.fingerprint] = data.important;
@@ -54,8 +54,8 @@ function compareDeclarations(declarations1, declarations2) {
}
}
for (var cursor = declarations2.head; cursor; cursor = cursor.next) {
var data = cursor.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
@@ -72,13 +72,13 @@ function compareDeclarations(declarations1, declarations2) {
return result;
}
function addSelectors(dest, source) {
source.each(function(sourceData) {
var newStr = sourceData.id;
var cursor = dest.head;
export function addSelectors(dest, source) {
source.forEach((sourceData) => {
const newStr = sourceData.id;
let cursor = dest.head;
while (cursor) {
var nextStr = cursor.data.id;
const nextStr = cursor.data.id;
if (nextStr === newStr) {
return;
@@ -98,11 +98,11 @@ function addSelectors(dest, source) {
}
// check if simpleselectors has no equal specificity and element selector
function hasSimilarSelectors(selectors1, selectors2) {
var cursor1 = selectors1.head;
export function hasSimilarSelectors(selectors1, selectors2) {
let cursor1 = selectors1.head;
while (cursor1 !== null) {
var cursor2 = selectors2.head;
let cursor2 = selectors2.head;
while (cursor2 !== null) {
if (cursor1.data.compareMarker === cursor2.data.compareMarker) {
@@ -119,7 +119,7 @@ function hasSimilarSelectors(selectors1, selectors2) {
}
// test node can't to be skipped
function unsafeToSkipNode(node) {
export function unsafeToSkipNode(node) {
switch (node.type) {
case 'Rule':
// unsafe skip ruleset with selector similarities
@@ -140,12 +140,3 @@ function unsafeToSkipNode(node) {
// unsafe by default
return true;
}
module.exports = {
isEqualSelectors: isEqualSelectors,
isEqualDeclarations: isEqualDeclarations,
compareDeclarations: compareDeclarations,
addSelectors: addSelectors,
hasSimilarSelectors: hasSimilarSelectors,
unsafeToSkipNode: unsafeToSkipNode
};

60
node_modules/csso/lib/syntax.js generated vendored Normal file
View 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
};

40
node_modules/csso/lib/usage.js generated vendored
View File

@@ -1,15 +1,13 @@
var hasOwnProperty = Object.prototype.hasOwnProperty;
const { hasOwnProperty } = Object.prototype;
function buildMap(list, caseInsensitive) {
var map = Object.create(null);
const map = Object.create(null);
if (!Array.isArray(list)) {
return null;
}
for (var i = 0; i < list.length; i++) {
var name = list[i];
for (let name of list) {
if (caseInsensitive) {
name = name.toLowerCase();
}
@@ -25,9 +23,9 @@ function buildList(data) {
return null;
}
var tags = buildMap(data.tags, true);
var ids = buildMap(data.ids);
var classes = buildMap(data.classes);
const tags = buildMap(data.tags, true);
const ids = buildMap(data.ids);
const classes = buildMap(data.classes);
if (tags === null &&
ids === null &&
@@ -36,30 +34,28 @@ function buildList(data) {
}
return {
tags: tags,
ids: ids,
classes: classes
tags,
ids,
classes
};
}
function buildIndex(data) {
var scopes = false;
export function buildIndex(data) {
let scopes = false;
if (data.scopes && Array.isArray(data.scopes)) {
scopes = Object.create(null);
for (var i = 0; i < data.scopes.length; i++) {
var list = data.scopes[i];
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 (var j = 0; j < list.length; j++) {
var name = list[j];
for (const name of list) {
if (hasOwnProperty.call(scopes, name)) {
throw new Error('Class can\'t be used for several scopes: ' + name);
throw new Error(`Class can't be used for several scopes: ${name}`);
}
scopes[name] = i + 1;
@@ -70,10 +66,6 @@ function buildIndex(data) {
return {
whitelist: buildList(data),
blacklist: buildList(data.blacklist),
scopes: scopes
scopes
};
}
module.exports = {
buildIndex: buildIndex
};

2
node_modules/csso/lib/utils.js generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export { default as processSelector } from './restructure/prepare/processSelector.js';
export * from './restructure/utils.js';

5
node_modules/csso/lib/version.js generated vendored Normal file
View File

@@ -0,0 +1,5 @@
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
export const { version } = require('../package.json');

19
node_modules/csso/node_modules/css-tree/LICENSE generated vendored Executable file
View 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
node_modules/csso/node_modules/css-tree/README.md generated vendored Executable file
View 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
[![NPM version](https://img.shields.io/npm/v/css-tree.svg)](https://www.npmjs.com/package/css-tree)
[![Build Status](https://github.com/csstree/csstree/actions/workflows/build.yml/badge.svg)](https://github.com/csstree/csstree/actions/workflows/build.yml)
[![Coverage Status](https://coveralls.io/repos/github/csstree/csstree/badge.svg?branch=master)](https://coveralls.io/github/csstree/csstree?branch=master)
[![NPM Downloads](https://img.shields.io/npm/dm/css-tree.svg)](https://www.npmjs.com/package/css-tree)
[![Twitter](https://img.shields.io/badge/Twitter-@csstree-blue.svg)](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
![API map](https://cdn.rawgit.com/csstree/csstree/aaf327e/docs/api-map.svg)
## License
MIT

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

View 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
node_modules/csso/node_modules/css-tree/cjs/data-patch.cjs generated vendored Executable file
View File

@@ -0,0 +1,7 @@
'use strict';
const patch = require('../data/patch.json');
const patch$1 = patch;
module.exports = patch$1;

97
node_modules/csso/node_modules/css-tree/cjs/data.cjs generated vendored Executable file
View 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;

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

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