css-parser
CSS parser and minifier for node and the browser
Installation
$ npm install @tbela99/css-parser
Features
- fault-tolerant parser, will try to fix invalid tokens according to the CSS syntax module 3 recommendations.
- efficient minification, see benchmark, see benchmark
- automatically generate nested css rules
- generate sourcemap
- compute css shorthands. see the list below
- compute calc() expression
- nested css expansion
- remove duplicate properties
- flatten @import rules
- works the same way in node and web browser
Transform
Parse and render css in a single pass.
Usage
transform(css, transformOptions: TransformOptions = {}): TransformResult
Example
import {transform} from '@tbela99/css-parser';
const {ast, code, map, errors, stats} = await transform(css, {minify: true, resolveImport: true, cwd: 'files/css'});
TransformOptions
Include ParseOptions and RenderOptions
ParseOptions
- minify: boolean, optional. default to true. optimize ast.
- src: string, optional. original css file location to be used with sourcemap.
- sourcemap: boolean, optional. preserve node location data.
- nestingRules: boolean, optional. automatically generated nested rules.
- expandNestingRules: boolean, optional. convert nesting rules into separate rules. will automatically set nestingRules to false.
- removeCharset: boolean, optional. remove @charset.
- removeEmpty: boolean, optional. remove empty rule lists from the ast.
- resolveUrls: boolean, optional. resolve css 'url()' according to the parameters 'src' and 'cwd'
- resolveImport: boolean, optional. replace @import rule by the content of its referenced stylesheet.
- cwd: string, optional. the current working directory. when specified url() are resolved using this value
- removeDuplicateDeclarations: boolean, optional. remove duplicate declarations.
- computeShorthand: boolean, optional. compute shorthand properties.
- inlineCssVariables: boolean, optional. replace css variables with their current value.
- computeCalcExpression: boolean, optional. evaluate calc() expression
- inlineCssVariables: boolean, optional. replace some css variables with their actual value. they must be declared once in the :root {} rule.
RenderOptions
- minify: boolean, optional. default to true. minify css output.
- expandNestingRules: boolean, optional. expand nesting rules.
- sourcemap: boolean, optional. generate sourcemap
- preserveLicense: boolean, force preserving comments starting with '/*!' when minify is enabled.
- sourcemap: boolean, optional. generate sourcemap.
- indent: string, optional. css indention string. uses space character by default.
- newLine: string, optional. new line character.
- removeComments: boolean, remove comments in generated css.
- colorConvert: boolean, convert colors to hex.
- output: string, optional. file where to store css. url() are resolved according to the specified value. no file is created though.
- cwd: string, optional. value used as current working directory. when output is not provided, urls are resolved according to this value.
Parsing
Usage
parse(css, parseOptions = {})
Example
const {ast, errors, stats} = await parse(css);
Rendering
Usage
doRender(ast, RenderOptions = {});
Example
import {render} from '@tbela99/css-parser';
// minified
const {code, stats} = render(ast, {minify: true});
console.log(code);
Node Walker
import {walk} from '@tbela99/css-parser';
for (const {node, parent, root} of walk(ast)) {
// do somehting
}
Exports
There are several ways to import the library into your application.
Node exports
import as a module
import {transform} from '@tbela99/css-parser';
// ...
import as a CommonJS module
const {transform} = require('@tbela99/css-parser/cjs');
// ...
Web export
Programmatic import
import {transform} from '@tbela99/css-parser/web';
// ...
Javascript module
<script src="dist/web/index.js" type="module"></script>
Single JavaScript file
<script src="dist/index-umd-web.js"></script>
Example 1
Automatic CSS Nesting
CSS
const {parse, render} = require("@tbela99/css-parser/cjs");
const css = `
table.colortable td {
text-align:center;
}
table.colortable td.c {
text-transform:uppercase;
}
table.colortable td:first-child, table.colortable td:first-child+td {
border:1px solid black;
}
table.colortable th {
text-align:center;
background:black;
color:white;
}
`;
const result = await parse(css, {nestingRules:true}).then(result => render(result.ast, {minify:false}).code);
Result
table.colortable {
& td {
text-align: center;
&.c {
text-transform: uppercase
}
&:first-child,&:first-child+td {
border: 1px solid #000
}
}
& th {
text-align: center;
background: #000;
color: #fff
}
}
Example 2
Nested CSS Expansion
CSS
table.colortable {
& td {
text-align: center;
&.c {
text-transform: uppercase
}
&:first-child,&:first-child+td {
border: 1px solid #000
}
}
& th {
text-align: center;
background: #000;
color: #fff
}
}
Javascript
import {parse, render} from '@tbela99/css-parser';
const options = {minify: true};
const {code} = await parse(css, options).then(result => render(result.ast, {minify: false, expandNestingRules: true}));
//
console.debug(code);
Result
table.colortable td {
text-align:center;
}
table.colortable td.c {
text-transform:uppercase;
}
table.colortable td:first-child, table.colortable td:first-child+td {
border:1px solid black;
}
table.colortable th {
text-align:center;
background:black;
color:white;
}
Example 3
Calc() resolution
import {parse, render} from '@tbela99/css-parser';
const css = `
.foo-bar {
width: calc(100px * 2);
height: calc(((75.37% - 63.5px) - 900px) + (2 * 100px));
max-width: calc(3.5rem + calc(var(--bs-border-width) * 2));
}
`;
const prettyPrint = await parse(css).then(result => render(result.ast, {minify: false}).code);
result
.foo-bar {
width: 200px;
height: calc(75.37% - 763.5px);
max-width: calc(3.5rem + var(--bs-border-width)*2)
}
Example 4
CSS variable inlining
import {parse, render} from '@tbela99/css-parser';
const css = `
:root {
--preferred-width: 20px;
}
.foo-bar {
width: calc(calc(var(--preferred-width) + 1px) / 3 + 5px);
height: calc(100% / 4);}
`
const prettyPrint = await parse(css, {inlineCssVariables: true}).then(result => render(result.ast, {minify: false}).code);
result
.foo-bar {
width: 12px;
height: 25%
}
AST
Comment
- typ: string 'Comment'
- val: string, the comment
Declaration
- typ: string 'Declaration'
- nam: string, declaration name
- val: array of tokens
Rule
- typ: string 'Rule'
- sel: string, css selector
- chi: array of children
AtRule
- typ: string 'AtRule'
- nam: string. AtRule name
- val: rule prelude
AtRuleStyleSheet
- typ: string 'Stylesheet'
- chi: array of children
Sourcemap
- [x] sourcemap generation
Minification
- [x] reduce calc()
- [x] inline css variables
- [x] merge identical rules
- [x] merge adjacent rules
- [x] minify colors
- [x] minify numbers and Dimensions tokens
- [x] compute shorthand: see the list below
- [x] remove redundant declarations
- [x] conditionally unwrap :is()
- [x] automatic css nesting
- [x] automatically wrap selectors using :is()
- [x] avoid reparsing (declarations, selectors, at-rule)
- [x] node and browser versions
- [x] decode and replace utf-8 escape sequence
Computed shorthands properties
- [x] background
- [x] border
- [x] border-bottom
- [x] border-color
- [x] border-left
- [x] border-radius
- [x] border-right
- [x] border-style
- [x] border-top
- [x] border-width
- [x] font
- [x] inset
- [x] margin
- [x] outline
- [x] overflow
- [x] padding
- [x] text-decoration
Performance
- [x] flatten @import
Thanks to Jetbrains for sponsoring this project with a free license