Comparing version 3.5.4 to 4.0.0
193
package.json
{ | ||
"name": "stylis", | ||
"main": "stylis.js", | ||
"unpkg": "stylis.min.js", | ||
"jsdelivr": "stylis.min.js", | ||
"types": "stylis.d.ts", | ||
"description": "light - weight css preprocessor", | ||
"version": "3.5.4", | ||
"homepage": "https://github.com/thysultan/stylis.js", | ||
"license": "MIT", | ||
"author": { | ||
"name": "Sultan Tarimo", | ||
"email": "sultantarimo@me.com" | ||
}, | ||
"keywords": [ | ||
"css", | ||
"preprocessor" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/thysultan/stylis.js" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/thysultan/stylis.js/issues" | ||
}, | ||
"scripts": { | ||
"test": "node ./tests", | ||
"minify": "uglifyjs stylis.js -o stylis.min.js --compress 'conditionals=false,if_return=false,booleans=false' --mangle --screw-ie8 --source-map url=stylis.min.js.map", | ||
"prepublish": "npm run minify" | ||
}, | ||
"files": [ | ||
"stylis.js", | ||
"stylis.min.js", | ||
"stylis.min.js.map", | ||
"stylis.d.ts" | ||
], | ||
"devDependencies": { | ||
"uglify-js": "^3.2.2" | ||
} | ||
"name": "stylis", | ||
"version": "4.0.0", | ||
"license": "MIT", | ||
"description": "A Light–weight CSS Preprocessor", | ||
"homepage": "https://github.com/thysultan/stylis.js", | ||
"author": "Sultan Tarimo <sultantarimo@me.com>", | ||
"repository": "https://github.com/thysultan/stylis.js", | ||
"bugs": "https://github.com/thysultan/stylis.js/issues", | ||
"type": "module", | ||
"main": "dist/stylis.umd.js", | ||
"module": "dist/stylis.esm.js", | ||
"exports": { | ||
"import": "./index.js", | ||
"require": "./dist/stylis.cjs" | ||
}, | ||
"files": [ | ||
"index.js", | ||
"dist/", | ||
"src/" | ||
], | ||
"scripts": { | ||
"lint": "eslint ./", | ||
"pretest": "npm run lint && npm run build", | ||
"test": "nyc npm run spec", | ||
"spec": "mocha --harmony --require esm script/setup.js --recursive test", | ||
"build": "rollup --config script/build.js --configSrc ./", | ||
"start": "rollup --config script/build.js --configSrc ./ --watch", | ||
"prepare": "npm run build", | ||
"postversion": "git push --follow-tags && npm publish", | ||
"release-major": "npm version major -m '%s'", | ||
"release-minor": "npm version minor -m '%s'", | ||
"release-patch": "npm version patch -m '%s'" | ||
}, | ||
"devDependencies": { | ||
"stylis": "./", | ||
"chai": "4.2.0", | ||
"eslint": "6.8.0", | ||
"esm": "3.2.25", | ||
"mocha": "7.0.0", | ||
"nyc": "15.0.0", | ||
"rollup": "1.28.0", | ||
"rollup-plugin-terser": "5.1.3", | ||
"rollup-plugin-size": "0.2.1" | ||
}, | ||
"nyc": { | ||
"temp-dir": "./coverage/.nyc_output", | ||
"exclude": [ | ||
"**/dist/", | ||
"**/test/", | ||
"**/script/" | ||
], | ||
"reporter": [ | ||
"lcov", | ||
"text" | ||
] | ||
}, | ||
"esm": { | ||
"cjs": true, | ||
"cache": false | ||
}, | ||
"eslintIgnore": [ | ||
"script/", | ||
"test/", | ||
"dist/", | ||
"docs/" | ||
], | ||
"eslintConfig": { | ||
"env": { | ||
"commonjs": true, | ||
"browser": true, | ||
"node": true, | ||
"es6": true | ||
}, | ||
"extends": "eslint:recommended", | ||
"parserOptions": { | ||
"ecmaVersion": 7, | ||
"sourceType": "module", | ||
"ecmaFeatures": { | ||
"impliedStrict": true | ||
} | ||
}, | ||
"rules": { | ||
"indent": [ | ||
"error", | ||
"tab", | ||
{ | ||
"SwitchCase": 1 | ||
} | ||
], | ||
"linebreak-style": [ | ||
"error", | ||
"unix" | ||
], | ||
"quotes": [ | ||
"error", | ||
"single" | ||
], | ||
"semi": [ | ||
"error", | ||
"never" | ||
], | ||
"no-cond-assign": [ | ||
"off" | ||
], | ||
"no-redeclare": [ | ||
"off" | ||
], | ||
"no-fallthrough": [ | ||
"off" | ||
], | ||
"no-console": [ | ||
"off" | ||
], | ||
"no-unsafe-finally": [ | ||
"off" | ||
], | ||
"no-shadow-restricted-names": [ | ||
"error" | ||
], | ||
"no-whitespace-before-property": [ | ||
"error" | ||
], | ||
"eol-last": [ | ||
"error" | ||
], | ||
"func-call-spacing": [ | ||
"error", | ||
"never" | ||
], | ||
"brace-style": [ | ||
"error", | ||
"1tbs", | ||
{ | ||
"allowSingleLine": true | ||
} | ||
], | ||
"require-jsdoc": [ | ||
"error", | ||
{ | ||
"require": { | ||
"FunctionDeclaration": true | ||
} | ||
} | ||
], | ||
"no-trailing-spaces": [ | ||
"error", | ||
{ | ||
"skipBlankLines": true | ||
} | ||
], | ||
"no-constant-condition": [ | ||
"off" | ||
] | ||
} | ||
} | ||
} |
382
README.md
@@ -5,369 +5,135 @@ # STYLIS | ||
light – weight css preprocessor | ||
A Light–weight CSS Preprocessor. | ||
- 3Kb | ||
[![Coverage](https://coveralls.io/repos/github/thysultan/stylis.js/badge.svg?branch=master)](https://coveralls.io/github/thysultan/stylis.js) | ||
[![Size](https://badgen.net/bundlephobia/minzip/stylis)](https://bundlephobia.com/result?p=stylis) | ||
[![Licence](https://badgen.net/badge/license/MIT/blue)](https://github.com/dyo/dyo/blob/master/LICENSE.md) | ||
[![NPM](https://badgen.net/npm/v/dyo)](https://www.npmjs.com/package/dyo) | ||
[![npm](https://img.shields.io/npm/v/stylis.svg?style=flat)](https://www.npmjs.com/package/stylis) [![licence](https://img.shields.io/badge/licence-MIT-blue.svg?style=flat)](https://github.com/thysultan/stylis.js/blob/master/LICENSE.md) [![Build Status](https://semaphoreci.com/api/v1/thysultan/stylis-js/branches/master/shields_badge.svg)](https://semaphoreci.com/thysultan/stylis-js) ![dependencies](https://img.shields.io/badge/dependencies-none-green.svg?style=flat) | ||
## Supports | ||
* Edge | ||
* IE 8+ | ||
* Chrome | ||
* Firefox | ||
* Safari | ||
* Node | ||
--- | ||
## Installation | ||
#### direct download | ||
* Use a Direct Download: `<script src=stylis.js></script>` | ||
* Use a CDN: `<script src=unpkg.com/stylis></script>` | ||
* Use NPM: `npm install stylis --save` | ||
```html | ||
<script src=stylis.min.js></script> | ||
``` | ||
#### CDN | ||
```html | ||
<script src=https://unpkg.com/stylis@latest/stylis.min.js></script> | ||
``` | ||
#### npm | ||
``` | ||
npm install stylis --save | ||
``` | ||
## Features | ||
- selector namespacing/isolation | ||
- inline global injection ex. `:global(selector)` | ||
- nesting `a { &:hover {} }` | ||
- selector namespacing | ||
- vendor prefixing (flex-box, etc...) | ||
- flat stylesheets `color: red; h1 { color: red; }` | ||
- keyframe and animation namespacing | ||
- plugins | ||
- minification | ||
- built to support [closure-compiler advanced mode](https://developers.google.com/closure/compiler) | ||
- esm module compatible | ||
- tree-shaking-able | ||
--- | ||
## Abstract Syntax Structure | ||
## Input | ||
```javascript | ||
stylis('#id', ` | ||
font-size: 2em; | ||
// line comments | ||
/* block comments */ | ||
:global(body) {background:red} | ||
h1 { | ||
h2 { | ||
h3 { | ||
content:'nesting' | ||
} | ||
} | ||
```js | ||
const declaration = { | ||
value: 'color:red;', | ||
type: 'decl', | ||
props: 'color', | ||
children: 'red', | ||
line: 1, column: 1 | ||
} | ||
@media (max-width: 600px) { | ||
& {display:none} | ||
const comment = { | ||
value: '/*@noflip*/', | ||
type: 'comm', | ||
props: '/', | ||
children: '@noflip', | ||
line: 1, column: 1 | ||
} | ||
&:before { | ||
animation: slide 3s ease infinite | ||
const ruleset = { | ||
value: 'h1,h2', | ||
type: 'rule', | ||
props: ['h1', 'h2'], | ||
children: [/* ... */], | ||
line: 1, column: 1 | ||
} | ||
@keyframes slide { | ||
from { opacity: 0} | ||
to { opacity: 1} | ||
const atruleset = { | ||
value: '@media (max-width:100), (min-width:100)', | ||
type: '@media', | ||
props: ['(max-width:100)', '(min-width:100)'], | ||
children: [/* ... */], | ||
line: 1, column: 1 | ||
} | ||
& { | ||
display: flex | ||
} | ||
&::placeholder { | ||
color:red | ||
} | ||
`); | ||
``` | ||
## Output | ||
## Example: | ||
```css | ||
#id {font-size: 2em;} | ||
body {background:red} | ||
h1 h2 h3 {content: 'nesting'} | ||
@media (max-width: 600px) { | ||
#id {display:none} | ||
} | ||
#id:before { | ||
-webkit-animation: slide-id 3s ease infinite; | ||
animation: slide-id 3s ease infinite; | ||
} | ||
@-webkit-keyframes slide-id { | ||
from { opacity: 0} | ||
to { opacity: 1} | ||
} | ||
@keyframes slide-id { | ||
from { opacity: 0} | ||
to { opacity: 1} | ||
} | ||
#id { | ||
display:-webkit-box; | ||
display:-webkit-flex; | ||
display:-ms-flexbox; | ||
display:flex; | ||
} | ||
#id::-webkit-input-placeholder {color:red;} | ||
#id::-moz-placeholder {color:red;} | ||
#id:-ms-input-placeholder {color:red;} | ||
#id::placeholder {color:red;} | ||
``` | ||
## API | ||
#### Stylis | ||
```javascript | ||
stylis(selector: {String}, css: {String}) | ||
``` | ||
#### Factory | ||
```js | ||
// factory pattern | ||
var stylis = new stylis(options) | ||
import {compile, serialize, stringify} from 'stylis' | ||
// singleton pattern | ||
var stylis = stylis | ||
serialize(compile(`h1{all:unset}`), stringify) | ||
``` | ||
When using the factory pattern the if an object is passed as optional `options` argument, this will be passed to `stylis.set(options)` | ||
### Compile | ||
#### Set | ||
```javascript | ||
// all options except compress and cascade are enabled by default | ||
stylis.set({ | ||
// (dis/en)able :global selectors | ||
global: {Boolean} | ||
// (dis/en)able aggressive cascade isolation | ||
// true for normal cascade(default) false for no cascading | ||
cascade: {Boolean} | ||
// (dis/en)able namespace keyframes + animations | ||
keyframe: {Boolean} | ||
// (dis/en)able vendor prefixing | ||
prefix: {Boolean|Function(key: string, value: string, context: number)} | ||
// (dis/en)able aggressive minification | ||
compress: {Boolean} | ||
// (dis/en)able (no)semicolon support | ||
// false to enable no-semicolons (default) | ||
semicolon: {Boolean}, | ||
// tell stylis to make an effort to preserve empty rules, | ||
// i.e `.selector{ }` | ||
preserve: {Boolean} | ||
}) | ||
``` | ||
#### Vendor Prefixing | ||
By default vendor is enabled, however there is an option to disable vendor prefixing, either completely or dynamically. | ||
The following would disable prefixing. | ||
```js | ||
stylis.set({prefix: false}) | ||
compile('h1{all:unset}') === [{value: 'h1', type: 'rule', props: ['h1'], children: [/* ... */]}] | ||
compile('--foo:unset;') === [{value: '--foo:unset;', type: 'decl', props: '--foo', children: 'unset'}] | ||
``` | ||
Alternatively you can disable prefixing on a case by case basis by providing a function that returns a `boolean` indiciating whether to prefixing that particular rule. | ||
### Tokenize | ||
```js | ||
stylis.set({ | ||
prefix: (key, value, context) => { | ||
return false | ||
} | ||
}) | ||
tokenize('h1 h2 h3 [h4 h5] fn(args) "a b c"') === ['h1', 'h2', 'h3', '[h4 h5]', 'fn', '(args)', '"a b c"'] | ||
``` | ||
The arguments correspond to the rule that is about to be vendor prefixed. For example: | ||
### Serialize | ||
```js | ||
// transform: none; | ||
key = 'transform' | ||
value = 'none' | ||
context = 1 | ||
// :read-only {...} | ||
key = ':read-only' | ||
value = '...' | ||
context = 2 | ||
// @keyframes {...} | ||
key = '@keyframes' | ||
value = '...' | ||
context = 3 | ||
serialize(compile('h1{all:unset}'), stringify) | ||
``` | ||
#### Use | ||
## Middleware | ||
```javascript | ||
stylis.use(plugin: {Function|Array<Function>|null}) | ||
``` | ||
The middleware helper is a convinent helper utility, that for all intensive purposes you can do without if you intend to implement your own traversal logic. The `stringify` middleware is one such middleware that can be used in conjunction with it. | ||
The use function is chainable ex. `stylis.use()()()` | ||
Elements passed to middlewares have a `root` property that is the immediate root/parent of the current element **in the compiled output**, so it references the parent in the already expanded CSS-like structure. Elements have also `parent` property that is the immediate parent of the current element **from the input structure** (structure representing the input string). | ||
## Plugins | ||
### Traversal | ||
The optional middleware function accepts four arguments with `this` pointing to a reference of the current stylis instance. | ||
```js | ||
(context, content, selectors, parent, line, column, length) | ||
serialize(compile('h1{all:unset}'), middleware([(element, index, children) => { | ||
assert(children === element.root.children && children[index] === element.children) | ||
}, stringify])) === 'h1{all:unset;}' | ||
``` | ||
Plugins are executed in stages identified by an `context` interger value. | ||
The abstract syntax tree also includes an additional `return` property for more niche uses. | ||
``` | ||
-2 /* post-process context */ | ||
-1 /* preparation context */ | ||
0 /* newline context */ | ||
### Prefixing | ||
1 /* property context */ | ||
2 /* selector block context */ | ||
3 /* @at-rule block context */ | ||
```js | ||
serialize(compile('h1{all:unset}'), middleware([(element, index, children, callback) => { | ||
if (element.type === 'decl' && element.props === 'all' && element.children === 'unset') | ||
element.return = 'color:red;' + element.value | ||
}, stringify])) === 'h1{color:red;all:unset;}' | ||
``` | ||
> Note: Since the newline context is intended for source-map plugins by default stylis will not execute plugins in this context unless enabled, this can be done through `stylis.use(true)` or disabled after that through `stylis.use(false)`. | ||
- `-2` post processed context, before the compiled css output is returned | ||
- `-1` preparation context, before the compiler starts | ||
- `0` after every newline | ||
- `1` on a property declaration ex. `color: red;` | ||
- `2` after a selector block of css has been processed ex. `.foo {color:red;}` | ||
- `3` after a `@at-rule` block of css has been processed ex. `@media {h1{color:red;}}` | ||
If at any context(except 0) that the middleware returns a different string the content of css will be replaced with the return value. | ||
To remove all plugins just call `.use` with null/no arguments. | ||
```js | ||
// removes all previously added plugins, then adds one | ||
stylis.use(null)(ctx => {}) | ||
serialize(compile('h1{all:unset}'), middleware([(element, index, children, callback) => { | ||
if (element.type === 'rule' && element.props.indexOf('h1') > -1) | ||
return serialize([{...element, props: ['h2', 'h3']}], callback) | ||
}, stringify])) === 'h2,h3{all:unset;}h1{all:unset;}' | ||
``` | ||
For example we can add a feature `random()` to our css that when used prints a random number. | ||
### Reading | ||
```javascript | ||
/** | ||
* plugin | ||
* | ||
* @param {number} context | ||
* @param {Array<string>} selector | ||
* @param {Array<string>} parent | ||
* @param {string} content | ||
* @param {number} line | ||
* @param {number} column | ||
* @param {number} length | ||
* @return {(string|void)?} | ||
*/ | ||
const plugin = (context, content, selectors, parent, line, column, length) => { | ||
switch (context) { | ||
case 1: return content.replace(/random\(\)/g, Math.random()) | ||
} | ||
} | ||
/** | ||
* use | ||
* | ||
* @param {(Array<function>|function|null|boolean)} plugin | ||
* @return {Function} use | ||
*/ | ||
stylis.use(plugin) | ||
stylis(``, `h1 { width: calc(random()*10); }`) | ||
```js | ||
serialize(compile('h1{all:unset}'), middleware([stringify, (element, index, children) => { | ||
assert(element.return === 'h1{all:unset;}') | ||
}])) === 'h1{all:unset;color:red;}' | ||
``` | ||
This will replace all instances of `random()` with a random number. | ||
The middlewares in [src/Middleware.js](src/Middleware.js) dive into tangible examples of how you might implement a middleware, alternatively you could also create your own middleware system as `compile` returns all the nessessary structure to fork from. | ||
Internally Before stylis processes `calc(random()*10);` it passes it to the plugin if a plugin exists; If in turn the plugin returns something different from what it received stylis will replace the content of the property with the return value and continue processing that. | ||
The same can be said for a selector block, in both contexts an argument `selector` is passed that contains the current array of selectors that the block of css/property stylis is working on. | ||
This array of selectors is mutable and will reflect the output of selectors if changed. | ||
## Benchmark | ||
Stylis is fast, and though it does not generate an AST you can with a plugin create an AST out of the resulting input, this and other aspects allow it to be very small(3KB). | ||
Stylis is at-least 2X faster than its predecesor. | ||
The benchmark results are using [https://github.com/postcss/benchmark](https://github.com/postcss/benchmark) | ||
### License | ||
> Note that the benchmark is not a one-2-one comparison because each library was developed for different goals and different set of features. | ||
Stylis appears in all the benchmarks because by default stylis both parsers, processes and auto prefixes in one pass. | ||
#### Parsers | ||
``` | ||
Stylis x 54.28 ops/sec ±4.45% (58 runs sampled) | ||
CSSTree x 39.73 ops/sec ±9.18% (56 runs sampled) | ||
PostCSS x 21.11 ops/sec ±8.46% (57 runs sampled) | ||
CSSOM x 19.20 ops/sec ±6.53% (36 runs sampled) | ||
Mensch x 17.85 ops/sec ±13.39% (37 runs sampled) | ||
Rework x 12.80 ops/sec ±4.42% (36 runs sampled) | ||
PostCSS Full x 8.15 ops/sec ±7.79% (45 runs sampled) | ||
Gonzales x 5.21 ops/sec ±7.75% (18 runs sampled) | ||
Gonzales PE x 3.99 ops/sec ±10.37% (15 runs sampled) | ||
Stylecow x 3.97 ops/sec ±9.48% (15 runs sampled) | ||
ParserLib x 1.79 ops/sec ±8.58% (9 runs sampled) | ||
Fastest test is Stylis at 1.37x faster than CSSTree | ||
``` | ||
--- | ||
#### Preprocessors | ||
``` | ||
Stylis x 26.26 ops/sec ±5.95% (49 runs sampled) | ||
PostCSS x 16.23 ops/sec ±11.21% (47 runs sampled) | ||
Rework x 10.65 ops/sec ±3.86% (55 runs sampled) | ||
libsass x 6.83 ops/sec ±2.29% (22 runs sampled) | ||
Less x 4.75 ops/sec ±9.14% (29 runs sampled) | ||
Stylus x 3.67 ops/sec ±28.12% (25 runs sampled) | ||
Stylecow x 2.15 ops/sec ±6.36% (15 runs sampled) | ||
Ruby Sass x 0.31 ops/sec ±8.12% (6 runs sampled) | ||
Fastest test is Stylis at 1.62x faster than PostCSS | ||
``` | ||
--- | ||
#### Prefixers | ||
``` | ||
Stylis x 45.52 ops/sec ±14.61% (77 runs sampled) | ||
Autoprefixer x 13.32 ops/sec ±6.51% (67 runs sampled) | ||
Stylecow x 2.28 ops/sec ±5.97% (16 runs sampled) | ||
nib x 1.79 ops/sec ±25.32% (15 runs sampled) | ||
Compass x 0.15 ops/sec ±9.34% (5 runs sampled) | ||
Fastest test is Stylis at 3.4x faster than Autoprefixer | ||
``` | ||
Stylis is [MIT licensed](./LICENSE). |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
162075
18
Yes
9
811
1
139
1
2